useInpSpanSamplesWebVitalsQuery.tsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  2. import {
  3. DEFAULT_INDEXED_INTERACTION_SORT,
  4. type InteractionSpanSampleRowWithScore,
  5. SORTABLE_INDEXED_INTERACTION_FIELDS,
  6. } from 'sentry/views/insights/browser/webVitals/types';
  7. import type {BrowserType} from 'sentry/views/insights/browser/webVitals/utils/queryParameterDecoders/browserType';
  8. import {useWebVitalsSort} from 'sentry/views/insights/browser/webVitals/utils/useWebVitalsSort';
  9. import {useSpansIndexed} from 'sentry/views/insights/common/queries/useDiscover';
  10. import {SpanIndexedField, type SubregionCode} from 'sentry/views/insights/types';
  11. export function useInpSpanSamplesWebVitalsQuery({
  12. transaction,
  13. limit,
  14. enabled,
  15. filters = {},
  16. sortName,
  17. browserTypes,
  18. subregions,
  19. }: {
  20. limit: number;
  21. browserTypes?: BrowserType[];
  22. enabled?: boolean;
  23. filters?: {[key: string]: string[] | string | number | undefined};
  24. sortName?: string;
  25. subregions?: SubregionCode[];
  26. transaction?: string;
  27. }) {
  28. const filteredSortableFields = SORTABLE_INDEXED_INTERACTION_FIELDS;
  29. const sort = useWebVitalsSort({
  30. sortName,
  31. defaultSort: DEFAULT_INDEXED_INTERACTION_SORT,
  32. sortableFields: filteredSortableFields as unknown as string[],
  33. });
  34. const mutableSearch = MutableSearch.fromQueryObject({
  35. has: 'message',
  36. [`!${SpanIndexedField.SPAN_DESCRIPTION}`]: '<unknown>',
  37. 'span.op': 'ui.interaction.click',
  38. 'measurements.score.weight.inp': '>0',
  39. ...filters,
  40. });
  41. if (transaction !== undefined) {
  42. mutableSearch.addFilterValue(SpanIndexedField.ORIGIN_TRANSACTION, transaction);
  43. }
  44. if (browserTypes) {
  45. mutableSearch.addDisjunctionFilterValues(SpanIndexedField.BROWSER_NAME, browserTypes);
  46. }
  47. if (subregions) {
  48. mutableSearch.addDisjunctionFilterValues(
  49. SpanIndexedField.USER_GEO_SUBREGION,
  50. subregions
  51. );
  52. }
  53. const {data, isPending, ...rest} = useSpansIndexed(
  54. {
  55. search: mutableSearch,
  56. sorts: [sort],
  57. fields: [
  58. SpanIndexedField.INP,
  59. SpanIndexedField.INP_SCORE,
  60. SpanIndexedField.INP_SCORE_WEIGHT,
  61. SpanIndexedField.TOTAL_SCORE,
  62. SpanIndexedField.ID,
  63. SpanIndexedField.TIMESTAMP,
  64. SpanIndexedField.PROFILE_ID,
  65. SpanIndexedField.REPLAY_ID,
  66. SpanIndexedField.USER,
  67. SpanIndexedField.ORIGIN_TRANSACTION,
  68. SpanIndexedField.PROJECT,
  69. SpanIndexedField.BROWSER_NAME,
  70. SpanIndexedField.SPAN_SELF_TIME,
  71. SpanIndexedField.SPAN_DESCRIPTION,
  72. ],
  73. enabled,
  74. limit,
  75. },
  76. 'api.performance.browser.web-vitals.spans'
  77. );
  78. const tableData: InteractionSpanSampleRowWithScore[] =
  79. !isPending && data?.length
  80. ? data.map(row => {
  81. return {
  82. ...row,
  83. 'measurements.inp': row[SpanIndexedField.INP],
  84. 'user.display': row[SpanIndexedField.USER],
  85. replayId: row[SpanIndexedField.REPLAY_ID],
  86. 'profile.id': row[SpanIndexedField.PROFILE_ID],
  87. inpScore: Math.round(
  88. ((row[`measurements.score.inp`] ?? 0) /
  89. (row[`measurements.score.weight.inp`] ?? 0)) *
  90. 100
  91. ),
  92. totalScore: Math.round(row[`measurements.score.total`] ?? 0),
  93. projectSlug: row[SpanIndexedField.PROJECT],
  94. };
  95. })
  96. : [];
  97. return {
  98. data: tableData,
  99. isPending,
  100. ...rest,
  101. };
  102. }