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
pull/82249/head
Sonia Aguilar 1 year ago committed by GitHub
parent 3e93a0991f
commit beca6a08b0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 17
      public/app/features/alerting/unified/components/rule-editor/alert-rule-form/AlertRuleForm.tsx
  2. 10
      public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/AlertManagerRouting.tsx
  3. 33
      public/app/features/alerting/unified/utils/rule-form.test.ts
  4. 18
      public/app/features/alerting/unified/utils/rule-form.ts

@ -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({

@ -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<RuleFormValues>();
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 = <ContactPointReceiverSummary receivers={integrations ?? []} />;
@ -74,7 +82,7 @@ export function AlertManagerManualRouting({ alertManager }: AlertManagerManualRo
<div className={styles.routingSection}>
<CollapsableSection
label="Muting, grouping and timings (optional)"
isOpen={false}
isOpen={hasRouteSettings}
className={styles.collapsableSection}
>
<Stack direction="column" gap={1}>

@ -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);
});
});

@ -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) {

Loading…
Cancel
Save