Alerting: Consolidate mute timings selector logic (#92372)
This commit is contained in:
+33
@@ -0,0 +1,33 @@
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { MultiSelect, MultiSelectCommonProps } from '@grafana/ui';
|
||||
import { useSelectableMuteTimings } from 'app/features/alerting/unified/components/mute-timings/useMuteTimings';
|
||||
import { BaseAlertmanagerArgs } from 'app/features/alerting/unified/types/hooks';
|
||||
import { timeIntervalToString } from 'app/features/alerting/unified/utils/alertmanager';
|
||||
import { MuteTimeInterval } from 'app/plugins/datasource/alertmanager/types';
|
||||
|
||||
const mapMuteTiming = ({ name, time_intervals }: MuteTimeInterval): SelectableValue<string> => ({
|
||||
value: name,
|
||||
label: name,
|
||||
description: time_intervals.map((interval) => timeIntervalToString(interval)).join(', AND '),
|
||||
});
|
||||
|
||||
/** Provides a MultiSelect with available mute timings for the given alertmanager */
|
||||
const MuteTimingsSelector = ({
|
||||
alertmanager,
|
||||
selectProps,
|
||||
}: BaseAlertmanagerArgs & { selectProps: MultiSelectCommonProps<string> }) => {
|
||||
const { data } = useSelectableMuteTimings({ alertmanager });
|
||||
|
||||
const muteTimingOptions = data?.map((value) => mapMuteTiming(value)) || [];
|
||||
|
||||
return (
|
||||
<MultiSelect
|
||||
aria-label="Mute timings"
|
||||
options={muteTimingOptions}
|
||||
placeholder="Select mute timings..."
|
||||
{...selectProps}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default MuteTimingsSelector;
|
||||
+3
-1
@@ -96,6 +96,8 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"templates": ["slack-template", "custom-email", "provisioned-template", "template with spaces"]
|
||||
"templates": ["slack-template", "custom-email", "provisioned-template", "template with spaces"],
|
||||
"time_intervals": [],
|
||||
"mute_time_intervals": []
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -36,6 +36,7 @@ exports[`should be able to test and save a receiver 2`] = `
|
||||
[
|
||||
{
|
||||
"alertmanager_config": {
|
||||
"mute_time_intervals": [],
|
||||
"receivers": [
|
||||
{
|
||||
"grafana_managed_receiver_configs": [
|
||||
@@ -158,6 +159,7 @@ exports[`should be able to test and save a receiver 2`] = `
|
||||
"provisioned-template",
|
||||
"template with spaces",
|
||||
],
|
||||
"time_intervals": [],
|
||||
},
|
||||
"template_file_provenances": {
|
||||
"provisioned-template": "api",
|
||||
|
||||
@@ -7,7 +7,8 @@ import {
|
||||
ComGithubGrafanaGrafanaPkgApisAlertingNotificationsV0Alpha1TimeInterval,
|
||||
IoK8SApimachineryPkgApisMetaV1ObjectMeta,
|
||||
} from 'app/features/alerting/unified/openapi/timeIntervalsApi.gen';
|
||||
import { BaseAlertmanagerArgs } from 'app/features/alerting/unified/types/hooks';
|
||||
import { BaseAlertmanagerArgs, Skippable } from 'app/features/alerting/unified/types/hooks';
|
||||
import { GRAFANA_RULES_SOURCE_NAME } from 'app/features/alerting/unified/utils/datasource';
|
||||
import { PROVENANCE_ANNOTATION, PROVENANCE_NONE } from 'app/features/alerting/unified/utils/k8s/constants';
|
||||
import { getK8sNamespace, shouldUseK8sApi } from 'app/features/alerting/unified/utils/k8s/utils';
|
||||
import { MuteTimeInterval } from 'app/plugins/datasource/alertmanager/types';
|
||||
@@ -20,7 +21,7 @@ import {
|
||||
updateMuteTimingAction,
|
||||
} from '../../reducers/alertmanager/muteTimings';
|
||||
|
||||
const { useLazyGetAlertmanagerConfigurationQuery } = alertmanagerApi;
|
||||
const { useLazyGetAlertmanagerConfigurationQuery, useGetMuteTimingListQuery } = alertmanagerApi;
|
||||
const {
|
||||
useLazyListNamespacedTimeIntervalQuery,
|
||||
useCreateNamespacedTimeIntervalMutation,
|
||||
@@ -98,20 +99,23 @@ const useGrafanaAlertmanagerIntervals = () =>
|
||||
*
|
||||
* Otherwise, fetches and parses from the alertmanager config API
|
||||
*/
|
||||
export const useMuteTimings = ({ alertmanager }: BaseAlertmanagerArgs) => {
|
||||
export const useMuteTimings = ({ alertmanager, skip }: BaseAlertmanagerArgs & Skippable) => {
|
||||
const useK8sApi = shouldUseK8sApi(alertmanager);
|
||||
|
||||
const [getGrafanaTimeIntervals, intervalsResponse] = useGrafanaAlertmanagerIntervals();
|
||||
const [getAlertmanagerTimeIntervals, configApiResponse] = useAlertmanagerIntervals();
|
||||
|
||||
useEffect(() => {
|
||||
if (skip) {
|
||||
return;
|
||||
}
|
||||
if (useK8sApi) {
|
||||
const namespace = getK8sNamespace();
|
||||
getGrafanaTimeIntervals({ namespace });
|
||||
} else {
|
||||
getAlertmanagerTimeIntervals(alertmanager);
|
||||
}
|
||||
}, [alertmanager, getAlertmanagerTimeIntervals, getGrafanaTimeIntervals, useK8sApi]);
|
||||
}, [alertmanager, getAlertmanagerTimeIntervals, getGrafanaTimeIntervals, skip, useK8sApi]);
|
||||
return useK8sApi ? intervalsResponse : configApiResponse;
|
||||
};
|
||||
|
||||
@@ -294,3 +298,21 @@ export const useValidateMuteTiming = ({ alertmanager }: BaseAlertmanagerArgs) =>
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated This will be deprecated by the K8S API.
|
||||
* Once that is enabled by default, this method should be removed and `useMuteTimings` should always be used instead
|
||||
*/
|
||||
export const useSelectableMuteTimings = ({ alertmanager }: BaseAlertmanagerArgs) => {
|
||||
const useK8sApi = shouldUseK8sApi(alertmanager);
|
||||
const useDeprecatedEndpoint = alertmanager === GRAFANA_RULES_SOURCE_NAME && !useK8sApi;
|
||||
|
||||
/** Fetch from the (to be deprecated) specific endpoint for time-intervals */
|
||||
const deprecatedMuteTimingsResponse = useGetMuteTimingListQuery(undefined, {
|
||||
skip: !useDeprecatedEndpoint,
|
||||
});
|
||||
|
||||
const fetchMuteTimings = useMuteTimings({ alertmanager, skip: useDeprecatedEndpoint });
|
||||
|
||||
return useDeprecatedEndpoint ? deprecatedMuteTimingsResponse : fetchMuteTimings;
|
||||
};
|
||||
|
||||
+10
-22
@@ -1,8 +1,8 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { ReactNode, useMemo, useState } from 'react';
|
||||
import { ReactNode, useState } from 'react';
|
||||
import { useForm, Controller, useFieldArray } from 'react-hook-form';
|
||||
|
||||
import { GrafanaTheme2, SelectableValue } from '@grafana/data';
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
@@ -16,13 +16,14 @@ import {
|
||||
Switch,
|
||||
useStyles2,
|
||||
} from '@grafana/ui';
|
||||
import MuteTimingsSelector from 'app/features/alerting/unified/components/alertmanager-entities/MuteTimingsSelector';
|
||||
import { ContactPointSelector } from 'app/features/alerting/unified/components/notification-policies/ContactPointSelector';
|
||||
import { handleContactPointSelect } from 'app/features/alerting/unified/components/notification-policies/utils';
|
||||
import { MatcherOperator, RouteWithID } from 'app/plugins/datasource/alertmanager/types';
|
||||
|
||||
import { useAlertmanager } from '../../state/AlertmanagerContext';
|
||||
import { FormAmRoute } from '../../types/amroutes';
|
||||
import { matcherFieldOptions, timeIntervalToString } from '../../utils/alertmanager';
|
||||
import { matcherFieldOptions } from '../../utils/alertmanager';
|
||||
import {
|
||||
amRouteToFormAmRoute,
|
||||
commonGroupByOptions,
|
||||
@@ -33,7 +34,6 @@ import {
|
||||
stringToSelectableValue,
|
||||
stringsToSelectableValues,
|
||||
} from '../../utils/amroutes';
|
||||
import { useMuteTimings } from '../mute-timings/useMuteTimings';
|
||||
|
||||
import { PromDurationInput } from './PromDurationInput';
|
||||
import { getFormStyles } from './formStyles';
|
||||
@@ -52,18 +52,6 @@ export const AmRoutesExpandedForm = ({ actionButtons, route, onSubmit, defaults
|
||||
const { selectedAlertmanager } = useAlertmanager();
|
||||
const [groupByOptions, setGroupByOptions] = useState(stringsToSelectableValues(route?.group_by));
|
||||
const emptyMatcher = [{ name: '', operator: MatcherOperator.equal, value: '' }];
|
||||
const { data: muteTimings } = useMuteTimings({ alertmanager: selectedAlertmanager! });
|
||||
|
||||
const muteTimingOptions = useMemo(() => {
|
||||
const muteTimingsOptions: Array<SelectableValue<string>> =
|
||||
muteTimings?.map((value) => ({
|
||||
value: value.name,
|
||||
label: value.name,
|
||||
description: value.time_intervals.map((interval) => timeIntervalToString(interval)).join(', AND '),
|
||||
})) ?? [];
|
||||
|
||||
return muteTimingsOptions;
|
||||
}, [muteTimings]);
|
||||
|
||||
const formAmRoute = {
|
||||
...amRouteToFormAmRoute(route),
|
||||
@@ -285,12 +273,12 @@ export const AmRoutesExpandedForm = ({ actionButtons, route, onSubmit, defaults
|
||||
>
|
||||
<Controller
|
||||
render={({ field: { onChange, ref, ...field } }) => (
|
||||
<MultiSelect
|
||||
aria-label="Mute timings"
|
||||
{...field}
|
||||
className={formStyles.input}
|
||||
onChange={(value) => onChange(mapMultiSelectValueToStrings(value))}
|
||||
options={muteTimingOptions}
|
||||
<MuteTimingsSelector
|
||||
alertmanager={selectedAlertmanager!}
|
||||
selectProps={{
|
||||
...field,
|
||||
onChange: (value) => onChange(mapMultiSelectValueToStrings(value)),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
control={control}
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ export function AlertManagerManualRouting({ alertManager }: AlertManagerManualRo
|
||||
className={styles.collapsableSection}
|
||||
>
|
||||
<Stack direction="column" gap={1}>
|
||||
<MuteTimingFields alertManager={alertManagerName} />
|
||||
<MuteTimingFields alertmanager={alertManagerName} />
|
||||
<RoutingSettings alertManager={alertManagerName} />
|
||||
</Stack>
|
||||
</CollapsableSection>
|
||||
|
||||
+13
-40
@@ -1,65 +1,38 @@
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { Field, MultiSelect, useStyles2 } from '@grafana/ui';
|
||||
import { alertmanagerApi } from 'app/features/alerting/unified/api/alertmanagerApi';
|
||||
import { Field } from '@grafana/ui';
|
||||
import MuteTimingsSelector from 'app/features/alerting/unified/components/alertmanager-entities/MuteTimingsSelector';
|
||||
import { BaseAlertmanagerArgs } from 'app/features/alerting/unified/types/hooks';
|
||||
import { RuleFormValues } from 'app/features/alerting/unified/types/rule-form';
|
||||
import { timeIntervalToString } from 'app/features/alerting/unified/utils/alertmanager';
|
||||
import { mapMultiSelectValueToStrings } from 'app/features/alerting/unified/utils/amroutes';
|
||||
|
||||
import { getFormStyles } from '../../../../notification-policies/formStyles';
|
||||
|
||||
export interface MuteTimingFieldsProps {
|
||||
alertManager: string;
|
||||
}
|
||||
|
||||
export function MuteTimingFields({ alertManager }: MuteTimingFieldsProps) {
|
||||
const styles = useStyles2(getFormStyles);
|
||||
/** Provides a form field for use in simplified routing, for selecting appropriate mute timings */
|
||||
export function MuteTimingFields({ alertmanager }: BaseAlertmanagerArgs) {
|
||||
const {
|
||||
control,
|
||||
formState: { errors },
|
||||
} = useFormContext<RuleFormValues>();
|
||||
|
||||
const muteTimingOptions = useSelectableMuteTimings();
|
||||
return (
|
||||
<Field
|
||||
label="Mute timings"
|
||||
data-testid="am-mute-timing-select"
|
||||
description="Select a mute timing to define when not to send notifications for this alert rule"
|
||||
invalid={!!errors.contactPoints?.[alertManager]?.muteTimeIntervals}
|
||||
invalid={!!errors.contactPoints?.[alertmanager]?.muteTimeIntervals}
|
||||
>
|
||||
<Controller
|
||||
render={({ field: { onChange, ref, ...field } }) => (
|
||||
<MultiSelect
|
||||
aria-label="Mute timings"
|
||||
{...field}
|
||||
className={styles.input}
|
||||
onChange={(value) => onChange(mapMultiSelectValueToStrings(value))}
|
||||
options={muteTimingOptions}
|
||||
placeholder="Select mute timings..."
|
||||
<MuteTimingsSelector
|
||||
alertmanager={alertmanager}
|
||||
selectProps={{
|
||||
...field,
|
||||
onChange: (value) => onChange(mapMultiSelectValueToStrings(value)),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
control={control}
|
||||
name={`contactPoints.${alertManager}.muteTimeIntervals`}
|
||||
name={`contactPoints.${alertmanager}.muteTimeIntervals`}
|
||||
/>
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
|
||||
function useSelectableMuteTimings(): Array<SelectableValue<string>> {
|
||||
const fetchGrafanaMuteTimings = alertmanagerApi.endpoints.getMuteTimingList.useQuery(undefined, {
|
||||
refetchOnFocus: true,
|
||||
refetchOnReconnect: true,
|
||||
selectFromResult: (result) => ({
|
||||
...result,
|
||||
mutetimings: result.data
|
||||
? result.data.map((value) => ({
|
||||
value: value.name,
|
||||
label: value.name,
|
||||
description: value.time_intervals.map((interval) => timeIntervalToString(interval)).join(', AND '),
|
||||
}))
|
||||
: [],
|
||||
}),
|
||||
});
|
||||
return fetchGrafanaMuteTimings.mutetimings;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
import { HttpResponse, http } from 'msw';
|
||||
|
||||
import alertmanagerConfig from 'app/features/alerting/unified/components/contact-points/__mocks__/alertmanager.config.mock.json';
|
||||
import { GrafanaManagedContactPoint } from 'app/plugins/datasource/alertmanager/types';
|
||||
import { GrafanaManagedContactPoint, MuteTimeInterval } from 'app/plugins/datasource/alertmanager/types';
|
||||
|
||||
const defaultReceiversResponse: GrafanaManagedContactPoint[] = JSON.parse(JSON.stringify(alertmanagerConfig))
|
||||
.alertmanager_config.receivers;
|
||||
const defaultReceiversResponse: GrafanaManagedContactPoint[] = alertmanagerConfig.alertmanager_config.receivers;
|
||||
|
||||
const defaultTimeIntervalsResponse: MuteTimeInterval[] = alertmanagerConfig.alertmanager_config.time_intervals;
|
||||
|
||||
const getNotificationReceiversHandler = (response = defaultReceiversResponse) =>
|
||||
http.get('/api/v1/notifications/receivers', () => HttpResponse.json(response));
|
||||
|
||||
const handlers = [getNotificationReceiversHandler()];
|
||||
const getTimeIntervalsHandler = (response = defaultTimeIntervalsResponse) =>
|
||||
http.get('/api/v1/notifications/time-intervals', () => HttpResponse.json(response));
|
||||
|
||||
const handlers = [getNotificationReceiversHandler(), getTimeIntervalsHandler()];
|
||||
|
||||
export default handlers;
|
||||
|
||||
Reference in New Issue
Block a user