mirror of https://github.com/grafana/grafana.git
191 lines
7.5 KiB
TypeScript
191 lines
7.5 KiB
TypeScript
import { t } from 'app/core/internationalization';
|
||
import { RuleGroupIdentifier } from 'app/types/unified-alerting';
|
||
|
||
import { alertRuleApi } from '../../api/alertRuleApi';
|
||
import { notFoundToNullOrThrow } from '../../api/util';
|
||
import {
|
||
updateRuleGroupAction,
|
||
moveRuleGroupAction,
|
||
renameRuleGroupAction,
|
||
reorderRulesInRuleGroupAction,
|
||
} from '../../reducers/ruler/ruleGroups';
|
||
import { isGrafanaRulesSource } from '../../utils/datasource';
|
||
import { useAsync } from '../useAsync';
|
||
|
||
import { useProduceNewRuleGroup } from './useProduceNewRuleGroup';
|
||
|
||
const ruleUpdateSuccessMessage = () => t('alerting.rule-groups.update.success', 'Successfully updated rule group');
|
||
|
||
/**
|
||
* Update an existing rule group, currently only supports updating the interval.
|
||
* Use "useRenameRuleGroup" or "useMoveRuleGroup" for updating the namespace or group name.
|
||
*/
|
||
export function useUpdateRuleGroupConfiguration() {
|
||
const [produceNewRuleGroup] = useProduceNewRuleGroup();
|
||
const [upsertRuleGroup] = alertRuleApi.endpoints.upsertRuleGroupForNamespace.useMutation();
|
||
|
||
return useAsync(async (ruleGroup: RuleGroupIdentifier, interval: string) => {
|
||
const { namespaceName } = ruleGroup;
|
||
|
||
const action = updateRuleGroupAction({ interval });
|
||
const { newRuleGroupDefinition, rulerConfig } = await produceNewRuleGroup(ruleGroup, action);
|
||
|
||
return upsertRuleGroup({
|
||
rulerConfig,
|
||
namespace: namespaceName,
|
||
payload: newRuleGroupDefinition,
|
||
notificationOptions: { successMessage: ruleUpdateSuccessMessage() },
|
||
}).unwrap();
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Move a rule group to either another namespace with (optionally) a different name, throws if the action
|
||
* targets an existing rule group.
|
||
* Optionally, update the rule group evaluation interval.
|
||
*/
|
||
export function useMoveRuleGroup() {
|
||
const [produceNewRuleGroup] = useProduceNewRuleGroup();
|
||
const [fetchRuleGroup] = alertRuleApi.endpoints.getRuleGroupForNamespace.useLazyQuery();
|
||
const [upsertRuleGroup] = alertRuleApi.endpoints.upsertRuleGroupForNamespace.useMutation();
|
||
const [deleteRuleGroup] = alertRuleApi.endpoints.deleteRuleGroupFromNamespace.useMutation();
|
||
|
||
// @TODO maybe add where we moved it from and to for additional peace of mind
|
||
const successMessage = t('alerting.rule-groups.move.success', 'Successfully moved rule group');
|
||
|
||
return useAsync(
|
||
async (ruleGroup: RuleGroupIdentifier, namespaceName: string, groupName?: string, interval?: string) => {
|
||
// we could technically support moving rule groups to another folder, though we don't have a "move" wizard yet.
|
||
if (isGrafanaRulesSource(ruleGroup.dataSourceName)) {
|
||
throw new Error('Moving a Grafana-managed rule group to another folder is currently not supported.');
|
||
}
|
||
|
||
const action = moveRuleGroupAction({ newNamespaceName: namespaceName, groupName, interval });
|
||
const { newRuleGroupDefinition, rulerConfig } = await produceNewRuleGroup(ruleGroup, action);
|
||
|
||
const oldNamespace = ruleGroup.namespaceName;
|
||
const targetNamespace = action.payload.newNamespaceName;
|
||
|
||
const oldGroupName = ruleGroup.groupName;
|
||
const targetGroupName = action.payload.groupName;
|
||
|
||
const isGroupRenamed = Boolean(targetGroupName) && oldGroupName !== targetGroupName;
|
||
|
||
// if we're also renaming the group, check if the target does not already exist
|
||
if (targetGroupName && isGroupRenamed) {
|
||
const targetGroup = await fetchRuleGroup({
|
||
rulerConfig,
|
||
namespace: targetNamespace,
|
||
group: targetGroupName,
|
||
// since this could throw 404
|
||
notificationOptions: { showErrorAlert: false },
|
||
})
|
||
.unwrap()
|
||
.catch(notFoundToNullOrThrow);
|
||
|
||
if (targetGroup?.rules?.length) {
|
||
throw new Error('Target group already has rules, merging rule groups is currently not supported.');
|
||
}
|
||
}
|
||
|
||
// create the new group in the target namespace
|
||
// ⚠️ it's important to do this before we remove the old group – better to have two groups than none if one of these requests fails
|
||
await upsertRuleGroup({
|
||
rulerConfig,
|
||
namespace: targetNamespace,
|
||
payload: newRuleGroupDefinition,
|
||
notificationOptions: { successMessage },
|
||
}).unwrap();
|
||
|
||
// now remove the old one
|
||
const result = await deleteRuleGroup({
|
||
rulerConfig,
|
||
namespace: oldNamespace,
|
||
group: oldGroupName,
|
||
notificationOptions: { showSuccessAlert: false },
|
||
}).unwrap();
|
||
|
||
return result;
|
||
}
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Rename a rule group but keep it within the same namespace, throws if the action targets an existing rule group.
|
||
* Optionally, update the rule group evaluation interval.
|
||
*/
|
||
export function useRenameRuleGroup() {
|
||
const [produceNewRuleGroup] = useProduceNewRuleGroup();
|
||
const [fetchRuleGroup] = alertRuleApi.endpoints.getRuleGroupForNamespace.useLazyQuery();
|
||
const [upsertRuleGroup] = alertRuleApi.endpoints.upsertRuleGroupForNamespace.useMutation();
|
||
const [deleteRuleGroup] = alertRuleApi.endpoints.deleteRuleGroupFromNamespace.useMutation();
|
||
|
||
return useAsync(async (ruleGroup: RuleGroupIdentifier, groupName: string, interval?: string) => {
|
||
const action = renameRuleGroupAction({ groupName, interval });
|
||
const { newRuleGroupDefinition, rulerConfig } = await produceNewRuleGroup(ruleGroup, action);
|
||
|
||
const oldGroupName = ruleGroup.groupName;
|
||
const newGroupName = action.payload.groupName;
|
||
const namespaceName = ruleGroup.namespaceName;
|
||
|
||
const successMessage = t('alerting.rule-groups.rename.success', 'Successfully renamed rule group');
|
||
|
||
// check if the target group exists
|
||
const targetGroup = await fetchRuleGroup({
|
||
rulerConfig,
|
||
namespace: namespaceName,
|
||
group: newGroupName,
|
||
// since this could throw 404
|
||
notificationOptions: { showErrorAlert: false },
|
||
})
|
||
.unwrap()
|
||
.catch(notFoundToNullOrThrow);
|
||
|
||
if (targetGroup?.rules?.length) {
|
||
throw new Error('Target group has existing rules, merging rule groups is currently not supported.');
|
||
}
|
||
|
||
// if the target group does not exist, create the new group
|
||
// ⚠️ it's important to do this before we remove the old group – better to have two groups than none if one of these requests fails
|
||
const result = await upsertRuleGroup({
|
||
rulerConfig,
|
||
namespace: namespaceName,
|
||
payload: newRuleGroupDefinition,
|
||
notificationOptions: { successMessage },
|
||
}).unwrap();
|
||
|
||
// now delete the group we renamed
|
||
await deleteRuleGroup({
|
||
rulerConfig,
|
||
namespace: namespaceName,
|
||
group: oldGroupName,
|
||
notificationOptions: { showSuccessAlert: false },
|
||
}).unwrap();
|
||
|
||
return result;
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Reorder rules within an existing rule group. Pass in an array of swap operations Array<[oldIndex, newIndex]>.
|
||
* This prevents rules from accidentally being updated and only allows indices to be moved around.
|
||
*/
|
||
export function useReorderRuleForRuleGroup() {
|
||
const [produceNewRuleGroup] = useProduceNewRuleGroup();
|
||
const [upsertRuleGroup] = alertRuleApi.endpoints.upsertRuleGroupForNamespace.useMutation();
|
||
|
||
return useAsync(async (ruleGroup: RuleGroupIdentifier, swaps: Array<[number, number]>) => {
|
||
const { namespaceName } = ruleGroup;
|
||
|
||
const action = reorderRulesInRuleGroupAction({ swaps });
|
||
const { newRuleGroupDefinition, rulerConfig } = await produceNewRuleGroup(ruleGroup, action);
|
||
|
||
return upsertRuleGroup({
|
||
rulerConfig,
|
||
namespace: namespaceName,
|
||
payload: newRuleGroupDefinition,
|
||
notificationOptions: { successMessage: ruleUpdateSuccessMessage() },
|
||
}).unwrap();
|
||
});
|
||
}
|