useFormulaDependencies.tsx 2.1 KB

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