Просмотр исходного кода

feat(crons): Add status to broken monitor environments (#66945)

Adds a new type of status icon, label for monitors that have been marked
as broken and those that have been automatically muted due to being
broken.

Dependent on: https://github.com/getsentry/sentry/pull/66934

Closes: https://github.com/getsentry/team-crons/issues/137
David Wang 11 месяцев назад
Родитель
Сommit
79731b1e0b

+ 1 - 0
fixtures/js-stubs/monitor.tsx

@@ -36,6 +36,7 @@ export function MonitorFixture(params: Partial<Monitor> = {}): Monitor {
         nextCheckIn: '2023-12-25T16:10:00Z',
         nextCheckInLatest: '2023-12-25T15:15:00Z',
         status: MonitorStatus.OK,
+        activeIncident: null,
       },
     ],
     alertRule: {

+ 31 - 5
static/app/views/monitors/components/overviewTimeline/monitorEnvironmentLabel.tsx

@@ -1,11 +1,15 @@
 import styled from '@emotion/styled';
 
 import {Tooltip} from 'sentry/components/tooltip';
+import {IconFix, IconMute} from 'sentry/icons';
+import {t} from 'sentry/locale';
 import {space} from 'sentry/styles/space';
+import type {ColorOrAlias} from 'sentry/utils/theme';
 import {
   type Monitor,
   type MonitorEnvironment,
   MonitorStatus,
+  type StatusNotice,
 } from 'sentry/views/monitors/types';
 import {statusIconColorMap} from 'sentry/views/monitors/utils/constants';
 
@@ -14,13 +18,35 @@ interface Props {
   monitorEnv: MonitorEnvironment;
 }
 
+const userNotifiedDisplay: StatusNotice = {
+  label: t(
+    'This environment is likely broken due to being in an error state for multiple days.'
+  ),
+  icon: <IconFix color="subText" />,
+  color: 'subText',
+};
+
+const envMutedDisplay: StatusNotice = {
+  label: t(
+    'This environment is likely broken due to being in an error state for multiple days. It has been automatically muted.'
+  ),
+  icon: <IconMute color="subText" />,
+  color: 'subText',
+};
+
 export default function MonitorEnvironmentLabel({monitorEnv, monitor}: Props) {
-  const {name, status, isMuted} = monitorEnv;
+  const {name, status, isMuted, activeIncident} = monitorEnv;
+  const {userNotifiedTimestamp, envMutedTimestamp} = activeIncident?.brokenNotice ?? {};
   const envStatus = monitor.isMuted || isMuted ? MonitorStatus.DISABLED : status;
-  const {label, icon} = statusIconColorMap[envStatus];
+  const {label, icon, color} = userNotifiedTimestamp
+    ? userNotifiedDisplay
+    : envMutedTimestamp
+      ? envMutedDisplay
+      : statusIconColorMap[envStatus];
+
   return (
     <EnvWithStatus>
-      <MonitorEnvLabel status={envStatus}>{name}</MonitorEnvLabel>
+      <MonitorEnvLabel color={color}>{name}</MonitorEnvLabel>
       <Tooltip title={label} skipWrapper>
         {icon}
       </Tooltip>
@@ -36,12 +62,12 @@ const EnvWithStatus = styled('div')`
   opacity: var(--disabled-opacity);
 `;
 
-const MonitorEnvLabel = styled('div')<{status: MonitorStatus}>`
+const MonitorEnvLabel = styled('div')<{color: ColorOrAlias}>`
   text-overflow: ellipsis;
   overflow: hidden;
   white-space: nowrap;
   min-width: 0;
 
-  color: ${p => p.theme[statusIconColorMap[p.status].color]};
+  color: ${p => p.theme[p.color]};
   opacity: var(--disabled-opacity);
 `;

+ 22 - 0
static/app/views/monitors/types.tsx

@@ -1,4 +1,5 @@
 import type {ObjectStatus, Project} from 'sentry/types';
+import type {ColorOrAlias} from 'sentry/utils/theme';
 
 export enum MonitorType {
   UNKNOWN = 'unknown',
@@ -69,7 +70,19 @@ export interface IntervalConfig extends BaseConfig {
 
 export type MonitorConfig = CrontabConfig | IntervalConfig;
 
+export interface MonitorEnvBrokenDetection {
+  envMutedTimestamp: string;
+  userNotifiedTimestamp: string;
+}
+
+export interface MonitorIncident {
+  brokenNotice: MonitorEnvBrokenDetection | null;
+  resolvingTimestamp: string;
+  startingTimestamp: string;
+}
+
 export interface MonitorEnvironment {
+  activeIncident: MonitorIncident | null;
   dateCreated: string;
   isMuted: boolean;
   lastCheckIn: string | null;
@@ -118,3 +131,12 @@ export interface CheckIn {
   status: CheckInStatus;
   groups?: {id: number; shortId: string}[];
 }
+
+/**
+ * Object used to store config for the display next to an environment in the timeline view
+ */
+export interface StatusNotice {
+  color: ColorOrAlias;
+  icon: React.ReactNode;
+  label: React.ReactNode;
+}

+ 2 - 6
static/app/views/monitors/utils/constants.tsx

@@ -1,9 +1,8 @@
 import type {StatusIndicatorProps} from 'sentry/components/statusIndicator';
 import {IconCheckmark, IconFire, IconTimer, IconUnsubscribed} from 'sentry/icons';
 import {t} from 'sentry/locale';
-import type {Aliases} from 'sentry/utils/theme';
 import type {StatsBucket} from 'sentry/views/monitors/components/overviewTimeline/types';
-import type {MonitorStatus} from 'sentry/views/monitors/types';
+import type {MonitorStatus, StatusNotice} from 'sentry/views/monitors/types';
 import {CheckInStatus} from 'sentry/views/monitors/types';
 
 // Orders the status in terms of ascending precedence for showing to the user
@@ -15,10 +14,7 @@ export const CHECKIN_STATUS_PRECEDENT = [
   CheckInStatus.ERROR,
 ] satisfies Array<keyof StatsBucket>;
 
-export const statusIconColorMap: Record<
-  MonitorStatus,
-  {color: keyof Aliases; icon: React.ReactNode; label: string}
-> = {
+export const statusIconColorMap: Record<MonitorStatus, StatusNotice> = {
   ok: {
     icon: <IconCheckmark color="successText" />,
     color: 'successText',