eventViewHierarchy.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. import {Fragment, useMemo} from 'react';
  2. import * as Sentry from '@sentry/react';
  3. import isEmpty from 'lodash/isEmpty';
  4. import {useFetchEventAttachments} from 'sentry/actionCreators/events';
  5. import ErrorBoundary from 'sentry/components/errorBoundary';
  6. import {getAttachmentUrl} from 'sentry/components/events/attachmentViewers/utils';
  7. import FeatureBadge from 'sentry/components/featureBadge';
  8. import LoadingIndicator from 'sentry/components/loadingIndicator';
  9. import {t} from 'sentry/locale';
  10. import {Event, IssueAttachment, Project} from 'sentry/types';
  11. import {defined} from 'sentry/utils';
  12. import {useApiQuery} from 'sentry/utils/queryClient';
  13. import useOrganization from 'sentry/utils/useOrganization';
  14. import {EventDataSection} from './eventDataSection';
  15. import {ViewHierarchy, ViewHierarchyData} 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>(
  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. {staleTime: Infinity, enabled: defined(hierarchyMeta)}
  46. );
  47. // Memoize the JSON parsing because downstream hooks depend on
  48. // referential equality of objects in the data
  49. const hierarchy = useMemo<ViewHierarchyData>(() => {
  50. if (!data) {
  51. return null;
  52. }
  53. try {
  54. return JSON.parse(data);
  55. } catch (err) {
  56. Sentry.captureException(err);
  57. return null;
  58. }
  59. }, [data]);
  60. if (isEmpty(viewHierarchies)) {
  61. return null;
  62. }
  63. // TODO(nar): This loading behaviour is subject to change
  64. if (isLoading || !data) {
  65. return <LoadingIndicator />;
  66. }
  67. return (
  68. <EventDataSection
  69. type="view_hierarchy"
  70. title={
  71. <Fragment>
  72. {t('View Hierarchy')}
  73. <FeatureBadge type="new" />
  74. </Fragment>
  75. }
  76. >
  77. <ErrorBoundary mini>
  78. <ViewHierarchy viewHierarchy={hierarchy} project={project} />
  79. </ErrorBoundary>
  80. </EventDataSection>
  81. );
  82. }
  83. function EventViewHierarchy(props: Props) {
  84. const organization = useOrganization();
  85. if (!organization.features.includes('event-attachments')) {
  86. return null;
  87. }
  88. return <EventViewHierarchyContent {...props} />;
  89. }
  90. export {EventViewHierarchy};