detectorSection.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. import styled from '@emotion/styled';
  2. import {LinkButton} from 'sentry/components/button';
  3. import {t} from 'sentry/locale';
  4. import {space} from 'sentry/styles/space';
  5. import type {Event} from 'sentry/types/event';
  6. import type {Group} from 'sentry/types/group';
  7. import type {Organization} from 'sentry/types/organization';
  8. import type {Project} from 'sentry/types/project';
  9. import {getConfigForIssueType} from 'sentry/utils/issueTypeConfig';
  10. import useOrganization from 'sentry/utils/useOrganization';
  11. import {SidebarSectionTitle} from 'sentry/views/issueDetails/streamline/sidebar/sidebar';
  12. interface DetectorDetails {
  13. description?: string;
  14. detectorPath?: string;
  15. }
  16. export function getDetectorDetails({
  17. event,
  18. organization,
  19. project,
  20. }: {
  21. event: Event;
  22. organization: Organization;
  23. project: Project;
  24. }): DetectorDetails {
  25. /**
  26. * Rather than check the issue category, we just check all the current set locations
  27. * for Alert Rule IDs. Hopefully we can consolidate this when we move to the detector system.
  28. * Ideally, this function wouldn't even check the event, but rather the group/issue.
  29. */
  30. const metricAlertRuleId = event?.contexts?.metric_alert?.alert_rule_id;
  31. if (metricAlertRuleId) {
  32. return {
  33. detectorPath: `/organizations/${organization.slug}/alerts/rules/details/${metricAlertRuleId}/`,
  34. // TODO(issues): We can probably enrich this description with details from the alert itself.
  35. description: t(
  36. 'This issue was created by a metric alert detector. View the detector details to learn more.'
  37. ),
  38. };
  39. }
  40. const cronSlug = event?.tags?.find(({key}) => key === 'monitor.slug')?.value;
  41. if (cronSlug) {
  42. return {
  43. detectorPath: `/organizations/${organization.slug}/alerts/rules/crons/${project.slug}/${cronSlug}/details/`,
  44. description: t(
  45. 'This issue was created by a cron monitor. View the monitor details to learn more.'
  46. ),
  47. };
  48. }
  49. const uptimeAlertRuleId = event?.tags?.find(tag => tag?.key === 'uptime_rule')?.value;
  50. if (uptimeAlertRuleId) {
  51. return {
  52. detectorPath: `/organizations/${organization.slug}/alerts/rules/uptime/${project.slug}/${uptimeAlertRuleId}/details/`,
  53. // TODO(issues): Update this to mention detectors when that language is user-facing
  54. description: t(
  55. 'This issue was created by an uptime alert rule. After 2 consecutive failed check-ins, an open period will be created.'
  56. ),
  57. };
  58. }
  59. return {
  60. detectorPath: undefined,
  61. description: undefined,
  62. };
  63. }
  64. export function DetectorSection({
  65. event,
  66. group,
  67. project,
  68. }: {
  69. event: Event;
  70. group: Group;
  71. project: Project;
  72. }) {
  73. const organization = useOrganization();
  74. const {detectorPath, description} = getDetectorDetails({event, organization, project});
  75. const issueConfig = getConfigForIssueType(group, project);
  76. if (!detectorPath) {
  77. return null;
  78. }
  79. return (
  80. <div>
  81. <SidebarSectionTitle>
  82. {issueConfig.detector.title ?? t('Detector')}
  83. </SidebarSectionTitle>
  84. {description && <DetectorDescription>{description}</DetectorDescription>}
  85. <LinkButton
  86. aria-label={issueConfig.detector.ctaText ?? t('View detector details')}
  87. href={detectorPath}
  88. style={{width: '100%'}}
  89. size="sm"
  90. >
  91. {issueConfig.detector.ctaText ?? t('View detector details')}
  92. </LinkButton>
  93. </div>
  94. );
  95. }
  96. const DetectorDescription = styled('p')`
  97. margin: ${space(1)} 0;
  98. `;