diff --git a/public/app/features/alerting/unified/components/alertmanager-entities/MuteTimingsSelector.tsx b/public/app/features/alerting/unified/components/alertmanager-entities/MuteTimingsSelector.tsx new file mode 100644 index 00000000000..9a7f34e3622 --- /dev/null +++ b/public/app/features/alerting/unified/components/alertmanager-entities/MuteTimingsSelector.tsx @@ -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 => ({ + 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 }) => { + const { data } = useSelectableMuteTimings({ alertmanager }); + + const muteTimingOptions = data?.map((value) => mapMuteTiming(value)) || []; + + return ( + + ); +}; + +export default MuteTimingsSelector; diff --git a/public/app/features/alerting/unified/components/contact-points/__mocks__/alertmanager.config.mock.json b/public/app/features/alerting/unified/components/contact-points/__mocks__/alertmanager.config.mock.json index 5038af78a6c..34230f1ba6d 100644 --- a/public/app/features/alerting/unified/components/contact-points/__mocks__/alertmanager.config.mock.json +++ b/public/app/features/alerting/unified/components/contact-points/__mocks__/alertmanager.config.mock.json @@ -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": [] } } diff --git a/public/app/features/alerting/unified/components/contact-points/__snapshots__/NewContactPoint.test.tsx.snap b/public/app/features/alerting/unified/components/contact-points/__snapshots__/NewContactPoint.test.tsx.snap index 4da1caff9e4..db54b874b61 100644 --- a/public/app/features/alerting/unified/components/contact-points/__snapshots__/NewContactPoint.test.tsx.snap +++ b/public/app/features/alerting/unified/components/contact-points/__snapshots__/NewContactPoint.test.tsx.snap @@ -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", diff --git a/public/app/features/alerting/unified/components/mute-timings/useMuteTimings.tsx b/public/app/features/alerting/unified/components/mute-timings/useMuteTimings.tsx index 3f2fb940696..6101e5943a4 100644 --- a/public/app/features/alerting/unified/components/mute-timings/useMuteTimings.tsx +++ b/public/app/features/alerting/unified/components/mute-timings/useMuteTimings.tsx @@ -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; +}; diff --git a/public/app/features/alerting/unified/components/notification-policies/EditNotificationPolicyForm.tsx b/public/app/features/alerting/unified/components/notification-policies/EditNotificationPolicyForm.tsx index d229bcc99fb..8cf320d006a 100644 --- a/public/app/features/alerting/unified/components/notification-policies/EditNotificationPolicyForm.tsx +++ b/public/app/features/alerting/unified/components/notification-policies/EditNotificationPolicyForm.tsx @@ -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> = - 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 > ( - onChange(mapMultiSelectValueToStrings(value))} - options={muteTimingOptions} + onChange(mapMultiSelectValueToStrings(value)), + }} /> )} control={control} 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 884fbb7bf05..521a4c8e7c5 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 @@ -61,7 +61,7 @@ export function AlertManagerManualRouting({ alertManager }: AlertManagerManualRo className={styles.collapsableSection} > - + diff --git a/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/route-settings/MuteTimingFields.tsx b/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/route-settings/MuteTimingFields.tsx index bf73daa8b11..9eaa8e5f7ca 100644 --- a/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/route-settings/MuteTimingFields.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/route-settings/MuteTimingFields.tsx @@ -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(); - const muteTimingOptions = useSelectableMuteTimings(); return ( ( - onChange(mapMultiSelectValueToStrings(value))} - options={muteTimingOptions} - placeholder="Select mute timings..." + onChange(mapMultiSelectValueToStrings(value)), + }} /> )} control={control} - name={`contactPoints.${alertManager}.muteTimeIntervals`} + name={`contactPoints.${alertmanager}.muteTimeIntervals`} /> ); } - -function useSelectableMuteTimings(): Array> { - 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; -} diff --git a/public/app/features/alerting/unified/mocks/server/handlers/notifications.ts b/public/app/features/alerting/unified/mocks/server/handlers/notifications.ts index faac331d6df..6628ef5485f 100644 --- a/public/app/features/alerting/unified/mocks/server/handlers/notifications.ts +++ b/public/app/features/alerting/unified/mocks/server/handlers/notifications.ts @@ -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;