142 lines
5.6 KiB
TypeScript
142 lines
5.6 KiB
TypeScript
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;
|
||
}
|
||
});
|
||
}
|