useFormulaDependencies.tsx 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. import {useCallback, useMemo} from 'react';
  2. import {parseFormula} from 'sentry/components/metrics/equationInput/syntax/parser';
  3. import {
  4. type TokenList,
  5. TokenType,
  6. } from 'sentry/components/metrics/equationInput/syntax/types';
  7. import {getQuerySymbol} from 'sentry/components/metrics/querySymbol';
  8. import {unescapeMetricsFormula} from 'sentry/utils/metrics';
  9. import {
  10. isMetricsEquationWidget,
  11. isMetricsQueryWidget,
  12. type MetricsQueryWidget,
  13. } from 'sentry/utils/metrics/types';
  14. import {useMetricsContext} from 'sentry/views/metrics/context';
  15. interface FormulaDependencies {
  16. dependencies: MetricsQueryWidget[];
  17. isError: boolean;
  18. }
  19. export function useFormulaDependencies() {
  20. const {widgets} = useMetricsContext();
  21. const queriesLookup = useMemo(() => {
  22. const lookup = new Map<string, MetricsQueryWidget>();
  23. widgets.forEach(widget => {
  24. if (isMetricsQueryWidget(widget)) {
  25. lookup.set(getQuerySymbol(widget.id), widget);
  26. }
  27. });
  28. return lookup;
  29. }, [widgets]);
  30. const getFormulaQueryDependencies = useCallback(
  31. (formula: string): FormulaDependencies => {
  32. let tokens: TokenList = [];
  33. try {
  34. tokens = parseFormula(unescapeMetricsFormula(formula));
  35. } catch {
  36. // We should not end up here, but if we do, we should not crash the UI
  37. return {dependencies: [], isError: true};
  38. }
  39. const dependencies: MetricsQueryWidget[] = [];
  40. let isError = false;
  41. tokens.forEach(token => {
  42. if (token.type === TokenType.VARIABLE) {
  43. const widget = queriesLookup.get(token.content);
  44. if (widget) {
  45. dependencies.push(widget);
  46. } else {
  47. isError = true;
  48. }
  49. }
  50. });
  51. return {dependencies, isError};
  52. },
  53. [queriesLookup]
  54. );
  55. const formulaDependencies = useMemo(() => {
  56. return widgets.reduce((acc: Record<number, FormulaDependencies>, widget) => {
  57. if (isMetricsEquationWidget(widget)) {
  58. acc[widget.id] = getFormulaQueryDependencies(widget.formula);
  59. }
  60. return acc;
  61. }, {});
  62. }, [getFormulaQueryDependencies, widgets]);
  63. return formulaDependencies;
  64. }