grafana/public/app/features/alerting/unified/utils/rule-form.ts

653 lines
20 KiB
TypeScript
Raw Normal View History

import { omit } from 'lodash';
import {
DataQuery,
Alerting: Add notification policies preview in alert creation (#68839) * Add notification policies preview in alert rule form Co-authored-by: Konrad Lalik <konrad.lalik@grafana.com> * Refactor using new useGetPotentialInstances hook and apply some style changes * Add notification policy detail modal * Use backtesting api for simulating potential alert instances * Fix logic to travserse all the children from the root route * Split notification preview by alert manager * Add instance count to matching policy header and fix some styles * Move some logic to a new hook useGetAlertManagersSourceNames to make the code more clean * Fix some tests * Add initial test for NotificationPreview * Use button to preview potential instances * Add link to contact point details * Add route matching result details * Show AlertManager image in the routing preview list * Add tests setup, add single AM preview test * Handle no matchers and no labels use case * Update some style in collapse component and fix policy path in modal * Update modal styles * Update styles * Update collapse header styling * Normalize tree nodes should happen before findMatchingRoutes call * Fix findMatchingRoutes and findMatchingAlertGroups methods after reabasing * Move instances matching to the web worker code * Fix config fetching for vanilla prometheus AMs * Add tests * Add tests mocks * Fix tests after adding web worker * Display matching labels for each matching alert instance * Add minor css improvements * Revert changes added in Collapse component as we don't use it anymore * Move the route details modal to a separate file * Move NotificationRoute and preview hook into separate files * Fix Alertmanager preview tests * Fix tests * Move matcher code to a separate file, improve matcher mock * Add permissions control for contact point edit view link * Fix from and to for the temporal use of backtesting api * Fix tests, add lazy loading of the preview component Co-authored-by: Sonia Aguilar <soniaaguilarpeiron@gmail.com> * Fix preview test * Add onclick on the header div so it collapse and expands when clicking on it, and update styles to be consistent with the rest of tables * Adapt the code to the new rule testing endpoint definition * Fix tests * small changes after reviewing the final code * compute entire inherited tree before computing the routes map * Throw error in case of not having receiver in routesByIdMap and add test for the use case of inheriting receiver from parent to check UI throws no errors * Add list of labels in the policy route path that produces the policy matchers to match potential instances * Use color determined by the key, in label tags when hovering matchers in the policy tree * Remove labels in modal and handle empty string as receiver to inherit from parent as we do with undefined * Revert "Add list of labels in the policy route path that produces the policy matchers to match potential instances" This reverts commit ee73ae9cf9caf10c33bcd58973279f23437a9146. * fix inheritance for computeInheritedTree * Fix message shown when preview has not been executed yet * First round for adressing PR review comments * Adress the rest of PR review commments * Update texts and rename id prop in NotificaitonStep to alertUid --------- Co-authored-by: Konrad Lalik <konrad.lalik@grafana.com> Co-authored-by: Gilles De Mey <gilles.de.mey@gmail.com>
2023-06-19 19:32:17 +08:00
DataSourceInstanceSettings,
DataSourceRef,
getDefaultRelativeTimeRange,
IntervalValues,
rangeUtil,
RelativeTimeRange,
ScopedVars,
TimeRange,
} from '@grafana/data';
import { getDataSourceSrv } from '@grafana/runtime';
import { ExpressionDatasourceRef } from '@grafana/runtime/src/utils/DataSourceWithBackend';
import { DataSourceJsonData } from '@grafana/schema';
import { getNextRefIdChar } from 'app/core/utils/query';
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
Alerting: Add notification policies preview in alert creation (#68839) * Add notification policies preview in alert rule form Co-authored-by: Konrad Lalik <konrad.lalik@grafana.com> * Refactor using new useGetPotentialInstances hook and apply some style changes * Add notification policy detail modal * Use backtesting api for simulating potential alert instances * Fix logic to travserse all the children from the root route * Split notification preview by alert manager * Add instance count to matching policy header and fix some styles * Move some logic to a new hook useGetAlertManagersSourceNames to make the code more clean * Fix some tests * Add initial test for NotificationPreview * Use button to preview potential instances * Add link to contact point details * Add route matching result details * Show AlertManager image in the routing preview list * Add tests setup, add single AM preview test * Handle no matchers and no labels use case * Update some style in collapse component and fix policy path in modal * Update modal styles * Update styles * Update collapse header styling * Normalize tree nodes should happen before findMatchingRoutes call * Fix findMatchingRoutes and findMatchingAlertGroups methods after reabasing * Move instances matching to the web worker code * Fix config fetching for vanilla prometheus AMs * Add tests * Add tests mocks * Fix tests after adding web worker * Display matching labels for each matching alert instance * Add minor css improvements * Revert changes added in Collapse component as we don't use it anymore * Move the route details modal to a separate file * Move NotificationRoute and preview hook into separate files * Fix Alertmanager preview tests * Fix tests * Move matcher code to a separate file, improve matcher mock * Add permissions control for contact point edit view link * Fix from and to for the temporal use of backtesting api * Fix tests, add lazy loading of the preview component Co-authored-by: Sonia Aguilar <soniaaguilarpeiron@gmail.com> * Fix preview test * Add onclick on the header div so it collapse and expands when clicking on it, and update styles to be consistent with the rest of tables * Adapt the code to the new rule testing endpoint definition * Fix tests * small changes after reviewing the final code * compute entire inherited tree before computing the routes map * Throw error in case of not having receiver in routesByIdMap and add test for the use case of inheriting receiver from parent to check UI throws no errors * Add list of labels in the policy route path that produces the policy matchers to match potential instances * Use color determined by the key, in label tags when hovering matchers in the policy tree * Remove labels in modal and handle empty string as receiver to inherit from parent as we do with undefined * Revert "Add list of labels in the policy route path that produces the policy matchers to match potential instances" This reverts commit ee73ae9cf9caf10c33bcd58973279f23437a9146. * fix inheritance for computeInheritedTree * Fix message shown when preview has not been executed yet * First round for adressing PR review comments * Adress the rest of PR review commments * Update texts and rename id prop in NotificaitonStep to alertUid --------- Co-authored-by: Konrad Lalik <konrad.lalik@grafana.com> Co-authored-by: Gilles De Mey <gilles.de.mey@gmail.com>
2023-06-19 19:32:17 +08:00
import { ExpressionDatasourceUID, ExpressionQuery, ExpressionQueryType } from 'app/features/expressions/types';
import { LokiQuery } from 'app/plugins/datasource/loki/types';
import { PromQuery } from 'app/plugins/datasource/prometheus/types';
import { RuleWithLocation } from 'app/types/unified-alerting';
import {
AlertDataQuery,
AlertQuery,
Annotations,
GrafanaAlertStateDecision,
GrafanaNotificationSettings,
GrafanaRuleDefinition,
Labels,
PostableRuleGrafanaRuleDTO,
RulerAlertingRuleDTO,
RulerRecordingRuleDTO,
RulerRuleDTO,
} from 'app/types/unified-alerting-dto';
import { EvalFunction } from '../../state/alertDef';
import { AlertManagerManualRouting, ContactPoint, RuleFormType, RuleFormValues } from '../types/rule-form';
import { getRulesAccess } from './access-control';
import { Annotation, defaultAnnotations } from './constants';
import { getDefaultOrFirstCompatibleDataSource, GRAFANA_RULES_SOURCE_NAME, isGrafanaRulesSource } from './datasource';
import { arrayToRecord, recordToArray } from './misc';
import { isAlertingRulerRule, isGrafanaRulerRule, isRecordingRulerRule } from './rules';
import { parseInterval } from './time';
export type PromOrLokiQuery = PromQuery | LokiQuery;
Alerting: Add notification policies preview in alert creation (#68839) * Add notification policies preview in alert rule form Co-authored-by: Konrad Lalik <konrad.lalik@grafana.com> * Refactor using new useGetPotentialInstances hook and apply some style changes * Add notification policy detail modal * Use backtesting api for simulating potential alert instances * Fix logic to travserse all the children from the root route * Split notification preview by alert manager * Add instance count to matching policy header and fix some styles * Move some logic to a new hook useGetAlertManagersSourceNames to make the code more clean * Fix some tests * Add initial test for NotificationPreview * Use button to preview potential instances * Add link to contact point details * Add route matching result details * Show AlertManager image in the routing preview list * Add tests setup, add single AM preview test * Handle no matchers and no labels use case * Update some style in collapse component and fix policy path in modal * Update modal styles * Update styles * Update collapse header styling * Normalize tree nodes should happen before findMatchingRoutes call * Fix findMatchingRoutes and findMatchingAlertGroups methods after reabasing * Move instances matching to the web worker code * Fix config fetching for vanilla prometheus AMs * Add tests * Add tests mocks * Fix tests after adding web worker * Display matching labels for each matching alert instance * Add minor css improvements * Revert changes added in Collapse component as we don't use it anymore * Move the route details modal to a separate file * Move NotificationRoute and preview hook into separate files * Fix Alertmanager preview tests * Fix tests * Move matcher code to a separate file, improve matcher mock * Add permissions control for contact point edit view link * Fix from and to for the temporal use of backtesting api * Fix tests, add lazy loading of the preview component Co-authored-by: Sonia Aguilar <soniaaguilarpeiron@gmail.com> * Fix preview test * Add onclick on the header div so it collapse and expands when clicking on it, and update styles to be consistent with the rest of tables * Adapt the code to the new rule testing endpoint definition * Fix tests * small changes after reviewing the final code * compute entire inherited tree before computing the routes map * Throw error in case of not having receiver in routesByIdMap and add test for the use case of inheriting receiver from parent to check UI throws no errors * Add list of labels in the policy route path that produces the policy matchers to match potential instances * Use color determined by the key, in label tags when hovering matchers in the policy tree * Remove labels in modal and handle empty string as receiver to inherit from parent as we do with undefined * Revert "Add list of labels in the policy route path that produces the policy matchers to match potential instances" This reverts commit ee73ae9cf9caf10c33bcd58973279f23437a9146. * fix inheritance for computeInheritedTree * Fix message shown when preview has not been executed yet * First round for adressing PR review comments * Adress the rest of PR review commments * Update texts and rename id prop in NotificaitonStep to alertUid --------- Co-authored-by: Konrad Lalik <konrad.lalik@grafana.com> Co-authored-by: Gilles De Mey <gilles.de.mey@gmail.com>
2023-06-19 19:32:17 +08:00
export const MINUTE = '1m';
export const getDefaultFormValues = (): RuleFormValues => {
const { canCreateGrafanaRules, canCreateCloudRules } = getRulesAccess();
return Object.freeze({
name: '',
Alerting: Add notification policies preview in alert creation (#68839) * Add notification policies preview in alert rule form Co-authored-by: Konrad Lalik <konrad.lalik@grafana.com> * Refactor using new useGetPotentialInstances hook and apply some style changes * Add notification policy detail modal * Use backtesting api for simulating potential alert instances * Fix logic to travserse all the children from the root route * Split notification preview by alert manager * Add instance count to matching policy header and fix some styles * Move some logic to a new hook useGetAlertManagersSourceNames to make the code more clean * Fix some tests * Add initial test for NotificationPreview * Use button to preview potential instances * Add link to contact point details * Add route matching result details * Show AlertManager image in the routing preview list * Add tests setup, add single AM preview test * Handle no matchers and no labels use case * Update some style in collapse component and fix policy path in modal * Update modal styles * Update styles * Update collapse header styling * Normalize tree nodes should happen before findMatchingRoutes call * Fix findMatchingRoutes and findMatchingAlertGroups methods after reabasing * Move instances matching to the web worker code * Fix config fetching for vanilla prometheus AMs * Add tests * Add tests mocks * Fix tests after adding web worker * Display matching labels for each matching alert instance * Add minor css improvements * Revert changes added in Collapse component as we don't use it anymore * Move the route details modal to a separate file * Move NotificationRoute and preview hook into separate files * Fix Alertmanager preview tests * Fix tests * Move matcher code to a separate file, improve matcher mock * Add permissions control for contact point edit view link * Fix from and to for the temporal use of backtesting api * Fix tests, add lazy loading of the preview component Co-authored-by: Sonia Aguilar <soniaaguilarpeiron@gmail.com> * Fix preview test * Add onclick on the header div so it collapse and expands when clicking on it, and update styles to be consistent with the rest of tables * Adapt the code to the new rule testing endpoint definition * Fix tests * small changes after reviewing the final code * compute entire inherited tree before computing the routes map * Throw error in case of not having receiver in routesByIdMap and add test for the use case of inheriting receiver from parent to check UI throws no errors * Add list of labels in the policy route path that produces the policy matchers to match potential instances * Use color determined by the key, in label tags when hovering matchers in the policy tree * Remove labels in modal and handle empty string as receiver to inherit from parent as we do with undefined * Revert "Add list of labels in the policy route path that produces the policy matchers to match potential instances" This reverts commit ee73ae9cf9caf10c33bcd58973279f23437a9146. * fix inheritance for computeInheritedTree * Fix message shown when preview has not been executed yet * First round for adressing PR review comments * Adress the rest of PR review commments * Update texts and rename id prop in NotificaitonStep to alertUid --------- Co-authored-by: Konrad Lalik <konrad.lalik@grafana.com> Co-authored-by: Gilles De Mey <gilles.de.mey@gmail.com>
2023-06-19 19:32:17 +08:00
uid: '',
labels: [{ key: '', value: '' }],
annotations: defaultAnnotations,
dataSourceName: null,
type: canCreateGrafanaRules ? RuleFormType.grafana : canCreateCloudRules ? RuleFormType.cloudAlerting : undefined, // viewers can't create prom alerts
group: '',
// grafana
folder: null,
queries: [],
recordingRulesQueries: [],
condition: '',
noDataState: GrafanaAlertStateDecision.NoData,
execErrState: GrafanaAlertStateDecision.Error,
evaluateFor: '5m',
evaluateEvery: MINUTE,
manualRouting: false, // let's decide this later
contactPoints: {},
overrideGrouping: false,
overrideTimings: false,
muteTimeIntervals: [],
// cortex / loki
namespace: '',
expression: '',
forTime: 1,
forTimeUnit: 'm',
});
};
2021-04-14 20:57:36 +08:00
export function formValuesToRulerRuleDTO(values: RuleFormValues): RulerRuleDTO {
const { name, expression, forTime, forTimeUnit, keepFiringForTime, keepFiringForTimeUnit, type } = values;
if (type === RuleFormType.cloudAlerting) {
let keepFiringFor: string | undefined;
if (keepFiringForTime && keepFiringForTimeUnit) {
keepFiringFor = `${keepFiringForTime}${keepFiringForTimeUnit}`;
}
return {
alert: name,
for: `${forTime}${forTimeUnit}`,
keep_firing_for: keepFiringFor,
annotations: arrayToRecord(values.annotations || []),
labels: arrayToRecord(values.labels || []),
expr: expression,
};
} else if (type === RuleFormType.cloudRecording) {
return {
record: name,
labels: arrayToRecord(values.labels || []),
expr: expression,
};
}
throw new Error(`unexpected rule type: ${type}`);
2021-04-14 20:57:36 +08:00
}
export function listifyLabelsOrAnnotations(
item: Labels | Annotations | undefined,
addEmpty: boolean
): Array<{ key: string; value: string }> {
const list = [...recordToArray(item || {})];
if (addEmpty) {
list.push({ key: '', value: '' });
}
return list;
}
//make sure default annotations are always shown in order even if empty
export function normalizeDefaultAnnotations(annotations: Array<{ key: string; value: string }>) {
const orderedAnnotations = [...annotations];
const defaultAnnotationKeys = defaultAnnotations.map((annotation) => annotation.key);
defaultAnnotationKeys.forEach((defaultAnnotationKey, index) => {
const fieldIndex = orderedAnnotations.findIndex((field) => field.key === defaultAnnotationKey);
if (fieldIndex === -1) {
//add the default annotation if abstent
const emptyValue = { key: defaultAnnotationKey, value: '' };
orderedAnnotations.splice(index, 0, emptyValue);
} else if (fieldIndex !== index) {
//move it to the correct position if present
orderedAnnotations.splice(index, 0, orderedAnnotations.splice(fieldIndex, 1)[0]);
}
});
return orderedAnnotations;
}
export function getNotificationSettingsForDTO(
manualRouting: boolean,
contactPoints?: AlertManagerManualRouting
): GrafanaNotificationSettings | undefined {
if (contactPoints?.grafana?.selectedContactPoint && manualRouting) {
return {
receiver: contactPoints?.grafana?.selectedContactPoint,
mute_timings: contactPoints?.grafana?.muteTimeIntervals,
group_by: contactPoints?.grafana?.overrideGrouping ? contactPoints?.grafana?.groupBy : undefined,
group_wait:
contactPoints?.grafana?.overrideTimings && contactPoints?.grafana?.groupWaitValue
? contactPoints?.grafana?.groupWaitValue
: undefined,
group_interval:
contactPoints?.grafana?.overrideTimings && contactPoints?.grafana?.groupIntervalValue
? contactPoints?.grafana?.groupIntervalValue
: undefined,
repeat_interval:
contactPoints?.grafana?.overrideTimings && contactPoints?.grafana?.repeatIntervalValue
? contactPoints?.grafana?.repeatIntervalValue
: undefined,
};
}
return undefined;
}
export function formValuesToRulerGrafanaRuleDTO(values: RuleFormValues): PostableRuleGrafanaRuleDTO {
const { name, condition, noDataState, execErrState, evaluateFor, queries, isPaused, contactPoints, manualRouting } =
values;
2021-04-14 20:57:36 +08:00
if (condition) {
const notificationSettings: GrafanaNotificationSettings | undefined = getNotificationSettingsForDTO(
manualRouting,
contactPoints
);
2021-04-14 20:57:36 +08:00
return {
grafana_alert: {
title: name,
condition,
no_data_state: noDataState,
exec_err_state: execErrState,
data: queries.map(fixBothInstantAndRangeQuery),
is_paused: Boolean(isPaused),
notification_settings: notificationSettings,
2021-04-14 20:57:36 +08:00
},
2021-04-19 17:53:02 +08:00
for: evaluateFor,
annotations: arrayToRecord(values.annotations || []),
labels: arrayToRecord(values.labels || []),
2021-04-14 20:57:36 +08:00
};
}
throw new Error('Cannot create rule without specifying alert condition');
}
export function getContactPointsFromDTO(ga: GrafanaRuleDefinition): AlertManagerManualRouting | undefined {
const contactPoint: ContactPoint | undefined = ga.notification_settings
? {
selectedContactPoint: ga.notification_settings.receiver,
muteTimeIntervals: ga.notification_settings.mute_timings ?? [],
overrideGrouping:
Array.isArray(ga.notification_settings.group_by) && ga.notification_settings.group_by.length > 0,
overrideTimings: [
ga.notification_settings.group_wait,
ga.notification_settings.group_interval,
ga.notification_settings.repeat_interval,
].some(Boolean),
groupBy: ga.notification_settings.group_by || [],
groupWaitValue: ga.notification_settings.group_wait || '',
groupIntervalValue: ga.notification_settings.group_interval || '',
repeatIntervalValue: ga.notification_settings.repeat_interval || '',
}
: undefined;
const routingSettings: AlertManagerManualRouting | undefined = contactPoint
? {
[GRAFANA_RULES_SOURCE_NAME]: contactPoint,
}
: undefined;
return routingSettings;
}
export function rulerRuleToFormValues(ruleWithLocation: RuleWithLocation): RuleFormValues {
const { ruleSourceName, namespace, group, rule } = ruleWithLocation;
const defaultFormValues = getDefaultFormValues();
if (isGrafanaRulesSource(ruleSourceName)) {
if (isGrafanaRulerRule(rule)) {
const ga = rule.grafana_alert;
const routingSettings: AlertManagerManualRouting | undefined = getContactPointsFromDTO(ga);
return {
...defaultFormValues,
name: ga.title,
type: RuleFormType.grafana,
group: group.name,
evaluateEvery: group.interval || defaultFormValues.evaluateEvery,
evaluateFor: rule.for || '0',
noDataState: ga.no_data_state,
execErrState: ga.exec_err_state,
queries: ga.data,
condition: ga.condition,
annotations: normalizeDefaultAnnotations(listifyLabelsOrAnnotations(rule.annotations, false)),
labels: listifyLabelsOrAnnotations(rule.labels, true),
Alerting: Add notification policies preview in alert creation (#68839) * Add notification policies preview in alert rule form Co-authored-by: Konrad Lalik <konrad.lalik@grafana.com> * Refactor using new useGetPotentialInstances hook and apply some style changes * Add notification policy detail modal * Use backtesting api for simulating potential alert instances * Fix logic to travserse all the children from the root route * Split notification preview by alert manager * Add instance count to matching policy header and fix some styles * Move some logic to a new hook useGetAlertManagersSourceNames to make the code more clean * Fix some tests * Add initial test for NotificationPreview * Use button to preview potential instances * Add link to contact point details * Add route matching result details * Show AlertManager image in the routing preview list * Add tests setup, add single AM preview test * Handle no matchers and no labels use case * Update some style in collapse component and fix policy path in modal * Update modal styles * Update styles * Update collapse header styling * Normalize tree nodes should happen before findMatchingRoutes call * Fix findMatchingRoutes and findMatchingAlertGroups methods after reabasing * Move instances matching to the web worker code * Fix config fetching for vanilla prometheus AMs * Add tests * Add tests mocks * Fix tests after adding web worker * Display matching labels for each matching alert instance * Add minor css improvements * Revert changes added in Collapse component as we don't use it anymore * Move the route details modal to a separate file * Move NotificationRoute and preview hook into separate files * Fix Alertmanager preview tests * Fix tests * Move matcher code to a separate file, improve matcher mock * Add permissions control for contact point edit view link * Fix from and to for the temporal use of backtesting api * Fix tests, add lazy loading of the preview component Co-authored-by: Sonia Aguilar <soniaaguilarpeiron@gmail.com> * Fix preview test * Add onclick on the header div so it collapse and expands when clicking on it, and update styles to be consistent with the rest of tables * Adapt the code to the new rule testing endpoint definition * Fix tests * small changes after reviewing the final code * compute entire inherited tree before computing the routes map * Throw error in case of not having receiver in routesByIdMap and add test for the use case of inheriting receiver from parent to check UI throws no errors * Add list of labels in the policy route path that produces the policy matchers to match potential instances * Use color determined by the key, in label tags when hovering matchers in the policy tree * Remove labels in modal and handle empty string as receiver to inherit from parent as we do with undefined * Revert "Add list of labels in the policy route path that produces the policy matchers to match potential instances" This reverts commit ee73ae9cf9caf10c33bcd58973279f23437a9146. * fix inheritance for computeInheritedTree * Fix message shown when preview has not been executed yet * First round for adressing PR review comments * Adress the rest of PR review commments * Update texts and rename id prop in NotificaitonStep to alertUid --------- Co-authored-by: Konrad Lalik <konrad.lalik@grafana.com> Co-authored-by: Gilles De Mey <gilles.de.mey@gmail.com>
2023-06-19 19:32:17 +08:00
folder: { title: namespace, uid: ga.namespace_uid },
isPaused: ga.is_paused,
contactPoints: routingSettings,
manualRouting: Boolean(routingSettings),
// next line is for testing
// manualRouting: true,
// contactPoints: {
// grafana: {
// selectedContactPoint: "contact_point_5",
// muteTimeIntervals: [
// "mute timing 1"
// ],
// overrideGrouping: true,
// overrideTimings: true,
// "groupBy": [
// "..."
// ],
// groupWaitValue: "35s",
// groupIntervalValue: "6m",
// repeatIntervalValue: "5h"
// }
// }
};
} else {
throw new Error('Unexpected type of rule for grafana rules source');
}
} else {
if (isAlertingRulerRule(rule)) {
const datasourceUid = getDataSourceSrv().getInstanceSettings(ruleSourceName)?.uid ?? '';
const defaultQuery = {
refId: 'A',
datasourceUid,
queryType: '',
relativeTimeRange: getDefaultRelativeTimeRange(),
expr: rule.expr,
model: {
refId: 'A',
hide: false,
expr: rule.expr,
},
};
const alertingRuleValues = alertingRulerRuleToRuleForm(rule);
return {
...defaultFormValues,
...alertingRuleValues,
queries: [defaultQuery],
annotations: normalizeDefaultAnnotations(listifyLabelsOrAnnotations(rule.annotations, false)),
type: RuleFormType.cloudAlerting,
dataSourceName: ruleSourceName,
namespace,
group: group.name,
};
} else if (isRecordingRulerRule(rule)) {
const recordingRuleValues = recordingRulerRuleToRuleForm(rule);
return {
...defaultFormValues,
...recordingRuleValues,
type: RuleFormType.cloudRecording,
dataSourceName: ruleSourceName,
namespace,
group: group.name,
};
} else {
throw new Error('Unexpected type of rule for cloud rules source');
}
}
}
export function alertingRulerRuleToRuleForm(
rule: RulerAlertingRuleDTO
): Pick<
RuleFormValues,
| 'name'
| 'forTime'
| 'forTimeUnit'
| 'keepFiringForTime'
| 'keepFiringForTimeUnit'
| 'expression'
| 'annotations'
| 'labels'
> {
const defaultFormValues = getDefaultFormValues();
const [forTime, forTimeUnit] = rule.for
? parseInterval(rule.for)
: [defaultFormValues.forTime, defaultFormValues.forTimeUnit];
const [keepFiringForTime, keepFiringForTimeUnit] = rule.keep_firing_for
? parseInterval(rule.keep_firing_for)
: [defaultFormValues.keepFiringForTime, defaultFormValues.keepFiringForTimeUnit];
return {
name: rule.alert,
expression: rule.expr,
forTime,
forTimeUnit,
keepFiringForTime,
keepFiringForTimeUnit,
annotations: listifyLabelsOrAnnotations(rule.annotations, false),
labels: listifyLabelsOrAnnotations(rule.labels, true),
};
}
export function recordingRulerRuleToRuleForm(
rule: RulerRecordingRuleDTO
): Pick<RuleFormValues, 'name' | 'expression' | 'labels'> {
return {
name: rule.record,
expression: rule.expr,
labels: listifyLabelsOrAnnotations(rule.labels, true),
};
}
export const getDefaultQueries = (): AlertQuery[] => {
const dataSource = getDefaultOrFirstCompatibleDataSource();
if (!dataSource) {
return [...getDefaultExpressions('A', 'B')];
}
const relativeTimeRange = getDefaultRelativeTimeRange();
return [
{
refId: 'A',
datasourceUid: dataSource.uid,
queryType: '',
relativeTimeRange,
model: {
refId: 'A',
},
},
...getDefaultExpressions('B', 'C'),
];
};
export const getDefaultRecordingRulesQueries = (
rulesSourcesWithRuler: Array<DataSourceInstanceSettings<DataSourceJsonData>>
): AlertQuery[] => {
const relativeTimeRange = getDefaultRelativeTimeRange();
return [
{
refId: 'A',
datasourceUid: rulesSourcesWithRuler[0]?.uid || '',
queryType: '',
relativeTimeRange,
model: {
refId: 'A',
},
},
];
};
const getDefaultExpressions = (...refIds: [string, string]): AlertQuery[] => {
const refOne = refIds[0];
const refTwo = refIds[1];
const reduceExpression: ExpressionQuery = {
refId: refIds[0],
type: ExpressionQueryType.reduce,
datasource: {
uid: ExpressionDatasourceUID,
type: ExpressionDatasourceRef.type,
},
conditions: [
{
type: 'query',
evaluator: {
params: [],
type: EvalFunction.IsAbove,
},
operator: {
type: 'and',
},
query: {
params: [refOne],
},
reducer: {
params: [],
type: 'last',
},
},
],
reducer: 'last',
expression: 'A',
};
const thresholdExpression: ExpressionQuery = {
refId: refTwo,
type: ExpressionQueryType.threshold,
datasource: {
uid: ExpressionDatasourceUID,
type: ExpressionDatasourceRef.type,
},
conditions: [
{
type: 'query',
evaluator: {
params: [0],
type: EvalFunction.IsAbove,
},
operator: {
type: 'and',
},
query: {
params: [refTwo],
},
reducer: {
params: [],
type: 'last',
},
},
],
expression: refOne,
};
return [
{
refId: refOne,
datasourceUid: ExpressionDatasourceUID,
queryType: '',
model: reduceExpression,
},
{
refId: refTwo,
datasourceUid: ExpressionDatasourceUID,
queryType: '',
model: thresholdExpression,
},
];
};
const dataQueriesToGrafanaQueries = async (
queries: DataQuery[],
relativeTimeRange: RelativeTimeRange,
scopedVars: ScopedVars | {},
panelDataSourceRef?: DataSourceRef,
maxDataPoints?: number,
minInterval?: string
): Promise<AlertQuery[]> => {
const result: AlertQuery[] = [];
for (const target of queries) {
const datasource = await getDataSourceSrv().get(target.datasource?.uid ? target.datasource : panelDataSourceRef);
const dsRef = { uid: datasource.uid, type: datasource.type };
const range = rangeUtil.relativeToTimeRange(relativeTimeRange);
const { interval, intervalMs } = getIntervals(range, minInterval ?? datasource.interval, maxDataPoints);
const queryVariables = {
__interval: { text: interval, value: interval },
__interval_ms: { text: intervalMs, value: intervalMs },
...scopedVars,
};
const interpolatedTarget = datasource.interpolateVariablesInQueries
? await datasource.interpolateVariablesInQueries([target], queryVariables)[0]
: target;
// expressions
if (dsRef.uid === ExpressionDatasourceUID) {
const newQuery: AlertQuery = {
refId: interpolatedTarget.refId,
queryType: '',
relativeTimeRange,
datasourceUid: ExpressionDatasourceUID,
model: interpolatedTarget,
};
result.push(newQuery);
// queries
} else {
const datasourceSettings = getDataSourceSrv().getInstanceSettings(dsRef);
if (datasourceSettings && datasourceSettings.meta.alerting) {
const newQuery: AlertQuery = {
refId: interpolatedTarget.refId,
queryType: interpolatedTarget.queryType ?? '',
relativeTimeRange,
datasourceUid: datasourceSettings.uid,
model: {
...interpolatedTarget,
maxDataPoints,
intervalMs,
},
};
result.push(newQuery);
}
}
}
return result;
};
export const panelToRuleFormValues = async (
panel: PanelModel,
dashboard: DashboardModel
): Promise<Partial<RuleFormValues> | undefined> => {
const { targets } = panel;
if (!panel.id || !dashboard.uid) {
return undefined;
}
const relativeTimeRange = rangeUtil.timeRangeToRelative(rangeUtil.convertRawToRange(dashboard.time));
const queries = await dataQueriesToGrafanaQueries(
targets,
relativeTimeRange,
panel.scopedVars || {},
panel.datasource ?? undefined,
panel.maxDataPoints ?? undefined,
panel.interval ?? undefined
);
// if no alerting capable queries are found, can't create a rule
if (!queries.length || !queries.find((query) => query.datasourceUid !== ExpressionDatasourceUID)) {
return undefined;
}
if (!queries.find((query) => query.datasourceUid === ExpressionDatasourceUID)) {
const [reduceExpression, _thresholdExpression] = getDefaultExpressions(getNextRefIdChar(queries), '-');
queries.push(reduceExpression);
const [_reduceExpression, thresholdExpression] = getDefaultExpressions(
reduceExpression.refId,
getNextRefIdChar(queries)
);
queries.push(thresholdExpression);
}
Alerting: Add notification policies preview in alert creation (#68839) * Add notification policies preview in alert rule form Co-authored-by: Konrad Lalik <konrad.lalik@grafana.com> * Refactor using new useGetPotentialInstances hook and apply some style changes * Add notification policy detail modal * Use backtesting api for simulating potential alert instances * Fix logic to travserse all the children from the root route * Split notification preview by alert manager * Add instance count to matching policy header and fix some styles * Move some logic to a new hook useGetAlertManagersSourceNames to make the code more clean * Fix some tests * Add initial test for NotificationPreview * Use button to preview potential instances * Add link to contact point details * Add route matching result details * Show AlertManager image in the routing preview list * Add tests setup, add single AM preview test * Handle no matchers and no labels use case * Update some style in collapse component and fix policy path in modal * Update modal styles * Update styles * Update collapse header styling * Normalize tree nodes should happen before findMatchingRoutes call * Fix findMatchingRoutes and findMatchingAlertGroups methods after reabasing * Move instances matching to the web worker code * Fix config fetching for vanilla prometheus AMs * Add tests * Add tests mocks * Fix tests after adding web worker * Display matching labels for each matching alert instance * Add minor css improvements * Revert changes added in Collapse component as we don't use it anymore * Move the route details modal to a separate file * Move NotificationRoute and preview hook into separate files * Fix Alertmanager preview tests * Fix tests * Move matcher code to a separate file, improve matcher mock * Add permissions control for contact point edit view link * Fix from and to for the temporal use of backtesting api * Fix tests, add lazy loading of the preview component Co-authored-by: Sonia Aguilar <soniaaguilarpeiron@gmail.com> * Fix preview test * Add onclick on the header div so it collapse and expands when clicking on it, and update styles to be consistent with the rest of tables * Adapt the code to the new rule testing endpoint definition * Fix tests * small changes after reviewing the final code * compute entire inherited tree before computing the routes map * Throw error in case of not having receiver in routesByIdMap and add test for the use case of inheriting receiver from parent to check UI throws no errors * Add list of labels in the policy route path that produces the policy matchers to match potential instances * Use color determined by the key, in label tags when hovering matchers in the policy tree * Remove labels in modal and handle empty string as receiver to inherit from parent as we do with undefined * Revert "Add list of labels in the policy route path that produces the policy matchers to match potential instances" This reverts commit ee73ae9cf9caf10c33bcd58973279f23437a9146. * fix inheritance for computeInheritedTree * Fix message shown when preview has not been executed yet * First round for adressing PR review comments * Adress the rest of PR review commments * Update texts and rename id prop in NotificaitonStep to alertUid --------- Co-authored-by: Konrad Lalik <konrad.lalik@grafana.com> Co-authored-by: Gilles De Mey <gilles.de.mey@gmail.com>
2023-06-19 19:32:17 +08:00
const { folderTitle, folderUid } = dashboard.meta;
const formValues = {
type: RuleFormType.grafana,
folder:
Alerting: Add notification policies preview in alert creation (#68839) * Add notification policies preview in alert rule form Co-authored-by: Konrad Lalik <konrad.lalik@grafana.com> * Refactor using new useGetPotentialInstances hook and apply some style changes * Add notification policy detail modal * Use backtesting api for simulating potential alert instances * Fix logic to travserse all the children from the root route * Split notification preview by alert manager * Add instance count to matching policy header and fix some styles * Move some logic to a new hook useGetAlertManagersSourceNames to make the code more clean * Fix some tests * Add initial test for NotificationPreview * Use button to preview potential instances * Add link to contact point details * Add route matching result details * Show AlertManager image in the routing preview list * Add tests setup, add single AM preview test * Handle no matchers and no labels use case * Update some style in collapse component and fix policy path in modal * Update modal styles * Update styles * Update collapse header styling * Normalize tree nodes should happen before findMatchingRoutes call * Fix findMatchingRoutes and findMatchingAlertGroups methods after reabasing * Move instances matching to the web worker code * Fix config fetching for vanilla prometheus AMs * Add tests * Add tests mocks * Fix tests after adding web worker * Display matching labels for each matching alert instance * Add minor css improvements * Revert changes added in Collapse component as we don't use it anymore * Move the route details modal to a separate file * Move NotificationRoute and preview hook into separate files * Fix Alertmanager preview tests * Fix tests * Move matcher code to a separate file, improve matcher mock * Add permissions control for contact point edit view link * Fix from and to for the temporal use of backtesting api * Fix tests, add lazy loading of the preview component Co-authored-by: Sonia Aguilar <soniaaguilarpeiron@gmail.com> * Fix preview test * Add onclick on the header div so it collapse and expands when clicking on it, and update styles to be consistent with the rest of tables * Adapt the code to the new rule testing endpoint definition * Fix tests * small changes after reviewing the final code * compute entire inherited tree before computing the routes map * Throw error in case of not having receiver in routesByIdMap and add test for the use case of inheriting receiver from parent to check UI throws no errors * Add list of labels in the policy route path that produces the policy matchers to match potential instances * Use color determined by the key, in label tags when hovering matchers in the policy tree * Remove labels in modal and handle empty string as receiver to inherit from parent as we do with undefined * Revert "Add list of labels in the policy route path that produces the policy matchers to match potential instances" This reverts commit ee73ae9cf9caf10c33bcd58973279f23437a9146. * fix inheritance for computeInheritedTree * Fix message shown when preview has not been executed yet * First round for adressing PR review comments * Adress the rest of PR review commments * Update texts and rename id prop in NotificaitonStep to alertUid --------- Co-authored-by: Konrad Lalik <konrad.lalik@grafana.com> Co-authored-by: Gilles De Mey <gilles.de.mey@gmail.com>
2023-06-19 19:32:17 +08:00
folderUid && folderTitle
? {
Alerting: Add notification policies preview in alert creation (#68839) * Add notification policies preview in alert rule form Co-authored-by: Konrad Lalik <konrad.lalik@grafana.com> * Refactor using new useGetPotentialInstances hook and apply some style changes * Add notification policy detail modal * Use backtesting api for simulating potential alert instances * Fix logic to travserse all the children from the root route * Split notification preview by alert manager * Add instance count to matching policy header and fix some styles * Move some logic to a new hook useGetAlertManagersSourceNames to make the code more clean * Fix some tests * Add initial test for NotificationPreview * Use button to preview potential instances * Add link to contact point details * Add route matching result details * Show AlertManager image in the routing preview list * Add tests setup, add single AM preview test * Handle no matchers and no labels use case * Update some style in collapse component and fix policy path in modal * Update modal styles * Update styles * Update collapse header styling * Normalize tree nodes should happen before findMatchingRoutes call * Fix findMatchingRoutes and findMatchingAlertGroups methods after reabasing * Move instances matching to the web worker code * Fix config fetching for vanilla prometheus AMs * Add tests * Add tests mocks * Fix tests after adding web worker * Display matching labels for each matching alert instance * Add minor css improvements * Revert changes added in Collapse component as we don't use it anymore * Move the route details modal to a separate file * Move NotificationRoute and preview hook into separate files * Fix Alertmanager preview tests * Fix tests * Move matcher code to a separate file, improve matcher mock * Add permissions control for contact point edit view link * Fix from and to for the temporal use of backtesting api * Fix tests, add lazy loading of the preview component Co-authored-by: Sonia Aguilar <soniaaguilarpeiron@gmail.com> * Fix preview test * Add onclick on the header div so it collapse and expands when clicking on it, and update styles to be consistent with the rest of tables * Adapt the code to the new rule testing endpoint definition * Fix tests * small changes after reviewing the final code * compute entire inherited tree before computing the routes map * Throw error in case of not having receiver in routesByIdMap and add test for the use case of inheriting receiver from parent to check UI throws no errors * Add list of labels in the policy route path that produces the policy matchers to match potential instances * Use color determined by the key, in label tags when hovering matchers in the policy tree * Remove labels in modal and handle empty string as receiver to inherit from parent as we do with undefined * Revert "Add list of labels in the policy route path that produces the policy matchers to match potential instances" This reverts commit ee73ae9cf9caf10c33bcd58973279f23437a9146. * fix inheritance for computeInheritedTree * Fix message shown when preview has not been executed yet * First round for adressing PR review comments * Adress the rest of PR review commments * Update texts and rename id prop in NotificaitonStep to alertUid --------- Co-authored-by: Konrad Lalik <konrad.lalik@grafana.com> Co-authored-by: Gilles De Mey <gilles.de.mey@gmail.com>
2023-06-19 19:32:17 +08:00
uid: folderUid,
title: folderTitle,
}
: undefined,
queries,
name: panel.title,
condition: queries[queries.length - 1].refId,
annotations: [
{
key: Annotation.dashboardUID,
value: dashboard.uid,
},
{
key: Annotation.panelID,
value: String(panel.id),
},
],
};
return formValues;
};
export function getIntervals(range: TimeRange, lowLimit?: string, resolution?: number): IntervalValues {
if (!resolution) {
if (lowLimit && rangeUtil.intervalToMs(lowLimit) > 1000) {
return {
interval: lowLimit,
intervalMs: rangeUtil.intervalToMs(lowLimit),
};
}
return { interval: '1s', intervalMs: 1000 };
}
return rangeUtil.calculateInterval(range, resolution, lowLimit);
}
export function fixBothInstantAndRangeQuery(query: AlertQuery) {
const model = query.model;
if (!isPromQuery(model)) {
return query;
}
const isBothInstantAndRange = model.instant && model.range;
if (isBothInstantAndRange) {
return { ...query, model: { ...model, range: true, instant: false } };
}
return query;
}
function isPromQuery(model: AlertDataQuery): model is PromQuery {
return 'expr' in model && 'instant' in model && 'range' in model;
}
export function isPromOrLokiQuery(model: AlertDataQuery): model is PromOrLokiQuery {
return 'expr' in model;
}
// the backend will always execute "hidden" queries, so we have no choice but to remove the property in the front-end
// to avoid confusion. The query editor shows them as "disabled" and that's a different semantic meaning.
// furthermore the "AlertingQueryRunner" calls `filterQuery` on each data source and those will skip running queries that are "hidden"."
// It seems like we have no choice but to act like "hidden" queries don't exist in alerting.
export const ignoreHiddenQueries = (ruleDefinition: RuleFormValues): RuleFormValues => {
return {
...ruleDefinition,
queries: ruleDefinition.queries?.map((query) => omit(query, 'model.hide')),
};
};
export function formValuesFromExistingRule(rule: RuleWithLocation<RulerRuleDTO>) {
return ignoreHiddenQueries(rulerRuleToFormValues(rule));
}