fetchReplayList.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import * as Sentry from '@sentry/react';
  2. import type {Location} from 'history';
  3. import type {Client} from 'sentry/api';
  4. import {ALL_ACCESS_PROJECTS} from 'sentry/constants/pageFilters';
  5. import type {PageFilters} from 'sentry/types/core';
  6. import type {Organization} from 'sentry/types/organization';
  7. import type EventView from 'sentry/utils/discover/eventView';
  8. import {mapResponseToReplayRecord} from 'sentry/utils/replays/replayDataUtils';
  9. import type RequestError from 'sentry/utils/requestError/requestError';
  10. import type {ReplayListQueryReferrer, ReplayListRecord} from 'sentry/views/replays/types';
  11. export const DEFAULT_SORT = '-started_at';
  12. type State = {
  13. fetchError: undefined | RequestError;
  14. pageLinks: null | string;
  15. replays: undefined | ReplayListRecord[];
  16. };
  17. type Result = State;
  18. type Props = {
  19. api: Client;
  20. eventView: EventView;
  21. location: Location;
  22. organization: Organization;
  23. selection: PageFilters;
  24. perPage?: number;
  25. queryReferrer?: ReplayListQueryReferrer;
  26. };
  27. async function fetchReplayList({
  28. api,
  29. organization,
  30. location,
  31. eventView,
  32. queryReferrer,
  33. perPage,
  34. selection,
  35. }: Props): Promise<Result> {
  36. try {
  37. const path = `/organizations/${organization.slug}/replays/`;
  38. const payload = eventView.getEventsAPIPayload(location);
  39. // HACK!!! Because the sort field needs to be in the eventView, but I cannot
  40. // ask the server for compound fields like `os.name`.
  41. payload.field = payload.field.map(field => field.split('.')[0]);
  42. if (perPage) {
  43. payload.per_page = perPage;
  44. }
  45. // unique list
  46. payload.field = Array.from(new Set(payload.field));
  47. const [{data}, _textStatus, resp] = await api.requestPromise(path, {
  48. includeAllArgs: true,
  49. query: {
  50. ...payload,
  51. cursor: location.query.cursor,
  52. // when queryReferrer === 'issueReplays' we override the global view check on the backend
  53. // we also require a project param otherwise we won't yield results
  54. queryReferrer,
  55. project:
  56. queryReferrer === 'issueReplays'
  57. ? ALL_ACCESS_PROJECTS
  58. : queryReferrer === 'transactionReplays'
  59. ? selection.projects
  60. : payload.project,
  61. },
  62. });
  63. const pageLinks = resp?.getResponseHeader('Link') ?? '';
  64. return {
  65. fetchError: undefined,
  66. pageLinks,
  67. replays: payload.query ? data.map(mapResponseToReplayRecord) : [],
  68. // for the replay tab in transactions, if payload.query is undefined,
  69. // this means the transaction has no related replays.
  70. // but because we cannot query for an empty list of IDs (e.g. `id:[]` breaks our search endpoint),
  71. // and leaving query empty results in ALL replays being returned for a specified project
  72. // (which doesn't make sense as we want to show no replays),
  73. // we essentially want to hardcode no replays being returned.
  74. };
  75. } catch (error) {
  76. if (error.responseJSON?.detail) {
  77. return {
  78. fetchError: error.responseJSON.detail,
  79. pageLinks: null,
  80. replays: [],
  81. };
  82. }
  83. Sentry.captureException(error);
  84. return {
  85. fetchError: error,
  86. pageLinks: null,
  87. replays: [],
  88. };
  89. }
  90. }
  91. export default fetchReplayList;