eventViewHierarchy.tsx 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. import {Fragment, useMemo} from 'react';
  2. import * as Sentry from '@sentry/react';
  3. import {useFetchEventAttachments} from 'sentry/actionCreators/events';
  4. import ErrorBoundary from 'sentry/components/errorBoundary';
  5. import {getAttachmentUrl} from 'sentry/components/events/attachmentViewers/utils';
  6. import FeatureBadge from 'sentry/components/featureBadge';
  7. import LoadingIndicator from 'sentry/components/loadingIndicator';
  8. import {t} from 'sentry/locale';
  9. import type {Event, IssueAttachment, Project} from 'sentry/types';
  10. import {defined} from 'sentry/utils';
  11. import {useApiQuery} from 'sentry/utils/queryClient';
  12. import useOrganization from 'sentry/utils/useOrganization';
  13. import {EventDataSection} from './eventDataSection';
  14. import type {ViewHierarchyData} from './viewHierarchy';
  15. import {ViewHierarchy} from './viewHierarchy';
  16. type Props = {
  17. event: Event;
  18. project: Project;
  19. };
  20. function EventViewHierarchyContent({event, project}: Props) {
  21. const organization = useOrganization();
  22. const {data: attachments} = useFetchEventAttachments(
  23. {
  24. orgSlug: organization.slug,
  25. projectSlug: project.slug,
  26. eventId: event.id,
  27. },
  28. {notifyOnChangeProps: ['data']}
  29. );
  30. const viewHierarchies =
  31. attachments?.filter(attachment => attachment.type === 'event.view_hierarchy') ?? [];
  32. const hierarchyMeta: IssueAttachment | undefined = viewHierarchies[0];
  33. // There should be only one view hierarchy
  34. const {isLoading, data} = useApiQuery<string | ViewHierarchyData>(
  35. [
  36. defined(hierarchyMeta)
  37. ? getAttachmentUrl({
  38. attachment: hierarchyMeta,
  39. eventId: hierarchyMeta.event_id,
  40. orgId: organization.slug,
  41. projectSlug: project.slug,
  42. })
  43. : '',
  44. {
  45. headers: {
  46. Accept: '*/*; charset=utf-8',
  47. },
  48. },
  49. ],
  50. {staleTime: Infinity, enabled: defined(hierarchyMeta)}
  51. );
  52. // Memoize the JSON parsing because downstream hooks depend on
  53. // referential equality of objects in the data
  54. const hierarchy = useMemo<ViewHierarchyData>(() => {
  55. if (!data) {
  56. return null;
  57. }
  58. if (data && typeof data !== 'string') {
  59. return data;
  60. }
  61. try {
  62. return JSON.parse(data);
  63. } catch (err) {
  64. Sentry.captureException(err);
  65. return null;
  66. }
  67. }, [data]);
  68. if (viewHierarchies.length === 0) {
  69. return null;
  70. }
  71. if (isLoading || !data) {
  72. return <LoadingIndicator />;
  73. }
  74. return (
  75. <EventDataSection
  76. type="view_hierarchy"
  77. title={
  78. <Fragment>
  79. {t('View Hierarchy')}
  80. <FeatureBadge type="new" />
  81. </Fragment>
  82. }
  83. >
  84. <ErrorBoundary mini>
  85. <ViewHierarchy viewHierarchy={hierarchy} project={project} />
  86. </ErrorBoundary>
  87. </EventDataSection>
  88. );
  89. }
  90. function EventViewHierarchy(props: Props) {
  91. const organization = useOrganization();
  92. if (!organization.features.includes('event-attachments')) {
  93. return null;
  94. }
  95. return <EventViewHierarchyContent {...props} />;
  96. }
  97. export {EventViewHierarchy};