useResourcesQuery.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import {useDiscoverQuery} from 'sentry/utils/discover/discoverQuery';
  2. import EventView from 'sentry/utils/discover/eventView';
  3. import {DiscoverDatasets} from 'sentry/utils/discover/types';
  4. import {useLocation} from 'sentry/utils/useLocation';
  5. import useOrganization from 'sentry/utils/useOrganization';
  6. import usePageFilters from 'sentry/utils/usePageFilters';
  7. import {useResourceModuleFilters} from 'sentry/views/performance/browser/resources/utils/useResourceFilters';
  8. import {ValidSort} from 'sentry/views/performance/browser/resources/utils/useResourceSort';
  9. import {SpanMetricsField} from 'sentry/views/starfish/types';
  10. import {EMPTY_OPTION_VALUE} from 'sentry/views/starfish/views/spans/selectors/emptyOption';
  11. const {
  12. SPAN_DOMAIN,
  13. SPAN_GROUP,
  14. SPAN_DESCRIPTION,
  15. SPAN_OP,
  16. SPAN_SELF_TIME,
  17. RESOURCE_RENDER_BLOCKING_STATUS,
  18. HTTP_RESPONSE_CONTENT_LENGTH,
  19. PROJECT_ID,
  20. } = SpanMetricsField;
  21. type Props = {
  22. sort: ValidSort;
  23. defaultResourceTypes?: string[];
  24. limit?: number;
  25. query?: string;
  26. };
  27. export const DEFAULT_RESOURCE_FILTERS = ['!span.description:"browser-extension://*"'];
  28. export const useResourcesQuery = ({sort, defaultResourceTypes, query, limit}: Props) => {
  29. const pageFilters = usePageFilters();
  30. const location = useLocation();
  31. const resourceFilters = useResourceModuleFilters();
  32. const {slug: orgSlug} = useOrganization();
  33. const queryConditions = [
  34. ...(!query
  35. ? [
  36. ...DEFAULT_RESOURCE_FILTERS,
  37. ...(resourceFilters.transaction
  38. ? [`transaction:"${resourceFilters.transaction}"`]
  39. : []),
  40. ...getResourceTypeFilter(resourceFilters[SPAN_OP], defaultResourceTypes),
  41. ...getDomainFilter(resourceFilters[SPAN_DOMAIN]),
  42. ...(resourceFilters['resource.render_blocking_status']
  43. ? [
  44. `resource.render_blocking_status:${resourceFilters['resource.render_blocking_status']}`,
  45. ]
  46. : [`!resource.render_blocking_status:blocking`]),
  47. ]
  48. : []),
  49. query,
  50. ];
  51. // TODO - we should be using metrics data here
  52. const eventView = EventView.fromNewQueryWithPageFilters(
  53. {
  54. fields: [
  55. SPAN_DESCRIPTION,
  56. SPAN_OP,
  57. 'count()',
  58. `avg(${SPAN_SELF_TIME})`,
  59. 'spm()',
  60. SPAN_GROUP,
  61. SPAN_DOMAIN,
  62. `avg(${HTTP_RESPONSE_CONTENT_LENGTH})`,
  63. 'project.id',
  64. ],
  65. name: 'Resource module - resource table',
  66. query: queryConditions.join(' '),
  67. orderby: '-count',
  68. version: 2,
  69. dataset: DiscoverDatasets.SPANS_METRICS,
  70. },
  71. pageFilters.selection
  72. );
  73. if (sort) {
  74. eventView.sorts = [sort];
  75. }
  76. const result = useDiscoverQuery({
  77. eventView,
  78. limit: limit ?? 100,
  79. location,
  80. orgSlug,
  81. options: {
  82. refetchOnWindowFocus: false,
  83. },
  84. });
  85. const data = result?.data?.data.map(row => ({
  86. [SPAN_OP]: row[SPAN_OP].toString() as `resource.${string}`,
  87. [SPAN_DESCRIPTION]: row[SPAN_DESCRIPTION].toString(),
  88. ['avg(span.self_time)']: row[`avg(${SPAN_SELF_TIME})`] as number,
  89. 'count()': row['count()'] as number,
  90. 'spm()': row['spm()'] as number,
  91. [SPAN_GROUP]: row[SPAN_GROUP].toString(),
  92. [RESOURCE_RENDER_BLOCKING_STATUS]: row[RESOURCE_RENDER_BLOCKING_STATUS] as
  93. | ''
  94. | 'non-blocking'
  95. | 'blocking',
  96. [SPAN_DOMAIN]: row[SPAN_DOMAIN][0]?.toString(),
  97. [PROJECT_ID]: row[PROJECT_ID] as number,
  98. [`avg(http.response_content_length)`]: row[
  99. `avg(${HTTP_RESPONSE_CONTENT_LENGTH})`
  100. ] as number,
  101. ['count_unique(transaction)']: row['count_unique(transaction)'] as number,
  102. }));
  103. return {...result, data: data || []};
  104. };
  105. export const getDomainFilter = (selectedDomain: string | undefined) => {
  106. if (!selectedDomain) {
  107. return [];
  108. }
  109. if (selectedDomain === EMPTY_OPTION_VALUE) {
  110. return [`!has:${SPAN_DOMAIN}`];
  111. }
  112. return [`${SPAN_DOMAIN}:${selectedDomain}`];
  113. };
  114. export const getResourceTypeFilter = (
  115. selectedSpanOp: string | undefined,
  116. defaultResourceTypes: string[] | undefined
  117. ) => {
  118. let resourceFilter: string[] = [`${SPAN_OP}:resource.*`];
  119. if (selectedSpanOp) {
  120. resourceFilter = [`${SPAN_OP}:${selectedSpanOp}`];
  121. } else if (defaultResourceTypes) {
  122. resourceFilter = [`${SPAN_OP}:[${defaultResourceTypes.join(',')}]`];
  123. }
  124. return resourceFilter;
  125. };