useProjectWebVitalsTimeseriesQuery.tsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  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/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. });
  77. const data: WebVitalsScoreBreakdown = {
  78. lcp: [],
  79. fcp: [],
  80. cls: [],
  81. ttfb: [],
  82. fid: [],
  83. total: [],
  84. };
  85. result?.data?.['p75(measurements.lcp)'].data.forEach((interval, index) => {
  86. const lcp: number = result?.data?.['p75(measurements.lcp)'].data[index][1][0].count;
  87. const fcp: number = result?.data?.['p75(measurements.fcp)'].data[index][1][0].count;
  88. const cls: number = result?.data?.['p75(measurements.cls)'].data[index][1][0].count;
  89. const ttfb: number = result?.data?.['p75(measurements.ttfb)'].data[index][1][0].count;
  90. const fid: number = result?.data?.['p75(measurements.fid)'].data[index][1][0].count;
  91. // This is kinda jank, but since events-stats zero fills, we need to assume that 0 values mean no data.
  92. // 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.
  93. const {totalScore, lcpScore, fcpScore, fidScore, clsScore, ttfbScore} =
  94. calculatePerformanceScore({
  95. lcp: lcp === 0 ? Infinity : lcp,
  96. fcp: fcp === 0 ? Infinity : fcp,
  97. cls: cls === 0 ? Infinity : cls,
  98. ttfb: ttfb === 0 ? Infinity : ttfb,
  99. fid: fid === 0 ? Infinity : fid,
  100. });
  101. data.total.push({
  102. value: totalScore,
  103. name: interval[0] * 1000,
  104. });
  105. data.cls.push({
  106. value: clsScore,
  107. name: interval[0] * 1000,
  108. });
  109. data.lcp.push({
  110. value: lcpScore,
  111. name: interval[0] * 1000,
  112. });
  113. data.fcp.push({
  114. value: fcpScore,
  115. name: interval[0] * 1000,
  116. });
  117. data.ttfb.push({
  118. value: ttfbScore,
  119. name: interval[0] * 1000,
  120. });
  121. data.fid.push({
  122. value: fidScore,
  123. name: interval[0] * 1000,
  124. });
  125. });
  126. return {data, isLoading: result.isLoading};
  127. };