From beca6a08b030405b084eda0c831a26b95cb9b1e3 Mon Sep 17 00:00:00 2001 From: Sonia Aguilar <33540275+soniaAguilarPeiron@users.noreply.github.com> Date: Fri, 9 Feb 2024 12:16:28 +0100 Subject: [PATCH] Alerting: defaults for simplified routing (#82050) * Expand route settings by default when alert rule has values in these fields * Default to manual routing option if FF is enabled and local storage is not set to false * Add test for getDefautManualRouting function * Update seting local storage item to false in case of policy routing * Only save to local storage when creating a new alert rule --- .../alert-rule-form/AlertRuleForm.tsx | 17 +++++++--- .../simplifiedRouting/AlertManagerRouting.tsx | 10 +++++- .../alerting/unified/utils/rule-form.test.ts | 33 +++++++++++++++++++ .../alerting/unified/utils/rule-form.ts | 18 ++++++++-- 4 files changed, 71 insertions(+), 7 deletions(-) diff --git a/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/AlertRuleForm.tsx b/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/AlertRuleForm.tsx index a7a5ef32199..e1f56127f28 100644 --- a/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/AlertRuleForm.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/AlertRuleForm.tsx @@ -1,11 +1,11 @@ import { css } from '@emotion/css'; import React, { useEffect, useMemo, useState } from 'react'; -import { FormProvider, SubmitErrorHandler, useForm, UseFormWatch } from 'react-hook-form'; +import { FormProvider, SubmitErrorHandler, UseFormWatch, useForm } from 'react-hook-form'; import { Link, useParams } from 'react-router-dom'; import { GrafanaTheme2 } from '@grafana/data'; import { config } from '@grafana/runtime'; -import { Button, ConfirmModal, CustomScrollbar, HorizontalGroup, Spinner, useStyles2, Stack } from '@grafana/ui'; +import { Button, ConfirmModal, CustomScrollbar, HorizontalGroup, Spinner, Stack, useStyles2 } from '@grafana/ui'; import { AppChromeUpdate } from 'app/core/components/AppChrome/AppChromeUpdate'; import { useAppNotification } from 'app/core/copy/appNotification'; import { contextSrv } from 'app/core/core'; @@ -14,17 +14,18 @@ import { useQueryParams } from 'app/core/hooks/useQueryParams'; import { useDispatch } from 'app/types'; import { RuleWithLocation } from 'app/types/unified-alerting'; -import { logInfo, LogMessages, trackNewAlerRuleFormError } from '../../../Analytics'; +import { LogMessages, logInfo, trackNewAlerRuleFormError } from '../../../Analytics'; import { useUnifiedAlertingSelector } from '../../../hooks/useUnifiedAlertingSelector'; import { deleteRuleAction, saveRuleFormAction } from '../../../state/actions'; import { RuleFormType, RuleFormValues } from '../../../types/rule-form'; import { initialAsyncRequestState } from '../../../utils/redux'; import { + MANUAL_ROUTING_KEY, + MINUTE, formValuesFromExistingRule, getDefaultFormValues, getDefaultQueries, ignoreHiddenQueries, - MINUTE, normalizeDefaultAnnotations, } from '../../../utils/rule-form'; import * as ruleId from '../../../utils/rule-id'; @@ -108,6 +109,14 @@ export const AlertRuleForm = ({ existing, prefill }: Props) => { notifyApp.error(conditionErrorMsg); return; } + // when creating a new rule, we save the manual routing setting in local storage + if (!existing) { + if (values.manualRouting) { + localStorage.setItem(MANUAL_ROUTING_KEY, 'true'); + } else { + localStorage.setItem(MANUAL_ROUTING_KEY, 'false'); + } + } dispatch( saveRuleFormAction({ diff --git a/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/AlertManagerRouting.tsx b/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/AlertManagerRouting.tsx index 06ab125cba9..94ee0b3be3f 100644 --- a/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/AlertManagerRouting.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/AlertManagerRouting.tsx @@ -1,8 +1,10 @@ import { css } from '@emotion/css'; import React, { useState } from 'react'; +import { useFormContext } from 'react-hook-form'; import { GrafanaTheme2 } from '@grafana/data'; import { Alert, CollapsableSection, LoadingPlaceholder, Stack, useStyles2 } from '@grafana/ui'; +import { RuleFormValues } from 'app/features/alerting/unified/types/rule-form'; import { AlertManagerDataSource } from 'app/features/alerting/unified/utils/datasource'; import { ContactPointReceiverSummary } from '../../../contact-points/ContactPoints'; @@ -36,6 +38,12 @@ export function AlertManagerManualRouting({ alertManager }: AlertManagerManualRo setSelectedContactPointWithMetadata(contactPoint); }; + const { watch } = useFormContext(); + const hasRouteSettings = + watch(`contactPoints.${alertManagerName}.overrideGrouping`) || + watch(`contactPoints.${alertManagerName}.overrideTimings`) || + watch(`contactPoints.${alertManagerName}.muteTimeIntervals`)?.length > 0; + const options = contactPoints.map((receiver) => { const integrations = receiver?.grafana_managed_receiver_configs; const description = ; @@ -74,7 +82,7 @@ export function AlertManagerManualRouting({ alertManager }: AlertManagerManualRo
diff --git a/public/app/features/alerting/unified/utils/rule-form.test.ts b/public/app/features/alerting/unified/utils/rule-form.test.ts index 48911f86b6a..735a8f7087b 100644 --- a/public/app/features/alerting/unified/utils/rule-form.test.ts +++ b/public/app/features/alerting/unified/utils/rule-form.test.ts @@ -1,3 +1,4 @@ +import { config } from '@grafana/runtime'; import { PromQuery } from 'app/plugins/datasource/prometheus/types'; import { GrafanaAlertStateDecision, GrafanaRuleDefinition, RulerAlertingRuleDTO } from 'app/types/unified-alerting-dto'; @@ -5,11 +6,13 @@ import { AlertManagerManualRouting, RuleFormType, RuleFormValues } from '../type import { GRAFANA_RULES_SOURCE_NAME } from './datasource'; import { + MANUAL_ROUTING_KEY, alertingRulerRuleToRuleForm, formValuesToRulerGrafanaRuleDTO, formValuesToRulerRuleDTO, getContactPointsFromDTO, getDefaultFormValues, + getDefautManualRouting, getNotificationSettingsForDTO, } from './rule-form'; @@ -207,3 +210,33 @@ describe('getNotificationSettingsForDTO', () => { }); }); }); + +describe('getDefautManualRouting', () => { + afterEach(() => { + window.localStorage.clear(); + }); + + it('returns false if the feature toggle is not enabled', () => { + config.featureToggles.alertingSimplifiedRouting = false; + expect(getDefautManualRouting()).toBe(false); + }); + + it('returns true if the feature toggle is enabled and localStorage is not set', () => { + config.featureToggles.alertingSimplifiedRouting = true; + expect(getDefautManualRouting()).toBe(true); + }); + + it('returns false if the feature toggle is enabled and localStorage is set to "false"', () => { + config.featureToggles.alertingSimplifiedRouting = true; + localStorage.setItem(MANUAL_ROUTING_KEY, 'false'); + expect(getDefautManualRouting()).toBe(false); + }); + + it('returns true if the feature toggle is enabled and localStorage is set to any value other than "false"', () => { + config.featureToggles.alertingSimplifiedRouting = true; + localStorage.setItem(MANUAL_ROUTING_KEY, 'true'); + expect(getDefautManualRouting()).toBe(true); + localStorage.removeItem(MANUAL_ROUTING_KEY); + expect(getDefautManualRouting()).toBe(true); + }); +}); diff --git a/public/app/features/alerting/unified/utils/rule-form.ts b/public/app/features/alerting/unified/utils/rule-form.ts index cd4d6abdaad..f7dd7d6c437 100644 --- a/public/app/features/alerting/unified/utils/rule-form.ts +++ b/public/app/features/alerting/unified/utils/rule-form.ts @@ -11,7 +11,7 @@ import { ScopedVars, TimeRange, } from '@grafana/data'; -import { getDataSourceSrv } from '@grafana/runtime'; +import { config, getDataSourceSrv } from '@grafana/runtime'; import { ExpressionDatasourceRef } from '@grafana/runtime/src/utils/DataSourceWithBackend'; import { sceneGraph, VizPanel } from '@grafana/scenes'; import { DataSourceJsonData } from '@grafana/schema'; @@ -54,6 +54,8 @@ export type PromOrLokiQuery = PromQuery | LokiQuery; export const MINUTE = '1m'; +export const MANUAL_ROUTING_KEY = 'grafana.alerting.manualRouting'; + export const getDefaultFormValues = (): RuleFormValues => { const { canCreateGrafanaRules, canCreateCloudRules } = getRulesAccess(); @@ -75,7 +77,7 @@ export const getDefaultFormValues = (): RuleFormValues => { execErrState: GrafanaAlertStateDecision.Error, evaluateFor: '5m', evaluateEvery: MINUTE, - manualRouting: false, // let's decide this later + manualRouting: getDefautManualRouting(), // we default to true if the feature toggle is enabled and the user hasn't set local storage to false contactPoints: {}, overrideGrouping: false, overrideTimings: false, @@ -89,6 +91,18 @@ export const getDefaultFormValues = (): RuleFormValues => { }); }; +export const getDefautManualRouting = () => { + // first check if feature toggle for simplified routing is enabled + const simplifiedRoutingToggleEnabled = config.featureToggles.alertingSimplifiedRouting ?? false; + if (!simplifiedRoutingToggleEnabled) { + return false; + } + //then, check in local storage if the user has enabled simplified routing + // if it's not set, we'll default to true + const manualRouting = localStorage.getItem(MANUAL_ROUTING_KEY); + return manualRouting !== 'false'; +}; + export function formValuesToRulerRuleDTO(values: RuleFormValues): RulerRuleDTO { const { name, expression, forTime, forTimeUnit, keepFiringForTime, keepFiringForTimeUnit, type } = values; if (type === RuleFormType.cloudAlerting) {