Alerting: Add missing_series_evals_to_resolve option to alert rule form (#102808)

* Add missing_series_evals_to_resolve option to alert rule form

* Fix tests and undefined behaviour

* Update translations

* Update translations

* Update falsy logic

* Remove null type

* Update tests

* Update details tab

* Add default value

* Remove null

* Update description text, add placeholder, remove default
This commit is contained in:
Tom Ratcliffe
2025-04-07 19:49:45 +01:00
committed by GitHub
parent f8a72214cf
commit c2af05c328
10 changed files with 67 additions and 1 deletions
@@ -142,6 +142,7 @@ export function GrafanaEvaluationBehaviorStep({
getValues,
formState: { errors },
control,
register,
} = useFormContext<RuleFormValues>();
const [group, type, isPaused, folder, evaluateEvery] = watch([
@@ -373,6 +374,29 @@ export function GrafanaEvaluationBehaviorStep({
name="execErrState"
/>
</Field>
<Field
label={t('alerting.alert.missing-series-resolve', 'Missing series evaluations to resolve')}
description={t(
'alerting.alert.description-missing-series-evaluations',
'How many consecutive evaluation intervals with no data for a dimension must pass before the alert state is considered stale and automatically resolved. If no value is provided, the value will default to 2.'
)}
invalid={!!errors.missingSeriesEvalsToResolve?.message}
error={errors.missingSeriesEvalsToResolve?.message}
className={styles.inlineField}
htmlFor="missing-series-resolve"
>
<Input
placeholder={t(
'alerting.grafana-evaluation-behavior-step.missing-series-resolve-placeholder',
'Default: 2'
)}
id="missing-series-resolve"
{...register('missingSeriesEvalsToResolve', {
pattern: { value: /^\d+$/, message: 'Must be a positive integer.' },
})}
width={21}
/>
</Field>
</>
)}
</>
@@ -118,6 +118,7 @@ const expectedModifiedRule2 = (uid: string) => ({
no_data_state: 'NoData',
title: 'Rule2 updated',
uid: uid,
missing_series_evals_to_resolve: 0,
},
keep_firing_for: '1m',
labels: {
@@ -142,6 +142,7 @@ exports[`Can create a new grafana managed alert using simplified routing can cre
],
"exec_err_state": "Error",
"is_paused": false,
"missing_series_evals_to_resolve": 0,
"no_data_state": "NoData",
"notification_settings": {
"receiver": "lotsa-emails",
@@ -317,6 +318,7 @@ exports[`Can create a new grafana managed alert using simplified routing switch
"simplified_query_and_expressions_section": false,
},
},
"missing_series_evals_to_resolve": 0,
"no_data_state": "NoData",
"title": "my great new rule",
},
@@ -489,6 +491,7 @@ exports[`Can create a new grafana managed alert using simplified routing switch
"simplified_query_and_expressions_section": true,
},
},
"missing_series_evals_to_resolve": 0,
"no_data_state": "NoData",
"notification_settings": {
"receiver": "lotsa-emails",
@@ -664,6 +667,7 @@ exports[`Can create a new grafana managed alert using simplified routing switch
"simplified_query_and_expressions_section": true,
},
},
"missing_series_evals_to_resolve": 0,
"no_data_state": "NoData",
"title": "my great new rule",
},
@@ -836,6 +840,7 @@ exports[`Can create a new grafana managed alert using simplified routing switch
"simplified_query_and_expressions_section": false,
},
},
"missing_series_evals_to_resolve": 0,
"no_data_state": "NoData",
"notification_settings": {
"receiver": "lotsa-emails",
@@ -1,5 +1,6 @@
import { css } from '@emotion/css';
import { formatDistanceToNowStrict } from 'date-fns';
import { isUndefined } from 'lodash';
import { GrafanaTheme2, dateTimeFormat, dateTimeFormatTimeAgo } from '@grafana/data';
import { config } from '@grafana/runtime';
@@ -77,6 +78,12 @@ export const Details = ({ rule }: DetailsProps) => {
const updated = rulerRuleType.grafana.rule(rule.rulerRule) ? rule.rulerRule.grafana_alert.updated : undefined;
const isPaused = rulerRuleType.grafana.rule(rule.rulerRule) && isPausedRule(rule.rulerRule);
const missingSeriesEvalsToResolve =
rulerRuleType.grafana.rule(rule.rulerRule) &&
!isUndefined(rule.rulerRule.grafana_alert.missing_series_evals_to_resolve)
? String(rule.rulerRule.grafana_alert.missing_series_evals_to_resolve)
: undefined;
const pausedIcon = (
<Stack>
<Text color="warning">
@@ -152,7 +159,18 @@ export const Details = ({ rule }: DetailsProps) => {
<DetailText
id="last-evaluation-duration"
label={t('alerting.alert.last-evaluation-duration', 'Last evaluation duration')}
value={isPaused ? pausedIcon : `${evaluationDuration} ms`}
value={`${evaluationDuration} ms`}
/>
)}
{missingSeriesEvalsToResolve && (
<DetailText
id="missing-series-resolve"
label={t('alerting.alert.missing-series-resolve', 'Missing series evaluations to resolve')}
value={missingSeriesEvalsToResolve}
tooltipValue={t(
'alerting.alert.description-missing-series-evaluations',
'How many consecutive evaluation intervals with no data for a dimension must pass before the alert state is considered stale and automatically resolved. If no value is provided, the value will default to 2.'
)}
/>
)}
</>
@@ -144,6 +144,7 @@ exports[`RuleEditor grafana managed rules can create new grafana managed alert 1
],
"exec_err_state": "Error",
"is_paused": false,
"missing_series_evals_to_resolve": 0,
"no_data_state": "NoData",
"title": "my great new rule",
},
@@ -252,6 +253,7 @@ exports[`RuleEditor grafana managed rules can restore grafana managed alert when
"simplified_query_and_expressions_section": false,
},
},
"missing_series_evals_to_resolve": 0,
"no_data_state": "NoData",
"title": "Grafana-rule",
},
@@ -63,6 +63,7 @@ export interface RuleFormValues {
keepFiringForTime?: number;
keepFiringForTimeUnit?: string;
expression: string;
missingSeriesEvalsToResolve?: number;
}
export type Folder = { title: string; uid: string };
@@ -10,6 +10,7 @@ exports[`formValuesToRulerGrafanaRuleDTO should correctly convert rule form valu
"exec_err_state": "Error",
"is_paused": false,
"metadata": undefined,
"missing_series_evals_to_resolve": 0,
"no_data_state": "NoData",
"notification_settings": undefined,
"title": "",
@@ -63,6 +64,7 @@ exports[`formValuesToRulerGrafanaRuleDTO should not save both instant and range
"exec_err_state": "Error",
"is_paused": false,
"metadata": undefined,
"missing_series_evals_to_resolve": 0,
"no_data_state": "NoData",
"notification_settings": undefined,
"title": "",
@@ -128,12 +128,14 @@ export function getNotificationSettingsForDTO(
}
return undefined;
}
function getEditorSettingsForDTO(simplifiedEditor: SimplifiedEditor) {
return {
simplified_query_and_expressions_section: simplifiedEditor.simplifiedQueryEditor,
simplified_notifications_section: simplifiedEditor.simplifiedNotificationEditor,
};
}
export function formValuesToRulerGrafanaRuleDTO(values: RuleFormValues): PostableRuleGrafanaRuleDTO {
const {
name,
@@ -149,6 +151,7 @@ export function formValuesToRulerGrafanaRuleDTO(values: RuleFormValues): Postabl
type,
metric,
targetDatasourceUid,
missingSeriesEvalsToResolve,
} = values;
if (!condition) {
throw new Error('You cannot create an alert rule without specifying the alert condition');
@@ -178,6 +181,10 @@ export function formValuesToRulerGrafanaRuleDTO(values: RuleFormValues): Postabl
exec_err_state: execErrState,
notification_settings: notificationSettings,
metadata,
missing_series_evals_to_resolve: missingSeriesEvalsToResolve
? Number(missingSeriesEvalsToResolve)
: // API uses 0 value to reset, as `missing_series_evals_to_resolve` cannot be 0
0,
},
annotations,
labels,
@@ -315,6 +322,8 @@ export function rulerRuleToFormValues(ruleWithLocation: RuleWithLocation): RuleF
manualRouting: Boolean(routingSettings),
editorSettings: getEditorSettingsFromDTO(ga),
missingSeriesEvalsToResolve: ga.missing_series_evals_to_resolve,
};
} else {
throw new Error('Unexpected type of rule for grafana rules source');
+1
View File
@@ -268,6 +268,7 @@ export interface PostableGrafanaRuleDefinition {
target_datasource_uid?: string;
};
intervalSeconds?: number;
missing_series_evals_to_resolve?: number;
}
export interface GrafanaRuleDefinition extends PostableGrafanaRuleDefinition {
id?: string;
+3
View File
@@ -297,6 +297,7 @@
"alert": {
"alert-state": "Alert state",
"annotations": "Annotations",
"description-missing-series-evaluations": "How many consecutive evaluation intervals with no data for a dimension must pass before the alert state is considered stale and automatically resolved. If no value is provided, the value will default to 2.",
"evaluation": "Evaluation",
"evaluation-paused": "Alert evaluation currently paused",
"evaluation-paused-description": "Notifications for this rule will not fire and no alert instances will be created until the rule is un-paused.",
@@ -305,6 +306,7 @@
"last-evaluation-duration": "Last evaluation duration",
"last-updated-at": "Last updated at",
"last-updated-by": "Last updated by",
"missing-series-resolve": "Missing series evaluations to resolve",
"no-annotations": "No annotations",
"pending-period": "Pending period",
"rule": "Rule",
@@ -923,6 +925,7 @@
"label-disable-resolved-message": "Disable resolved message"
},
"grafana-evaluation-behavior-step": {
"missing-series-resolve-placeholder": "Default: 2",
"or": "or",
"text-configure-no-data-and-error-handling": "Configure no data and error handling",
"title-set-evaluation-behavior": "Set evaluation behavior"