useHasLinkedIssues.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import type {ExternalIssueComponent} from 'sentry/components/group/externalIssuesList/types';
  2. import useIssueTrackingFilter from 'sentry/components/group/externalIssuesList/useIssueTrackingFilter';
  3. import SentryAppInstallationStore from 'sentry/stores/sentryAppInstallationsStore';
  4. import {useLegacyStore} from 'sentry/stores/useLegacyStore';
  5. import type {Event} from 'sentry/types/event';
  6. import type {Group} from 'sentry/types/group';
  7. import type {Project} from 'sentry/types/project';
  8. import useOrganization from 'sentry/utils/useOrganization';
  9. import useSentryAppComponentsStore from 'sentry/utils/useSentryAppComponentsStore';
  10. type Props = {
  11. event: Event;
  12. group: Group;
  13. project: Project;
  14. };
  15. export default function useExternalIssueData({group, event, project}: Props) {
  16. const organization = useOrganization();
  17. const issueTrackingFilter = useIssueTrackingFilter();
  18. const components = useSentryAppComponentsStore({componentType: 'issue-link'});
  19. const sentryAppInstallations = useLegacyStore(SentryAppInstallationStore);
  20. const renderSentryAppIssues = (): ExternalIssueComponent[] => {
  21. return components
  22. .map<ExternalIssueComponent | null>(component => {
  23. const {sentryApp, error: disabled} = component;
  24. const installation = sentryAppInstallations.find(
  25. i => i.app.uuid === sentryApp.uuid
  26. );
  27. // should always find a match but TS complains if we don't handle this case
  28. if (!installation) {
  29. return null;
  30. }
  31. const issue = (group.sentryAppIssues || []).find(
  32. i => i.serviceType === sentryApp.slug
  33. );
  34. return {
  35. type: 'sentry-app-issue',
  36. key: sentryApp.slug,
  37. disabled,
  38. hasLinkedIssue: !!issue,
  39. props: {
  40. sentryApp,
  41. group,
  42. organization,
  43. event,
  44. sentryAppComponent: component,
  45. sentryAppInstallation: installation,
  46. externalIssue: issue,
  47. disabled,
  48. },
  49. };
  50. })
  51. .filter((x): x is ExternalIssueComponent => x !== null);
  52. };
  53. const renderIntegrationIssues = (): ExternalIssueComponent[] => {
  54. return (
  55. group.integrationIssues?.map(issue => ({
  56. type: 'integration-issue',
  57. key: issue.key,
  58. disabled: false,
  59. hasLinkedIssue: true,
  60. props: {
  61. configurations: [],
  62. externalIssue: issue,
  63. group,
  64. onChange: () => {},
  65. },
  66. })) ?? []
  67. );
  68. };
  69. const renderPluginIssues = (): ExternalIssueComponent[] => {
  70. return group.pluginIssues?.map((plugin, i) => ({
  71. type: 'plugin-issue',
  72. key: `plugin-issue-${i}`,
  73. disabled: false,
  74. hasLinkedIssue: true,
  75. props: {
  76. group,
  77. project,
  78. plugin,
  79. },
  80. }));
  81. };
  82. const renderPluginActions = (): ExternalIssueComponent[] => {
  83. return (
  84. group.pluginActions?.map((plugin, i) => ({
  85. type: 'plugin-action',
  86. key: `plugin-action-${i}`,
  87. disabled: false,
  88. hasLinkedIssue: false,
  89. props: {plugin},
  90. })) ?? []
  91. );
  92. };
  93. const linkedIssues = [
  94. ...renderSentryAppIssues(),
  95. ...renderIntegrationIssues(),
  96. ...renderPluginIssues(),
  97. ...renderPluginActions(),
  98. ].filter(issue => !issueTrackingFilter || issue.key === issueTrackingFilter);
  99. // Plugins: need to do some extra logic to detect if the issue is linked,
  100. // by checking if there exists an issue object
  101. const plugins = linkedIssues.filter(
  102. a =>
  103. (a.type === 'plugin-issue' || a.type === 'plugin-action') &&
  104. 'issue' in a.props.plugin
  105. );
  106. // Sentry app issues: read from `hasLinkedIssue` property
  107. const sentryAppIssues = linkedIssues.filter(
  108. a =>
  109. a.hasLinkedIssue &&
  110. a.type === 'sentry-app-issue' &&
  111. a.props.externalIssue?.issueId === group.id
  112. );
  113. // Integration issues
  114. const integrationIssues = linkedIssues.filter(a => a.type === 'integration-issue');
  115. return {linkedIssues: plugins.concat(integrationIssues).concat(sentryAppIssues)};
  116. }