Skip to content

Commit

Permalink
Alerting: Allow disabling override timings for notification policies (#…
Browse files Browse the repository at this point in the history
…48648)

(cherry picked from commit fa37c6c)
  • Loading branch information
gillesdemey committed May 16, 2022
1 parent cd70ea3 commit 2f7aee1
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 47 deletions.
1 change: 1 addition & 0 deletions public/app/features/alerting/unified/AmRoutes.test.tsx
Expand Up @@ -646,6 +646,7 @@ const clickSelectOption = async (selectElement: HTMLElement, optionText: string)
const updateTiming = async (selectElement: HTMLElement, value: string, timeUnit: string): Promise<void> => {
const input = byRole('textbox').get(selectElement);
const select = byRole('combobox').get(selectElement);
userEvent.clear(input);
userEvent.type(input, value);
userEvent.click(select);
await selectOptionInTest(selectElement, timeUnit);
Expand Down
Expand Up @@ -36,7 +36,7 @@ export const AmRootRouteForm: FC<AmRootRouteFormProps> = ({
const [groupByOptions, setGroupByOptions] = useState(stringsToSelectableValues(routes.groupBy));

return (
<Form defaultValues={routes} onSubmit={onSave}>
<Form defaultValues={{ ...routes, overrideTimings: true }} onSubmit={onSave}>
{({ control, errors, setValue }) => (
<>
<Field label="Default contact point" invalid={!!errors.receiver} error={errors.receiver?.message}>
Expand Down Expand Up @@ -113,12 +113,7 @@ export const AmRootRouteForm: FC<AmRootRouteFormProps> = ({
<div className={cx(styles.container, styles.timingContainer)}>
<InputControl
render={({ field, fieldState: { invalid } }) => (
<Input
{...field}
className={styles.smallInput}
invalid={invalid}
placeholder={'Default 30 seconds'}
/>
<Input {...field} className={styles.smallInput} invalid={invalid} placeholder={'30'} />
)}
control={control}
name="groupWaitValue"
Expand Down Expand Up @@ -154,12 +149,7 @@ export const AmRootRouteForm: FC<AmRootRouteFormProps> = ({
<div className={cx(styles.container, styles.timingContainer)}>
<InputControl
render={({ field, fieldState: { invalid } }) => (
<Input
{...field}
className={styles.smallInput}
invalid={invalid}
placeholder={'Default 5 minutes'}
/>
<Input {...field} className={styles.smallInput} invalid={invalid} placeholder={'5'} />
)}
control={control}
name="groupIntervalValue"
Expand Down Expand Up @@ -195,7 +185,7 @@ export const AmRootRouteForm: FC<AmRootRouteFormProps> = ({
<div className={cx(styles.container, styles.timingContainer)}>
<InputControl
render={({ field, fieldState: { invalid } }) => (
<Input {...field} className={styles.smallInput} invalid={invalid} placeholder="Default 4 hours" />
<Input {...field} className={styles.smallInput} invalid={invalid} placeholder="4" />
)}
control={control}
name="repeatIntervalValue"
Expand Down
Expand Up @@ -45,15 +45,12 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
const styles = useStyles2(getStyles);
const formStyles = useStyles2(getFormStyles);
const [overrideGrouping, setOverrideGrouping] = useState(routes.groupBy.length > 0);
const [overrideTimings, setOverrideTimings] = useState(
!!routes.groupWaitValue || !!routes.groupIntervalValue || !!routes.repeatIntervalValue
);
const [groupByOptions, setGroupByOptions] = useState(stringsToSelectableValues(routes.groupBy));
const muteTimingOptions = useMuteTimingOptions();

return (
<Form defaultValues={routes} onSubmit={onSave}>
{({ control, register, errors, setValue }) => (
{({ control, register, errors, setValue, watch }) => (
<>
{/* @ts-ignore-check: react-hook-form made me do this */}
<input type="hidden" {...register('id')} />
Expand Down Expand Up @@ -171,7 +168,10 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
/>
</Field>
{overrideGrouping && (
<Field label="Group by" description="Group alerts when you receive a notification based on labels.">
<Field
label="Group by"
description="Group alerts when you receive a notification based on labels. If empty it will be inherited from the parent policy."
>
<InputControl
render={({ field: { onChange, ref, ...field } }) => (
<MultiSelect
Expand All @@ -196,17 +196,13 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
</Field>
)}
<Field label="Override general timings">
<Switch
id="override-timings-toggle"
value={overrideTimings}
onChange={() => setOverrideTimings((overrideTimings) => !overrideTimings)}
/>
<Switch id="override-timings-toggle" {...register('overrideTimings')} />
</Field>
{overrideTimings && (
{watch().overrideTimings && (
<>
<Field
label="Group wait"
description="The waiting time until the initial notification is sent for a new group created by an incoming alert."
description="The waiting time until the initial notification is sent for a new group created by an incoming alert. If empty it will be inherited from the parent policy."
invalid={!!errors.groupWaitValue}
error={errors.groupWaitValue?.message}
>
Expand All @@ -218,7 +214,6 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
{...field}
className={formStyles.smallInput}
invalid={invalid}
placeholder="Time"
aria-label="Group wait value"
/>
)}
Expand Down Expand Up @@ -247,7 +242,7 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
</Field>
<Field
label="Group interval"
description="The waiting time to send a batch of new alerts for that group after the first notification was sent."
description="The waiting time to send a batch of new alerts for that group after the first notification was sent. If empty it will be inherited from the parent policy."
invalid={!!errors.groupIntervalValue}
error={errors.groupIntervalValue?.message}
>
Expand All @@ -259,7 +254,6 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
{...field}
className={formStyles.smallInput}
invalid={invalid}
placeholder="Time"
aria-label="Group interval value"
/>
)}
Expand Down Expand Up @@ -300,7 +294,6 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
{...field}
className={formStyles.smallInput}
invalid={invalid}
placeholder="Time"
aria-label="Repeat interval value"
/>
)}
Expand Down
Expand Up @@ -11,6 +11,7 @@ const defaultAmRoute: FormAmRoute = {
continue: false,
receiver: '',
groupBy: [],
overrideTimings: false,
groupWaitValue: '',
groupWaitValueType: '',
groupIntervalValue: '',
Expand Down
1 change: 1 addition & 0 deletions public/app/features/alerting/unified/types/amroutes.ts
Expand Up @@ -6,6 +6,7 @@ export interface FormAmRoute {
continue: boolean;
receiver: string;
groupBy: string[];
overrideTimings: boolean;
groupWaitValue: string;
groupWaitValueType: string;
groupIntervalValue: string;
Expand Down
53 changes: 36 additions & 17 deletions public/app/features/alerting/unified/utils/amroutes.ts
Expand Up @@ -11,7 +11,7 @@ import { matcherToMatcherField, parseMatcher } from './alertmanager';
import { GRAFANA_RULES_SOURCE_NAME } from './datasource';
import { parseInterval, timeOptions } from './time';

const defaultValueAndType: [string, string] = ['', timeOptions[0].value];
const defaultValueAndType: [string, string] = ['', ''];

const matchersToArrayFieldMatchers = (
matchers: Record<string, string> | undefined,
Expand All @@ -29,9 +29,12 @@ const matchersToArrayFieldMatchers = (
[] as MatcherFieldValue[]
);

const intervalToValueAndType = (strValue: string | undefined): [string, string] => {
const intervalToValueAndType = (
strValue: string | undefined,
defaultValue?: typeof defaultValueAndType
): [string, string] => {
if (!strValue) {
return defaultValueAndType;
return defaultValue ?? defaultValueAndType;
}

const [value, valueType] = strValue ? parseInterval(strValue) : [undefined, undefined];
Expand Down Expand Up @@ -63,6 +66,7 @@ export const emptyRoute: FormAmRoute = {
routes: [],
continue: false,
receiver: '',
overrideTimings: false,
groupWaitValue: '',
groupWaitValueType: timeOptions[0].value,
groupIntervalValue: '',
Expand All @@ -78,9 +82,9 @@ export const amRouteToFormAmRoute = (route: Route | undefined): [FormAmRoute, Re
return [emptyRoute, {}];
}

const [groupWaitValue, groupWaitValueType] = intervalToValueAndType(route.group_wait);
const [groupIntervalValue, groupIntervalValueType] = intervalToValueAndType(route.group_interval);
const [repeatIntervalValue, repeatIntervalValueType] = intervalToValueAndType(route.repeat_interval);
const [groupWaitValue, groupWaitValueType] = intervalToValueAndType(route.group_wait, ['', 's']);
const [groupIntervalValue, groupIntervalValueType] = intervalToValueAndType(route.group_interval, ['', 'm']);
const [repeatIntervalValue, repeatIntervalValueType] = intervalToValueAndType(route.repeat_interval, ['', 'h']);

const id = String(Math.random());
const id2route = {
Expand Down Expand Up @@ -111,6 +115,7 @@ export const amRouteToFormAmRoute = (route: Route | undefined): [FormAmRoute, Re
continue: route.continue ?? false,
receiver: route.receiver ?? '',
groupBy: route.group_by ?? [],
overrideTimings: [groupWaitValue, groupIntervalValue, repeatIntervalValue].some(Boolean),
groupWaitValue,
groupWaitValueType,
groupIntervalValue,
Expand All @@ -130,24 +135,38 @@ export const formAmRouteToAmRoute = (
id2ExistingRoute: Record<string, Route>
): Route => {
const existing: Route | undefined = id2ExistingRoute[formAmRoute.id];

const {
overrideTimings,
groupWaitValue,
groupWaitValueType,
groupIntervalValue,
groupIntervalValueType,
repeatIntervalValue,
repeatIntervalValueType,
} = formAmRoute;

const overrideGroupWait = overrideTimings && groupWaitValue;
const group_wait = overrideGroupWait ? `${groupWaitValue}${groupWaitValueType}` : undefined;

const overrideGroupInterval = overrideTimings && groupIntervalValue;
const group_interval = overrideGroupInterval ? `${groupIntervalValue}${groupIntervalValueType}` : undefined;

const overrideRepeatInterval = overrideTimings && repeatIntervalValue;
const repeat_interval = overrideRepeatInterval ? `${repeatIntervalValue}${repeatIntervalValueType}` : undefined;

const amRoute: Route = {
...(existing ?? {}),
continue: formAmRoute.continue,
group_by: formAmRoute.groupBy,
object_matchers: formAmRoute.object_matchers.length
? formAmRoute.object_matchers.map((matcher) => [matcher.name, matcher.operator, matcher.value])
: undefined,
match: undefined,
match_re: undefined,
group_wait: formAmRoute.groupWaitValue
? `${formAmRoute.groupWaitValue}${formAmRoute.groupWaitValueType}`
: undefined,
group_interval: formAmRoute.groupIntervalValue
? `${formAmRoute.groupIntervalValue}${formAmRoute.groupIntervalValueType}`
: undefined,
repeat_interval: formAmRoute.repeatIntervalValue
? `${formAmRoute.repeatIntervalValue}${formAmRoute.repeatIntervalValueType}`
: undefined,
match: undefined, // DEPRECATED: Use matchers
match_re: undefined, // DEPRECATED: Use matchers
group_wait,
group_interval,
repeat_interval,
routes: formAmRoute.routes.map((subRoute) =>
formAmRouteToAmRoute(alertManagerSourceName, subRoute, id2ExistingRoute)
),
Expand Down

0 comments on commit 2f7aee1

Please sign in to comment.