useSortedTopNSeries.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. import type {Series} from 'sentry/types/echarts';
  2. import type {EventsStats, MultiSeriesEventsStats} from 'sentry/types/organization';
  3. import {encodeSort} from 'sentry/utils/discover/eventView';
  4. import {DURATION_UNITS, SIZE_UNITS} from 'sentry/utils/discover/fieldRenderers';
  5. import {getAggregateAlias} from 'sentry/utils/discover/fields';
  6. import {
  7. type DiscoverQueryProps,
  8. useGenericDiscoverQuery,
  9. } from 'sentry/utils/discover/genericDiscoverQuery';
  10. import {DiscoverDatasets} from 'sentry/utils/discover/types';
  11. import type {MutableSearch} from 'sentry/utils/tokenizeSearch';
  12. import {useLocation} from 'sentry/utils/useLocation';
  13. import useOrganization from 'sentry/utils/useOrganization';
  14. import usePageFilters from 'sentry/utils/usePageFilters';
  15. import {getSeriesEventView} from 'sentry/views/insights/common/queries/getSeriesEventView';
  16. import type {SpanFunctions, SpanIndexedField} from 'sentry/views/insights/types';
  17. import {getRetryDelay, shouldRetryHandler} from '../utils/retryHandlers';
  18. interface Options<Fields> {
  19. enabled?: boolean;
  20. fields?: string[];
  21. interval?: string;
  22. orderby?: string | string[];
  23. overriddenRoute?: string;
  24. referrer?: string;
  25. search?: MutableSearch;
  26. topEvents?: number;
  27. yAxis?: Fields;
  28. }
  29. export const useSortedTopNSeries = <
  30. Fields extends SpanIndexedField[] | SpanFunctions[] | string[],
  31. >(
  32. options: Options<Fields> = {},
  33. referrer: string,
  34. dataset?: DiscoverDatasets
  35. ) => {
  36. const location = useLocation();
  37. const organization = useOrganization();
  38. const {
  39. search = undefined,
  40. yAxis = [],
  41. interval = undefined,
  42. topEvents = 5,
  43. fields,
  44. orderby,
  45. overriddenRoute,
  46. enabled,
  47. } = options;
  48. const pageFilters = usePageFilters();
  49. const eventView = getSeriesEventView(
  50. search,
  51. fields,
  52. pageFilters.selection,
  53. yAxis,
  54. topEvents,
  55. dataset ?? DiscoverDatasets.SPANS_INDEXED,
  56. orderby
  57. );
  58. if (interval) {
  59. eventView.interval = interval;
  60. }
  61. const result = useGenericDiscoverQuery<MultiSeriesEventsStats, DiscoverQueryProps>({
  62. route: overriddenRoute ?? 'events-stats',
  63. eventView,
  64. location,
  65. orgSlug: organization.slug,
  66. getRequestPayload: () => ({
  67. ...eventView.getEventsAPIPayload(location),
  68. yAxis: eventView.yAxis,
  69. topEvents: eventView.topEvents,
  70. excludeOther: 0,
  71. partial: 1,
  72. orderby: eventView.sorts?.[0] ? encodeSort(eventView.sorts?.[0]) : undefined,
  73. interval: eventView.interval,
  74. }),
  75. options: {
  76. enabled: enabled && pageFilters.isReady,
  77. refetchOnWindowFocus: false,
  78. retry: shouldRetryHandler,
  79. retryDelay: getRetryDelay,
  80. staleTime: Infinity,
  81. },
  82. referrer,
  83. });
  84. const isFetchingOrLoading = result.isPending || result.isFetching;
  85. const data: Series[] = isFetchingOrLoading ? [] : transformToSeries(result.data);
  86. const pageLinks = result.response?.getResponseHeader('Link') ?? undefined;
  87. return {
  88. ...result,
  89. pageLinks,
  90. data,
  91. meta: result.data?.meta,
  92. };
  93. };
  94. function transformToSeries(result: MultiSeriesEventsStats | undefined): Series[] {
  95. if (!result) {
  96. return [];
  97. }
  98. const processedResults: [number, Series][] = Object.keys(result).map(seriesName => {
  99. const seriesData: EventsStats = result[seriesName];
  100. let scale = 1;
  101. if (seriesName) {
  102. const unit = seriesData.meta?.units?.[getAggregateAlias(seriesName)];
  103. // Scale series values to milliseconds or bytes depending on units from meta
  104. scale = (unit && (DURATION_UNITS[unit] ?? SIZE_UNITS[unit])) ?? 1;
  105. }
  106. const processsedData: Series = {
  107. seriesName: seriesName || '(empty string)',
  108. data: seriesData.data.map(([timestamp, countsForTimestamp]) => ({
  109. name: timestamp * 1000,
  110. value: countsForTimestamp.reduce((acc, {count}) => acc + count, 0) * scale,
  111. })),
  112. };
  113. return [seriesData.order || 0, processsedData];
  114. });
  115. return processedResults.sort(([a], [b]) => a - b).map(([, series]) => series);
  116. }