useProfileEvents.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. import {useQuery} from '@tanstack/react-query';
  2. import {ResponseMeta} from 'sentry/api';
  3. import {normalizeDateTimeParams} from 'sentry/components/organizations/pageFilters/parse';
  4. import {t} from 'sentry/locale';
  5. import {PageFilters} from 'sentry/types';
  6. import {defined} from 'sentry/utils';
  7. import {DURATION_UNITS, SIZE_UNITS} from 'sentry/utils/discover/fieldRenderers';
  8. import {FieldValueType} from 'sentry/utils/fields';
  9. import RequestError from 'sentry/utils/requestError/requestError';
  10. import useApi from 'sentry/utils/useApi';
  11. import useOrganization from 'sentry/utils/useOrganization';
  12. import usePageFilters from 'sentry/utils/usePageFilters';
  13. type Sort<F> = {
  14. key: F;
  15. order: 'asc' | 'desc';
  16. };
  17. interface UseProfileEventsOptions<F> {
  18. fields: readonly F[];
  19. referrer: string;
  20. sort: Sort<F>;
  21. cursor?: string;
  22. datetime?: PageFilters['datetime'];
  23. enabled?: boolean;
  24. limit?: number;
  25. query?: string;
  26. }
  27. type Unit = keyof typeof DURATION_UNITS | keyof typeof SIZE_UNITS | null;
  28. type EventsResultsDataRow<F extends string> = {
  29. [K in F]: string | number | null;
  30. };
  31. type EventsResultsMeta<F extends string> = {
  32. fields: Partial<{[K in F]: FieldValueType}>;
  33. units: Partial<{[K in F]: Unit}>;
  34. };
  35. export type EventsResults<F extends string> = {
  36. data: EventsResultsDataRow<F>[];
  37. meta: EventsResultsMeta<F>;
  38. };
  39. export function useProfileEvents<F extends string>({
  40. fields,
  41. limit,
  42. referrer,
  43. query,
  44. sort,
  45. cursor,
  46. enabled = true,
  47. datetime,
  48. }: UseProfileEventsOptions<F>) {
  49. const api = useApi();
  50. const organization = useOrganization();
  51. const {selection} = usePageFilters();
  52. const path = `/organizations/${organization.slug}/events/`;
  53. const endpointOptions = {
  54. query: {
  55. dataset: 'profiles',
  56. referrer,
  57. project: selection.projects,
  58. environment: selection.environments,
  59. ...normalizeDateTimeParams(datetime ?? selection.datetime),
  60. field: fields,
  61. per_page: limit,
  62. query,
  63. sort: sort.order === 'asc' ? sort.key : `-${sort.key}`,
  64. cursor,
  65. },
  66. };
  67. const queryKey = [path, endpointOptions];
  68. const queryFn = () =>
  69. api.requestPromise(path, {
  70. method: 'GET',
  71. includeAllArgs: true,
  72. query: endpointOptions.query,
  73. });
  74. return useQuery<
  75. [EventsResults<F>, string | undefined, ResponseMeta | undefined],
  76. RequestError
  77. >({
  78. queryKey,
  79. queryFn,
  80. refetchOnWindowFocus: false,
  81. retry: false,
  82. enabled,
  83. });
  84. }
  85. export function formatError(error: any): string | null {
  86. if (!defined(error)) {
  87. return null;
  88. }
  89. const detail = error.responseJSON?.detail;
  90. if (typeof detail === 'string') {
  91. return detail;
  92. }
  93. const message = detail?.message;
  94. if (typeof message === 'string') {
  95. return message;
  96. }
  97. return t('An unknown error occurred.');
  98. }
  99. export function formatSort<F extends string>(
  100. value: string | undefined,
  101. allowedKeys: readonly F[],
  102. fallback: Sort<F>
  103. ): Sort<F> {
  104. value = value || '';
  105. const order: Sort<F>['order'] = value[0] === '-' ? 'desc' : 'asc';
  106. const key = order === 'asc' ? value : value.substring(1);
  107. if (!allowedKeys.includes(key as F)) {
  108. return fallback;
  109. }
  110. return {key: key as F, order};
  111. }