grafana/public/app/features/alerting/unified/components/rule-viewer/tabs/Details.tsx

168 lines
5.1 KiB
TypeScript

import { css } from '@emotion/css';
import { formatDistanceToNowStrict } from 'date-fns';
import React, { useCallback } from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { Text, Stack, useStyles2, ClipboardButton, TextLink } from '@grafana/ui';
import { CombinedRule } from 'app/types/unified-alerting';
import { Annotations } from 'app/types/unified-alerting-dto';
import { isGrafanaRulerRule, isRecordingRulerRule } from '../../../utils/rules';
import { MetaText } from '../../MetaText';
import { Tokenize } from '../../Tokenize';
interface DetailsProps {
rule: CombinedRule;
}
enum RuleType {
GrafanaManagedAlertRule = 'Grafana-managed alert rule',
CloudAlertRule = 'Cloud alert rule',
CloudRecordingRule = 'Cloud recording rule',
}
const Details = ({ rule }: DetailsProps) => {
const styles = useStyles2(getStyles);
let ruleType: RuleType;
if (isGrafanaRulerRule(rule.rulerRule)) {
ruleType = RuleType.GrafanaManagedAlertRule;
} else if (isRecordingRulerRule(rule.rulerRule)) {
ruleType = RuleType.CloudRecordingRule;
} else {
// probably not the greatest assumption
ruleType = RuleType.CloudAlertRule;
}
const evaluationDuration = rule.promRule?.evaluationTime;
const evaluationTimestamp = rule.promRule?.lastEvaluation;
const copyRuleUID = useCallback(() => {
if (isGrafanaRulerRule(rule.rulerRule)) {
return rule.rulerRule.grafana_alert.uid;
} else {
return '';
}
}, [rule.rulerRule]);
const annotations: Annotations | undefined = !isRecordingRulerRule(rule.rulerRule)
? rule.annotations ?? []
: undefined;
const hasEvaluationDuration = Number.isFinite(evaluationDuration);
return (
<Stack direction="column" gap={3}>
<div className={styles.metadataWrapper}>
{/* type and identifier (optional) */}
<MetaText direction="column">
Rule type
<Text color="primary">{ruleType}</Text>
</MetaText>
<MetaText direction="column">
{isGrafanaRulerRule(rule.rulerRule) && (
<>
Rule Identifier
<Stack direction="row" alignItems="center" gap={0.5}>
<Text color="primary">
{rule.rulerRule.grafana_alert.uid}
<ClipboardButton fill="text" variant="secondary" icon="copy" size="sm" getText={copyRuleUID} />
</Text>
</Stack>
</>
)}
</MetaText>
{/* evaluation duration and pending period */}
<MetaText direction="column">
{hasEvaluationDuration && (
<>
Last evaluation
{evaluationTimestamp && evaluationDuration && (
<span>
<Text color="primary">{formatDistanceToNowStrict(new Date(evaluationTimestamp))} ago</Text>, took{' '}
<Text color="primary">{evaluationDuration}ms</Text>
</span>
)}
</>
)}
</MetaText>
<MetaText direction="column">
{!isRecordingRulerRule(rule.rulerRule) && (
<>
Pending period
<Text color="primary">{rule.rulerRule?.for ?? '0s'}</Text>
</>
)}
</MetaText>
{/* nodata and execution error state mapping */}
{isGrafanaRulerRule(rule.rulerRule) && (
<>
<MetaText direction="column">
Alert state if no data or all values are null
<Text color="primary">{rule.rulerRule.grafana_alert.no_data_state}</Text>
</MetaText>
<MetaText direction="column">
Alert state if execution error or timeout
<Text color="primary">{rule.rulerRule.grafana_alert.exec_err_state}</Text>
</MetaText>
</>
)}
</div>
{/* annotations go here */}
{annotations && (
<>
<Text variant="h4">Annotations</Text>
{Object.keys(annotations).length === 0 ? (
<Text variant="bodySmall" color="secondary" italic>
No annotations
</Text>
) : (
<div className={styles.metadataWrapper}>
{Object.entries(annotations).map(([name, value]) => (
<MetaText direction="column" key={name}>
{name}
<AnnotationValue value={value} />
</MetaText>
))}
</div>
)}
</>
)}
</Stack>
);
};
interface AnnotationValueProps {
value: string;
}
function AnnotationValue({ value }: AnnotationValueProps) {
const needsExternalLink = value && value.startsWith('http');
const tokenizeValue = <Tokenize input={value} delimiter={['{{', '}}']} />;
if (needsExternalLink) {
return (
<TextLink variant="bodySmall" href={value} external>
{value}
</TextLink>
);
}
return <Text color="primary">{tokenizeValue}</Text>;
}
const getStyles = (theme: GrafanaTheme2) => ({
metadataWrapper: css({
display: 'grid',
gridTemplateColumns: 'auto auto',
rowGap: theme.spacing(3),
columnGap: theme.spacing(12),
}),
});
export { Details };