eventViewHierarchy.tsx 3.1 KB

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