diff --git a/public/app/features/alerting/unified/components/rules/RuleActionsButtons.tsx b/public/app/features/alerting/unified/components/rules/RuleActionsButtons.tsx index 426e7b0961d..7b643cbd415 100644 --- a/public/app/features/alerting/unified/components/rules/RuleActionsButtons.tsx +++ b/public/app/features/alerting/unified/components/rules/RuleActionsButtons.tsx @@ -17,19 +17,24 @@ import { Stack, } from '@grafana/ui'; import { useAppNotification } from 'app/core/copy/appNotification'; +import { LIMIT_ALERTS } from 'app/features/alerting/unified/RuleList'; +import { alertRuleApi } from 'app/features/alerting/unified/api/alertRuleApi'; +import { useRulesFilter } from 'app/features/alerting/unified/hooks/useFilteredRules'; import { useDispatch } from 'app/types'; import { CombinedRule, RuleIdentifier, RulesSource } from 'app/types/unified-alerting'; import { AlertRuleAction, useAlertRuleAbility } from '../../hooks/useAbilities'; -import { deleteRuleAction } from '../../state/actions'; +import { deleteRuleAction, fetchAllPromAndRulerRulesAction } from '../../state/actions'; import { getRulesSourceName } from '../../utils/datasource'; import { createShareLink, createViewLink } from '../../utils/misc'; import * as ruleId from '../../utils/rule-id'; -import { isGrafanaRulerRule } from '../../utils/rules'; +import { isGrafanaRulerRule, isGrafanaRulerRulePaused } from '../../utils/rules'; import { createUrl } from '../../utils/url'; import { RedirectToCloneRule } from './CloneRule'; +const { useUpdateRuleMutation } = alertRuleApi; + export const matchesWidth = (width: number) => window.matchMedia(`(max-width: ${width}px)`).matches; interface Props { @@ -42,6 +47,7 @@ export const RuleActionsButtons = ({ rule, rulesSource }: Props) => { const location = useLocation(); const notifyApp = useAppNotification(); const style = useStyles2(getStyles); + const [updateRule] = useUpdateRuleMutation(); const [redirectToClone, setRedirectToClone] = useState< { identifier: RuleIdentifier; isProvisioned: boolean } | undefined @@ -49,6 +55,7 @@ export const RuleActionsButtons = ({ rule, rulesSource }: Props) => { const { namespace, group, rulerRule } = rule; const [ruleToDelete, setRuleToDelete] = useState(); + const { hasActiveFilters } = useRulesFilter(); const returnTo = location.pathname + location.search; const isViewMode = inViewMode(location.pathname); @@ -68,6 +75,45 @@ export const RuleActionsButtons = ({ rule, rulesSource }: Props) => { const buttons: JSX.Element[] = []; const moreActions: JSX.Element[] = []; + /** + * Triggers API call to update the current rule to the new `is_paused` state + */ + const setRulePause = async (newIsPaused: boolean) => { + if (!isGrafanaRulerRule(rule.rulerRule)) { + return; + } + const ruleUid = rule.rulerRule.grafana_alert.uid; + + // Parse the rules into correct format for API + const modifiedRules = group.rules.map((groupRule) => { + if (isGrafanaRulerRule(groupRule.rulerRule) && groupRule.rulerRule.grafana_alert.uid === ruleUid) { + return { + ...groupRule.rulerRule, + grafana_alert: { + ...groupRule.rulerRule.grafana_alert, + is_paused: newIsPaused, + }, + }; + } + + return groupRule.rulerRule!; + }); + + const payload = { + interval: group.interval!, + name: group.name, + rules: modifiedRules, + }; + + await updateRule({ nameSpaceUID: rule.namespace.uid!, payload }).unwrap(); + + const limitAlerts = hasActiveFilters ? undefined : LIMIT_ALERTS; + // Trigger a re-fetch of the rules table + // TODO: Migrate rules table functionality to RTK Query, so we instead rely + // on tag invalidation (or optimistic cache updates) for this + await dispatch(fetchAllPromAndRulerRulesAction(false, { limitAlerts })); + }; + const deleteRule = () => { if (ruleToDelete && ruleToDelete.rulerRule) { const identifier = ruleId.fromRulerRule( @@ -123,6 +169,19 @@ export const RuleActionsButtons = ({ rule, rulesSource }: Props) => { /> ); + + const isPaused = isGrafanaRulerRule(rule.rulerRule) && isGrafanaRulerRulePaused(rule.rulerRule); + const icon = isPaused ? 'play' : 'pause'; + const title = isPaused ? 'Resume alert evaluation' : 'Pause alert evaluation'; + moreActions.push( + { + setRulePause(!isPaused); + }} + /> + ); } if (isViewMode) {