useFormulaDependencies.tsx 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  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 {hasMetricsNewInputs} from 'sentry/utils/metrics/features';
  10. import {
  11. isMetricsEquationWidget,
  12. isMetricsQueryWidget,
  13. type MetricsQueryWidget,
  14. } from 'sentry/utils/metrics/types';
  15. import useOrganization from 'sentry/utils/useOrganization';
  16. import {useMetricsContext} from 'sentry/views/metrics/context';
  17. interface FormulaDependencies {
  18. dependencies: MetricsQueryWidget[];
  19. isError: boolean;
  20. }
  21. export function useFormulaDependencies() {
  22. const organization = useOrganization();
  23. const {widgets} = useMetricsContext();
  24. const metricsNewInputs = hasMetricsNewInputs(organization);
  25. const queriesLookup = useMemo(() => {
  26. const lookup = new Map<string, MetricsQueryWidget>();
  27. widgets.forEach(widget => {
  28. if (isMetricsQueryWidget(widget)) {
  29. lookup.set(getQuerySymbol(widget.id, metricsNewInputs), widget);
  30. }
  31. });
  32. return lookup;
  33. }, [widgets, metricsNewInputs]);
  34. const getFormulaQueryDependencies = useCallback(
  35. (formula: string): FormulaDependencies => {
  36. let tokens: TokenList = [];
  37. try {
  38. const form = metricsNewInputs ? formula.toUpperCase() : formula;
  39. tokens = parseFormula(unescapeMetricsFormula(form));
  40. } catch {
  41. // We should not end up here, but if we do, we should not crash the UI
  42. return {dependencies: [], isError: true};
  43. }
  44. const dependencies: MetricsQueryWidget[] = [];
  45. let isError = false;
  46. tokens.forEach(token => {
  47. if (token.type === TokenType.VARIABLE) {
  48. const widget = queriesLookup.get(
  49. metricsNewInputs ? token.content.toUpperCase() : token.content
  50. );
  51. if (widget) {
  52. dependencies.push(widget);
  53. } else {
  54. isError = true;
  55. }
  56. }
  57. });
  58. return {dependencies, isError};
  59. },
  60. [queriesLookup, metricsNewInputs]
  61. );
  62. const formulaDependencies = useMemo(() => {
  63. return widgets.reduce((acc: Record<number, FormulaDependencies>, widget) => {
  64. if (isMetricsEquationWidget(widget)) {
  65. acc[widget.id] = getFormulaQueryDependencies(widget.formula);
  66. }
  67. return acc;
  68. }, {});
  69. }, [getFormulaQueryDependencies, widgets]);
  70. return formulaDependencies;
  71. }