import {Fragment, type ReactElement} from 'react';
import styled from '@emotion/styled';
import moment from 'moment-timezone';
import Duration from 'sentry/components/duration';
import GlobalSelectionLink from 'sentry/components/globalSelectionLink';
import Link from 'sentry/components/links/link';
import {StatusIndicator} from 'sentry/components/statusIndicator';
import {t} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import {ActivationConditionType} from 'sentry/types/alerts';
import type {Organization} from 'sentry/types/organization';
import getDuration from 'sentry/utils/duration/getDuration';
import getDynamicText from 'sentry/utils/getDynamicText';
import {capitalize} from 'sentry/utils/string/capitalize';
import {COMPARISON_DELTA_OPTIONS} from 'sentry/views/alerts/rules/metric/constants';
import {StyledDateTime} from 'sentry/views/alerts/rules/metric/details/styles';
import {AlertRuleThresholdType} from 'sentry/views/alerts/rules/metric/types';
import type {ActivityType, Incident} from 'sentry/views/alerts/types';
import {IncidentActivityType, IncidentStatus} from 'sentry/views/alerts/types';
import {alertDetailsLink} from 'sentry/views/alerts/utils';
import {AlertWizardAlertNames} from 'sentry/views/alerts/wizard/options';
import {getAlertTypeFromAggregateDataset} from 'sentry/views/alerts/wizard/utils';
type MetricAlertActivityProps = {
incident: Incident;
organization: Organization;
};
function MetricAlertActivity({organization, incident}: MetricAlertActivityProps) {
// NOTE: while _possible_, we should never expect an incident to _not_ have a status_change activity
const activities: ActivityType[] = (incident.activities ?? []).filter(
activity => activity.type === IncidentActivityType.STATUS_CHANGE
);
const statusValues = [String(IncidentStatus.CRITICAL), String(IncidentStatus.WARNING)];
// TODO: kinda cheating with the forced `!`. Is there a better way to type this?
const latestActivity: ActivityType = activities.find(activity =>
statusValues.includes(String(activity.value))
)!;
const isCritical = Number(latestActivity.value) === IncidentStatus.CRITICAL;
// Find the _final_ most recent activity _after_ our triggered activity
// This exists for the `CLOSED` state (or any state NOT WARNING/CRITICAL)
const finalActivity = activities.find(
activity => activity.previousValue === latestActivity.value
);
const activityDuration = (
finalActivity ? moment(finalActivity.dateCreated) : moment()
).diff(moment(latestActivity.dateCreated), 'milliseconds');
const triggerLabel = isCritical ? 'critical' : 'warning';
const curentTrigger = incident.alertRule.triggers.find(
trigger => trigger.label === triggerLabel
);
const timeWindow = getDuration(incident.alertRule.timeWindow * 60);
const alertName = capitalize(
AlertWizardAlertNames[getAlertTypeFromAggregateDataset(incident.alertRule)]
);
const project = incident.alertRule.projects[0];
const activation = incident.activation;
let activationBlock: ReactElement | null = null;
// TODO: Split this string check into a separate component
if (activation) {
let condition;
let activator;
switch (activation.conditionType) {
case String(ActivationConditionType.RELEASE_CREATION):
condition = 'Release';
activator = (
{activation.activator}
);
break;
case String(ActivationConditionType.DEPLOY_CREATION):
condition = 'Deploy';
activator = activation.activator;
break;
default:
condition = '--';
}
activationBlock = (
from {condition} {activator}
);
}
return (
{latestActivity.value && (
)}
#{incident.identifier}
|
{/* If an alert rule is a % comparison based detection type */}
{incident.alertRule.detectionType !== 'dynamic' &&
incident.alertRule.comparisonDelta && (
{alertName} {curentTrigger?.alertThreshold}%
{t(
' %s in %s compared to the ',
incident.alertRule.thresholdType === AlertRuleThresholdType.ABOVE
? t('higher')
: t('lower'),
timeWindow
)}
{COMPARISON_DELTA_OPTIONS.find(
({value}) => value === incident.alertRule.comparisonDelta
)?.label ?? COMPARISON_DELTA_OPTIONS[0].label}
)}
{/* If an alert rule is a static detection type */}
{incident.alertRule.detectionType !== 'dynamic' &&
!incident.alertRule.comparisonDelta && (
{alertName}{' '}
{incident.alertRule.thresholdType === AlertRuleThresholdType.ABOVE
? t('above')
: t('below')}{' '}
{curentTrigger?.alertThreshold || '_'} {t('within')} {timeWindow}
{activationBlock}
)}
{/* If an alert rule is a dynamic detection type */}
{incident.alertRule.detectionType === 'dynamic' && (
{t('Detected an anomaly in the query for ')}
{alertName}
)}
|
{activityDuration &&
getDynamicText({
value: ,
fixed: '30s',
})}
|
|
);
}
export default MetricAlertActivity;
const Cell = styled('div')`
display: flex;
align-items: center;
white-space: nowrap;
font-size: ${p => p.theme.fontSizeMedium};
padding: ${space(1)};
overflow-x: hidden;
`;