detectorSection.tsx 3.8 KB

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