eventViewHierarchy.tsx 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. import {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 LoadingIndicator from 'sentry/components/loadingIndicator';
  7. import {t} from 'sentry/locale';
  8. import type {Event} from 'sentry/types/event';
  9. import type {IssueAttachment} from 'sentry/types/group';
  10. import type {Project} from 'sentry/types/project';
  11. import {defined} from 'sentry/utils';
  12. import {useApiQuery} from 'sentry/utils/queryClient';
  13. import useOrganization from 'sentry/utils/useOrganization';
  14. import {SectionKey} from 'sentry/views/issueDetails/streamline/context';
  15. import {InterimSection} from 'sentry/views/issueDetails/streamline/interimSection';
  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 {isPending, data} = useApiQuery<string | ViewHierarchyData>(
  37. [
  38. defined(hierarchyMeta)
  39. ? getAttachmentUrl({
  40. attachment: hierarchyMeta,
  41. eventId: hierarchyMeta.event_id,
  42. orgSlug: 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 (isPending || !data) {
  74. return <LoadingIndicator />;
  75. }
  76. return (
  77. <InterimSection title={t('View Hierarchy')} type={SectionKey.VIEW_HIERARCHY}>
  78. <ErrorBoundary mini>
  79. <ViewHierarchy viewHierarchy={hierarchy} project={project} />
  80. </ErrorBoundary>
  81. </InterimSection>
  82. );
  83. }
  84. function EventViewHierarchy(props: Props) {
  85. const organization = useOrganization();
  86. if (!organization.features.includes('event-attachments')) {
  87. return null;
  88. }
  89. return <EventViewHierarchyContent {...props} />;
  90. }
  91. export {EventViewHierarchy};