mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Allow disabling override timings for notification policies (#48648)
This commit is contained in:
parent
1a7ca3f0de
commit
fa37c6c9d3
@ -646,6 +646,7 @@ const clickSelectOption = async (selectElement: HTMLElement, optionText: string)
|
|||||||
const updateTiming = async (selectElement: HTMLElement, value: string, timeUnit: string): Promise<void> => {
|
const updateTiming = async (selectElement: HTMLElement, value: string, timeUnit: string): Promise<void> => {
|
||||||
const input = byRole('textbox').get(selectElement);
|
const input = byRole('textbox').get(selectElement);
|
||||||
const select = byRole('combobox').get(selectElement);
|
const select = byRole('combobox').get(selectElement);
|
||||||
|
await userEvent.clear(input);
|
||||||
await userEvent.type(input, value);
|
await userEvent.type(input, value);
|
||||||
await userEvent.click(select);
|
await userEvent.click(select);
|
||||||
await selectOptionInTest(selectElement, timeUnit);
|
await selectOptionInTest(selectElement, timeUnit);
|
||||||
|
@ -36,7 +36,7 @@ export const AmRootRouteForm: FC<AmRootRouteFormProps> = ({
|
|||||||
const [groupByOptions, setGroupByOptions] = useState(stringsToSelectableValues(routes.groupBy));
|
const [groupByOptions, setGroupByOptions] = useState(stringsToSelectableValues(routes.groupBy));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form defaultValues={routes} onSubmit={onSave}>
|
<Form defaultValues={{ ...routes, overrideTimings: true }} onSubmit={onSave}>
|
||||||
{({ control, errors, setValue }) => (
|
{({ control, errors, setValue }) => (
|
||||||
<>
|
<>
|
||||||
<Field label="Default contact point" invalid={!!errors.receiver} error={errors.receiver?.message}>
|
<Field label="Default contact point" invalid={!!errors.receiver} error={errors.receiver?.message}>
|
||||||
@ -111,12 +111,7 @@ export const AmRootRouteForm: FC<AmRootRouteFormProps> = ({
|
|||||||
<div className={cx(styles.container, styles.timingContainer)}>
|
<div className={cx(styles.container, styles.timingContainer)}>
|
||||||
<InputControl
|
<InputControl
|
||||||
render={({ field, fieldState: { invalid } }) => (
|
render={({ field, fieldState: { invalid } }) => (
|
||||||
<Input
|
<Input {...field} className={styles.smallInput} invalid={invalid} placeholder={'30'} />
|
||||||
{...field}
|
|
||||||
className={styles.smallInput}
|
|
||||||
invalid={invalid}
|
|
||||||
placeholder={'Default 30 seconds'}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
control={control}
|
control={control}
|
||||||
name="groupWaitValue"
|
name="groupWaitValue"
|
||||||
@ -151,12 +146,7 @@ export const AmRootRouteForm: FC<AmRootRouteFormProps> = ({
|
|||||||
<div className={cx(styles.container, styles.timingContainer)}>
|
<div className={cx(styles.container, styles.timingContainer)}>
|
||||||
<InputControl
|
<InputControl
|
||||||
render={({ field, fieldState: { invalid } }) => (
|
render={({ field, fieldState: { invalid } }) => (
|
||||||
<Input
|
<Input {...field} className={styles.smallInput} invalid={invalid} placeholder={'5'} />
|
||||||
{...field}
|
|
||||||
className={styles.smallInput}
|
|
||||||
invalid={invalid}
|
|
||||||
placeholder={'Default 5 minutes'}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
control={control}
|
control={control}
|
||||||
name="groupIntervalValue"
|
name="groupIntervalValue"
|
||||||
@ -191,7 +181,7 @@ export const AmRootRouteForm: FC<AmRootRouteFormProps> = ({
|
|||||||
<div className={cx(styles.container, styles.timingContainer)}>
|
<div className={cx(styles.container, styles.timingContainer)}>
|
||||||
<InputControl
|
<InputControl
|
||||||
render={({ field, fieldState: { invalid } }) => (
|
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}
|
control={control}
|
||||||
name="repeatIntervalValue"
|
name="repeatIntervalValue"
|
||||||
|
@ -45,15 +45,12 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
|
|||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
const formStyles = useStyles2(getFormStyles);
|
const formStyles = useStyles2(getFormStyles);
|
||||||
const [overrideGrouping, setOverrideGrouping] = useState(routes.groupBy.length > 0);
|
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 [groupByOptions, setGroupByOptions] = useState(stringsToSelectableValues(routes.groupBy));
|
||||||
const muteTimingOptions = useMuteTimingOptions();
|
const muteTimingOptions = useMuteTimingOptions();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form defaultValues={routes} onSubmit={onSave}>
|
<Form defaultValues={routes} onSubmit={onSave}>
|
||||||
{({ control, register, errors, setValue }) => (
|
{({ control, register, errors, setValue, watch }) => (
|
||||||
<>
|
<>
|
||||||
{/* @ts-ignore-check: react-hook-form made me do this */}
|
{/* @ts-ignore-check: react-hook-form made me do this */}
|
||||||
<input type="hidden" {...register('id')} />
|
<input type="hidden" {...register('id')} />
|
||||||
@ -169,7 +166,10 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
|
|||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
{overrideGrouping && (
|
{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
|
<InputControl
|
||||||
render={({ field: { onChange, ref, ...field } }) => (
|
render={({ field: { onChange, ref, ...field } }) => (
|
||||||
<MultiSelect
|
<MultiSelect
|
||||||
@ -193,17 +193,13 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
|
|||||||
</Field>
|
</Field>
|
||||||
)}
|
)}
|
||||||
<Field label="Override general timings">
|
<Field label="Override general timings">
|
||||||
<Switch
|
<Switch id="override-timings-toggle" {...register('overrideTimings')} />
|
||||||
id="override-timings-toggle"
|
|
||||||
value={overrideTimings}
|
|
||||||
onChange={() => setOverrideTimings((overrideTimings) => !overrideTimings)}
|
|
||||||
/>
|
|
||||||
</Field>
|
</Field>
|
||||||
{overrideTimings && (
|
{watch().overrideTimings && (
|
||||||
<>
|
<>
|
||||||
<Field
|
<Field
|
||||||
label="Group wait"
|
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}
|
invalid={!!errors.groupWaitValue}
|
||||||
error={errors.groupWaitValue?.message}
|
error={errors.groupWaitValue?.message}
|
||||||
>
|
>
|
||||||
@ -215,7 +211,6 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
|
|||||||
{...field}
|
{...field}
|
||||||
className={formStyles.smallInput}
|
className={formStyles.smallInput}
|
||||||
invalid={invalid}
|
invalid={invalid}
|
||||||
placeholder="Time"
|
|
||||||
aria-label="Group wait value"
|
aria-label="Group wait value"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -243,7 +238,7 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
|
|||||||
</Field>
|
</Field>
|
||||||
<Field
|
<Field
|
||||||
label="Group interval"
|
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}
|
invalid={!!errors.groupIntervalValue}
|
||||||
error={errors.groupIntervalValue?.message}
|
error={errors.groupIntervalValue?.message}
|
||||||
>
|
>
|
||||||
@ -255,7 +250,6 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
|
|||||||
{...field}
|
{...field}
|
||||||
className={formStyles.smallInput}
|
className={formStyles.smallInput}
|
||||||
invalid={invalid}
|
invalid={invalid}
|
||||||
placeholder="Time"
|
|
||||||
aria-label="Group interval value"
|
aria-label="Group interval value"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -295,7 +289,6 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
|
|||||||
{...field}
|
{...field}
|
||||||
className={formStyles.smallInput}
|
className={formStyles.smallInput}
|
||||||
invalid={invalid}
|
invalid={invalid}
|
||||||
placeholder="Time"
|
|
||||||
aria-label="Repeat interval value"
|
aria-label="Repeat interval value"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -11,6 +11,7 @@ const defaultAmRoute: FormAmRoute = {
|
|||||||
continue: false,
|
continue: false,
|
||||||
receiver: '',
|
receiver: '',
|
||||||
groupBy: [],
|
groupBy: [],
|
||||||
|
overrideTimings: false,
|
||||||
groupWaitValue: '',
|
groupWaitValue: '',
|
||||||
groupWaitValueType: '',
|
groupWaitValueType: '',
|
||||||
groupIntervalValue: '',
|
groupIntervalValue: '',
|
||||||
|
@ -6,6 +6,7 @@ export interface FormAmRoute {
|
|||||||
continue: boolean;
|
continue: boolean;
|
||||||
receiver: string;
|
receiver: string;
|
||||||
groupBy: string[];
|
groupBy: string[];
|
||||||
|
overrideTimings: boolean;
|
||||||
groupWaitValue: string;
|
groupWaitValue: string;
|
||||||
groupWaitValueType: string;
|
groupWaitValueType: string;
|
||||||
groupIntervalValue: string;
|
groupIntervalValue: string;
|
||||||
|
@ -11,7 +11,7 @@ import { matcherToMatcherField, parseMatcher } from './alertmanager';
|
|||||||
import { GRAFANA_RULES_SOURCE_NAME } from './datasource';
|
import { GRAFANA_RULES_SOURCE_NAME } from './datasource';
|
||||||
import { parseInterval, timeOptions } from './time';
|
import { parseInterval, timeOptions } from './time';
|
||||||
|
|
||||||
const defaultValueAndType: [string, string] = ['', timeOptions[0].value];
|
const defaultValueAndType: [string, string] = ['', ''];
|
||||||
|
|
||||||
const matchersToArrayFieldMatchers = (
|
const matchersToArrayFieldMatchers = (
|
||||||
matchers: Record<string, string> | undefined,
|
matchers: Record<string, string> | undefined,
|
||||||
@ -29,9 +29,12 @@ const matchersToArrayFieldMatchers = (
|
|||||||
[] as MatcherFieldValue[]
|
[] as MatcherFieldValue[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const intervalToValueAndType = (strValue: string | undefined): [string, string] => {
|
const intervalToValueAndType = (
|
||||||
|
strValue: string | undefined,
|
||||||
|
defaultValue?: typeof defaultValueAndType
|
||||||
|
): [string, string] => {
|
||||||
if (!strValue) {
|
if (!strValue) {
|
||||||
return defaultValueAndType;
|
return defaultValue ?? defaultValueAndType;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [value, valueType] = strValue ? parseInterval(strValue) : [undefined, undefined];
|
const [value, valueType] = strValue ? parseInterval(strValue) : [undefined, undefined];
|
||||||
@ -63,6 +66,7 @@ export const emptyRoute: FormAmRoute = {
|
|||||||
routes: [],
|
routes: [],
|
||||||
continue: false,
|
continue: false,
|
||||||
receiver: '',
|
receiver: '',
|
||||||
|
overrideTimings: false,
|
||||||
groupWaitValue: '',
|
groupWaitValue: '',
|
||||||
groupWaitValueType: timeOptions[0].value,
|
groupWaitValueType: timeOptions[0].value,
|
||||||
groupIntervalValue: '',
|
groupIntervalValue: '',
|
||||||
@ -78,9 +82,9 @@ export const amRouteToFormAmRoute = (route: Route | undefined): [FormAmRoute, Re
|
|||||||
return [emptyRoute, {}];
|
return [emptyRoute, {}];
|
||||||
}
|
}
|
||||||
|
|
||||||
const [groupWaitValue, groupWaitValueType] = intervalToValueAndType(route.group_wait);
|
const [groupWaitValue, groupWaitValueType] = intervalToValueAndType(route.group_wait, ['', 's']);
|
||||||
const [groupIntervalValue, groupIntervalValueType] = intervalToValueAndType(route.group_interval);
|
const [groupIntervalValue, groupIntervalValueType] = intervalToValueAndType(route.group_interval, ['', 'm']);
|
||||||
const [repeatIntervalValue, repeatIntervalValueType] = intervalToValueAndType(route.repeat_interval);
|
const [repeatIntervalValue, repeatIntervalValueType] = intervalToValueAndType(route.repeat_interval, ['', 'h']);
|
||||||
|
|
||||||
const id = String(Math.random());
|
const id = String(Math.random());
|
||||||
const id2route = {
|
const id2route = {
|
||||||
@ -111,6 +115,7 @@ export const amRouteToFormAmRoute = (route: Route | undefined): [FormAmRoute, Re
|
|||||||
continue: route.continue ?? false,
|
continue: route.continue ?? false,
|
||||||
receiver: route.receiver ?? '',
|
receiver: route.receiver ?? '',
|
||||||
groupBy: route.group_by ?? [],
|
groupBy: route.group_by ?? [],
|
||||||
|
overrideTimings: [groupWaitValue, groupIntervalValue, repeatIntervalValue].some(Boolean),
|
||||||
groupWaitValue,
|
groupWaitValue,
|
||||||
groupWaitValueType,
|
groupWaitValueType,
|
||||||
groupIntervalValue,
|
groupIntervalValue,
|
||||||
@ -130,6 +135,26 @@ export const formAmRouteToAmRoute = (
|
|||||||
id2ExistingRoute: Record<string, Route>
|
id2ExistingRoute: Record<string, Route>
|
||||||
): Route => {
|
): Route => {
|
||||||
const existing: Route | undefined = id2ExistingRoute[formAmRoute.id];
|
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 = {
|
const amRoute: Route = {
|
||||||
...(existing ?? {}),
|
...(existing ?? {}),
|
||||||
continue: formAmRoute.continue,
|
continue: formAmRoute.continue,
|
||||||
@ -137,17 +162,11 @@ export const formAmRouteToAmRoute = (
|
|||||||
object_matchers: formAmRoute.object_matchers.length
|
object_matchers: formAmRoute.object_matchers.length
|
||||||
? formAmRoute.object_matchers.map((matcher) => [matcher.name, matcher.operator, matcher.value])
|
? formAmRoute.object_matchers.map((matcher) => [matcher.name, matcher.operator, matcher.value])
|
||||||
: undefined,
|
: undefined,
|
||||||
match: undefined,
|
match: undefined, // DEPRECATED: Use matchers
|
||||||
match_re: undefined,
|
match_re: undefined, // DEPRECATED: Use matchers
|
||||||
group_wait: formAmRoute.groupWaitValue
|
group_wait,
|
||||||
? `${formAmRoute.groupWaitValue}${formAmRoute.groupWaitValueType}`
|
group_interval,
|
||||||
: undefined,
|
repeat_interval,
|
||||||
group_interval: formAmRoute.groupIntervalValue
|
|
||||||
? `${formAmRoute.groupIntervalValue}${formAmRoute.groupIntervalValueType}`
|
|
||||||
: undefined,
|
|
||||||
repeat_interval: formAmRoute.repeatIntervalValue
|
|
||||||
? `${formAmRoute.repeatIntervalValue}${formAmRoute.repeatIntervalValueType}`
|
|
||||||
: undefined,
|
|
||||||
routes: formAmRoute.routes.map((subRoute) =>
|
routes: formAmRoute.routes.map((subRoute) =>
|
||||||
formAmRouteToAmRoute(alertManagerSourceName, subRoute, id2ExistingRoute)
|
formAmRouteToAmRoute(alertManagerSourceName, subRoute, id2ExistingRoute)
|
||||||
),
|
),
|
||||||
|
Loading…
Reference in New Issue
Block a user