2025-04-01 10:38:02 +09:00

142 lines
5.6 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { produce } from 'immer';
import { isEqual } from 'lodash';
import { t } from 'app/core/internationalization';
import { EditableRuleIdentifier, RuleGroupIdentifier } from 'app/types/unified-alerting';
import { PostableRuleDTO } from 'app/types/unified-alerting-dto';
import { alertRuleApi } from '../../api/alertRuleApi';
import { addRuleAction, updateRuleAction } from '../../reducers/ruler/ruleGroups';
import { isGrafanaRuleIdentifier, isGrafanaRulerRule } from '../../utils/rules';
import { useAsync } from '../useAsync';
import { useDeleteRuleFromGroup } from './useDeleteRuleFromGroup';
import { useProduceNewRuleGroup } from './useProduceNewRuleGroup';
/**
* This hook will add a single rule to a rule group a new rule group will be created if it does not already exist.
*/
export function useAddRuleToRuleGroup() {
const [produceNewRuleGroup] = useProduceNewRuleGroup();
const [upsertRuleGroup] = alertRuleApi.endpoints.upsertRuleGroupForNamespace.useMutation();
const successMessage = t('alerting.rules.add-rule.success', 'Rule added successfully');
return useAsync(async (ruleGroup: RuleGroupIdentifier, rule: PostableRuleDTO, interval?: string) => {
const { namespaceName } = ruleGroup;
// the new rule might have to be created in a new group, pass name and interval (optional) to the action
const action = addRuleAction({ rule, interval, groupName: ruleGroup.groupName });
const { newRuleGroupDefinition, rulerConfig } = await produceNewRuleGroup(ruleGroup, action);
const result = upsertRuleGroup({
rulerConfig,
namespace: namespaceName,
payload: newRuleGroupDefinition,
notificationOptions: { successMessage },
}).unwrap();
return result;
});
}
/**
* This hook will update an existing rule within a rule group, does not support moving the rule to another namespace / group
*/
export function useUpdateRuleInRuleGroup() {
const [produceNewRuleGroup] = useProduceNewRuleGroup();
const [moveRuleToGroup] = useMoveRuleToRuleGroup();
const [upsertRuleGroup] = alertRuleApi.endpoints.upsertRuleGroupForNamespace.useMutation();
const successMessage = t('alerting.rules.update-rule.success', 'Rule updated successfully');
return useAsync(
async (
ruleGroup: RuleGroupIdentifier,
ruleIdentifier: EditableRuleIdentifier,
ruleDefinition: PostableRuleDTO,
targetRuleGroup?: RuleGroupIdentifier,
interval?: string
) => {
const { namespaceName } = ruleGroup;
const finalRuleDefinition = copyGrafanaUID(ruleIdentifier, ruleDefinition);
// check if the existing rule and the form values have the same rule group identifier
const sameTargetRuleGroup = isEqual(ruleGroup, targetRuleGroup);
if (targetRuleGroup && !sameTargetRuleGroup) {
const result = moveRuleToGroup.execute(ruleGroup, targetRuleGroup, ruleIdentifier, ruleDefinition, interval);
return result;
}
const action = updateRuleAction({ identifier: ruleIdentifier, rule: finalRuleDefinition });
const { newRuleGroupDefinition, rulerConfig } = await produceNewRuleGroup(ruleGroup, action);
return upsertRuleGroup({
rulerConfig,
namespace: namespaceName,
payload: newRuleGroupDefinition,
notificationOptions: { successMessage },
}).unwrap();
}
);
}
/**
* This hook will move an existing rule to another namespace or group. The rule definition can also be modified.
* For Grafana-managed rules we can perform a single atomic move operation by copying the rule UID from the previous rule definition.
*/
export function useMoveRuleToRuleGroup() {
const [produceNewRuleGroup] = useProduceNewRuleGroup();
const [deleteRuleFromGroup] = useDeleteRuleFromGroup();
const [upsertRuleGroup] = alertRuleApi.endpoints.upsertRuleGroupForNamespace.useMutation();
const successMessage = t('alerting.rules.update-rule.success', 'Rule updated successfully');
return useAsync(
async (
currentRuleGroup: RuleGroupIdentifier,
targetRuleGroup: RuleGroupIdentifier,
ruleIdentifier: EditableRuleIdentifier,
ruleDefinition: PostableRuleDTO,
interval?: string
) => {
const finalRuleDefinition = copyGrafanaUID(ruleIdentifier, ruleDefinition);
// 1. add the rule to the new namespace / group / ruler target
const addRuleToGroup = addRuleAction({ rule: finalRuleDefinition, interval });
const { newRuleGroupDefinition: newTargetGroup, rulerConfig: targetGroupRulerConfig } = await produceNewRuleGroup(
targetRuleGroup,
addRuleToGroup
);
const result = await upsertRuleGroup({
rulerConfig: targetGroupRulerConfig,
namespace: targetRuleGroup.namespaceName,
payload: newTargetGroup,
notificationOptions: { successMessage },
}).unwrap();
// 2. if not Grafana-managed: remove the rule from the existing namespace / group / ruler
if (!isGrafanaRuleIdentifier(ruleIdentifier)) {
await deleteRuleFromGroup.execute(currentRuleGroup, ruleIdentifier);
}
return result;
}
);
}
function copyGrafanaUID(ruleIdentifier: EditableRuleIdentifier, ruleDefinition: PostableRuleDTO) {
const isGrafanaManagedRuleIdentifier = isGrafanaRuleIdentifier(ruleIdentifier);
// by copying over the rule UID the backend will perform an atomic move operation
// so there is no need for us to manually remove it from the previous group
return produce(ruleDefinition, (draft) => {
const isGrafanaManagedRuleDefinition = isGrafanaRulerRule(draft);
if (isGrafanaManagedRuleIdentifier && isGrafanaManagedRuleDefinition) {
draft.grafana_alert.uid = ruleIdentifier.uid;
}
});
}