useHasLinkedIssues.tsx 3.9 KB

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