useProjectWebVitalsTimeseriesQuery.tsx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. import {getInterval} from 'sentry/components/charts/utils';
  2. import {Tag} from 'sentry/types';
  3. import {SeriesDataUnit} from 'sentry/types/echarts';
  4. import EventView, {MetaType} from 'sentry/utils/discover/eventView';
  5. import {
  6. DiscoverQueryProps,
  7. useGenericDiscoverQuery,
  8. } from 'sentry/utils/discover/genericDiscoverQuery';
  9. import {DiscoverDatasets} from 'sentry/utils/discover/types';
  10. import {useLocation} from 'sentry/utils/useLocation';
  11. import useOrganization from 'sentry/utils/useOrganization';
  12. import usePageFilters from 'sentry/utils/usePageFilters';
  13. import {calculatePerformanceScore} from 'sentry/views/performance/browser/webVitals/utils/queries/rawWebVitalsQueries/calculatePerformanceScore';
  14. type Props = {
  15. tag?: Tag;
  16. transaction?: string;
  17. };
  18. export type WebVitalsScoreBreakdown = {
  19. cls: SeriesDataUnit[];
  20. fcp: SeriesDataUnit[];
  21. fid: SeriesDataUnit[];
  22. lcp: SeriesDataUnit[];
  23. total: SeriesDataUnit[];
  24. ttfb: SeriesDataUnit[];
  25. };
  26. export const useProjectWebVitalsTimeseriesQuery = ({transaction, tag}: Props) => {
  27. const pageFilters = usePageFilters();
  28. const location = useLocation();
  29. const organization = useOrganization();
  30. const projectTimeSeriesEventView = EventView.fromNewQueryWithPageFilters(
  31. {
  32. yAxis: [
  33. 'p75(measurements.lcp)',
  34. 'p75(measurements.fcp)',
  35. 'p75(measurements.cls)',
  36. 'p75(measurements.ttfb)',
  37. 'p75(measurements.fid)',
  38. 'count()',
  39. ],
  40. name: 'Web Vitals',
  41. query:
  42. 'transaction.op:pageload' +
  43. (transaction ? ` transaction:"${transaction}"` : '') +
  44. (tag ? ` ${tag.key}:"${tag.name}"` : ''),
  45. version: 2,
  46. fields: [],
  47. interval: getInterval(pageFilters.selection.datetime, 'low'),
  48. dataset: DiscoverDatasets.METRICS,
  49. },
  50. pageFilters.selection
  51. );
  52. const result = useGenericDiscoverQuery<
  53. {
  54. data: any[];
  55. meta: MetaType;
  56. },
  57. DiscoverQueryProps
  58. >({
  59. route: 'events-stats',
  60. eventView: projectTimeSeriesEventView,
  61. location,
  62. orgSlug: organization.slug,
  63. getRequestPayload: () => ({
  64. ...projectTimeSeriesEventView.getEventsAPIPayload(location),
  65. yAxis: projectTimeSeriesEventView.yAxis,
  66. topEvents: projectTimeSeriesEventView.topEvents,
  67. excludeOther: 0,
  68. partial: 1,
  69. orderby: undefined,
  70. interval: projectTimeSeriesEventView.interval,
  71. }),
  72. options: {
  73. enabled: pageFilters.isReady,
  74. refetchOnWindowFocus: false,
  75. },
  76. referrer: 'api.performance.browser.web-vitals.timeseries-scores',
  77. });
  78. const data: WebVitalsScoreBreakdown = {
  79. lcp: [],
  80. fcp: [],
  81. cls: [],
  82. ttfb: [],
  83. fid: [],
  84. total: [],
  85. };
  86. result?.data?.['p75(measurements.lcp)']?.data.forEach((interval, index) => {
  87. const lcp: number = result?.data?.['p75(measurements.lcp)']?.data[index][1][0].count;
  88. const fcp: number = result?.data?.['p75(measurements.fcp)']?.data[index][1][0].count;
  89. const cls: number = result?.data?.['p75(measurements.cls)']?.data[index][1][0].count;
  90. const ttfb: number =
  91. result?.data?.['p75(measurements.ttfb)']?.data[index][1][0].count;
  92. const fid: number = result?.data?.['p75(measurements.fid)']?.data[index][1][0].count;
  93. // This is kinda jank, but since events-stats zero fills, we need to assume that 0 values mean no data.
  94. // 0 value for a webvital is low frequency, but not impossible. We may need to figure out a better way to handle this in the future.
  95. const {totalScore, lcpScore, fcpScore, fidScore, clsScore, ttfbScore} =
  96. calculatePerformanceScore({
  97. lcp: lcp === 0 ? Infinity : lcp,
  98. fcp: fcp === 0 ? Infinity : fcp,
  99. cls: cls === 0 ? Infinity : cls,
  100. ttfb: ttfb === 0 ? Infinity : ttfb,
  101. fid: fid === 0 ? Infinity : fid,
  102. });
  103. data.total.push({
  104. value: totalScore ?? 0,
  105. name: interval[0] * 1000,
  106. });
  107. data.cls.push({
  108. value: clsScore ?? 0,
  109. name: interval[0] * 1000,
  110. });
  111. data.lcp.push({
  112. value: lcpScore ?? 0,
  113. name: interval[0] * 1000,
  114. });
  115. data.fcp.push({
  116. value: fcpScore ?? 0,
  117. name: interval[0] * 1000,
  118. });
  119. data.ttfb.push({
  120. value: ttfbScore ?? 0,
  121. name: interval[0] * 1000,
  122. });
  123. data.fid.push({
  124. value: fidScore ?? 0,
  125. name: interval[0] * 1000,
  126. });
  127. });
  128. return {data, isLoading: result.isLoading};
  129. };