queryHandler.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import {Fragment, useEffect} from 'react';
  2. import {getUtcToLocalDateObject} from 'sentry/utils/dates';
  3. import {
  4. getIsMetricsDataFromResults,
  5. useMEPDataContext,
  6. } from 'sentry/utils/performance/contexts/metricsEnhancedPerformanceDataContext';
  7. import {QueryDefinitionWithKey, QueryHandlerProps, WidgetDataConstraint} from '../types';
  8. import {PerformanceWidgetSetting} from '../widgetDefinitions';
  9. /**
  10. * Component to handle switching component-style queries over to state. This
  11. * should be temporary to make it easier to switch away from waterfall style
  12. * api components.
  13. */
  14. export function QueryHandler<T extends WidgetDataConstraint>(
  15. props: QueryHandlerProps<T>
  16. ) {
  17. const children = props.children ?? <Fragment />;
  18. if (!props.queries.length) {
  19. return <Fragment>{children}</Fragment>;
  20. }
  21. return (
  22. <Fragment>
  23. {props.queries
  24. .filter(q => (q.enabled ? q.enabled(props.widgetData) : true))
  25. .map(query => (
  26. <SingleQueryHandler key={query.queryKey} {...props} query={query} />
  27. ))}
  28. </Fragment>
  29. );
  30. }
  31. function genericQueryReferrer(setting: PerformanceWidgetSetting) {
  32. return `api.performance.generic-widget-chart.${setting.replace(/_/g, '-')}`;
  33. }
  34. function SingleQueryHandler<T extends WidgetDataConstraint>(
  35. props: QueryHandlerProps<T> & {query: QueryDefinitionWithKey<T>}
  36. ) {
  37. const query = props.query;
  38. const globalSelection = props.queryProps.eventView.getPageFilters();
  39. const start = globalSelection.datetime.start
  40. ? getUtcToLocalDateObject(globalSelection.datetime.start)
  41. : null;
  42. const end = globalSelection.datetime.end
  43. ? getUtcToLocalDateObject(globalSelection.datetime.end)
  44. : null;
  45. useEffect(
  46. () => () => {
  47. // Destroy previous data on unmount, in case enabled value changes and unmounts the query component.
  48. props.removeWidgetDataForKey(query.queryKey);
  49. },
  50. // eslint-disable-next-line react-hooks/exhaustive-deps
  51. []
  52. );
  53. return (
  54. <query.component
  55. key={query.queryKey}
  56. fields={query.fields}
  57. yAxis={query.fields}
  58. start={start}
  59. end={end}
  60. period={globalSelection.datetime.period}
  61. project={globalSelection.projects}
  62. environment={globalSelection.environments}
  63. organization={props.queryProps.organization}
  64. orgSlug={props.queryProps.organization.slug}
  65. eventView={props.queryProps.eventView}
  66. query={props.queryProps.eventView.getQueryWithAdditionalConditions()}
  67. widgetData={props.widgetData}
  68. referrer={genericQueryReferrer(props.queryProps.chartSetting)}
  69. >
  70. {results => {
  71. return (
  72. <Fragment>
  73. <QueryResultSaver<T> results={results} {...props} query={query} />
  74. </Fragment>
  75. );
  76. }}
  77. </query.component>
  78. );
  79. }
  80. function QueryResultSaver<T extends WidgetDataConstraint>(
  81. props: {
  82. // TODO(k-fish): Fix this any.
  83. query: QueryDefinitionWithKey<T>;
  84. results: any;
  85. } & QueryHandlerProps<T>
  86. ) {
  87. const mepContext = useMEPDataContext();
  88. const {results, query} = props;
  89. const transformed = query.transform(props.queryProps, results, props.query);
  90. useEffect(() => {
  91. const isMetricsData = getIsMetricsDataFromResults(
  92. results,
  93. props.queryProps.fields[0]
  94. );
  95. mepContext.setIsMetricsData(isMetricsData);
  96. props.setWidgetDataForKey(query.queryKey, transformed);
  97. // eslint-disable-next-line react-hooks/exhaustive-deps
  98. }, [transformed?.hasData, transformed?.isLoading, transformed?.isErrored]);
  99. return <Fragment />;
  100. }