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

76 lines
3.2 KiB
TypeScript

import { Action } from '@reduxjs/toolkit';
import { RuleGroupIdentifier } from 'app/types/unified-alerting';
import { PostableRulerRuleGroupDTO } from 'app/types/unified-alerting-dto';
import { alertRuleApi } from '../../api/alertRuleApi';
import { featureDiscoveryApi } from '../../api/featureDiscoveryApi';
import { notFoundToNullOrThrow } from '../../api/util';
import { ruleGroupReducer } from '../../reducers/ruler/ruleGroups';
import { DEFAULT_GROUP_EVALUATION_INTERVAL } from '../../rule-editor/formDefaults';
const { useLazyGetRuleGroupForNamespaceQuery } = alertRuleApi;
const { useLazyDiscoverDsFeaturesQuery } = featureDiscoveryApi;
export const RulerNotSupportedError = (name: string) =>
new Error(`DataSource ${name} does not support ruler API or does not have the ruler API enabled.`);
/**
* Hook for reuse that handles freshly fetching a rule group's definition, applying an action to it,
* and then performing the API mutations necessary to persist the change.
*
* All rule groups changes should ideally be implemented as a wrapper around this hook,
* to ensure that we always protect as best we can against accidentally overwriting changes,
* and to guard against user concurrency issues.
*
* @throws
*/
export function useProduceNewRuleGroup() {
const [fetchRuleGroup, requestState] = useLazyGetRuleGroupForNamespaceQuery();
const [discoverDataSourceFeatures] = useLazyDiscoverDsFeaturesQuery();
/**
* This function will fetch the latest configuration we have for the rule group, apply a diff to it via a reducer and
* returns the result.
*
* The API does not allow operations on a single rule and will always overwrite the existing rule group with the payload.
*
* ┌─────────────────────────┐ ┌───────────────┐ ┌──────────────────┐
* │ fetch latest rule group │─▶│ apply reducer │─▶│ new rule group │
* └─────────────────────────┘ └───────────────┘ └──────────────────┘
*/
const produceNewRuleGroup = async (ruleGroup: RuleGroupIdentifier, action: Action) => {
const { dataSourceName, groupName, namespaceName } = ruleGroup;
const { rulerConfig } = await discoverDataSourceFeatures({ rulesSourceName: dataSourceName }).unwrap();
if (!rulerConfig) {
throw RulerNotSupportedError(dataSourceName);
}
const latestRuleGroupDefinition = await fetchRuleGroup({
rulerConfig,
namespace: namespaceName,
group: groupName,
// @TODO maybe only supress if 404?
notificationOptions: { showErrorAlert: false },
})
.unwrap()
.catch(notFoundToNullOrThrow);
const newRuleGroupDefinition = ruleGroupReducer(
latestRuleGroupDefinition ?? createBlankRuleGroup(ruleGroup.groupName),
action
);
return { newRuleGroupDefinition, rulerConfig };
};
return [produceNewRuleGroup, requestState] as const;
}
const createBlankRuleGroup = (name: string): PostableRulerRuleGroupDTO => ({
name,
interval: DEFAULT_GROUP_EVALUATION_INTERVAL,
rules: [],
});