diff --git a/public/app/features/alerting/unified/Silences.test.tsx b/public/app/features/alerting/unified/Silences.test.tsx index 0ac440359d5..0d9d0a44f1d 100644 --- a/public/app/features/alerting/unified/Silences.test.tsx +++ b/public/app/features/alerting/unified/Silences.test.tsx @@ -28,7 +28,7 @@ const renderSilences = (location = '/alerting/silences/') => { , { - routerOptions: { + historyOptions: { initialEntries: [location], }, } @@ -156,10 +156,11 @@ describe('Silences', () => { it( 'filters silences by matchers', async () => { + const user = userEvent.setup(); renderSilences(); const queryBar = await ui.queryBar.find(); - await userEvent.type(queryBar, 'foo=bar'); + await user.type(queryBar, 'foo=bar'); await waitFor(() => expect(ui.silenceRow.getAll()).toHaveLength(2)); }, @@ -238,6 +239,7 @@ describe('Silence create/edit', () => { it( 'creates a new silence', async () => { + const user = userEvent.setup(); renderSilences(baseUrlPath); expect(await ui.editor.durationField.find()).toBeInTheDocument(); @@ -249,8 +251,8 @@ describe('Silence create/edit', () => { const startDateString = dateTime(start).format('YYYY-MM-DD'); const endDateString = dateTime(end).format('YYYY-MM-DD'); - await userEvent.clear(ui.editor.durationInput.get()); - await userEvent.type(ui.editor.durationInput.get(), '1d'); + await user.clear(ui.editor.durationInput.get()); + await user.type(ui.editor.durationInput.get(), '1d'); await waitFor(() => expect(ui.editor.durationInput.query()).toHaveValue('1d')); await waitFor(() => expect(ui.editor.timeRange.get()).toHaveTextContent(startDateString)); @@ -267,7 +269,7 @@ describe('Silence create/edit', () => { await addAdditionalMatcher(); await enterSilenceLabel(3, 'env', MatcherOperator.notRegex, 'dev|staging'); - await userEvent.click(ui.editor.submit.get()); + await user.click(ui.editor.submit.get()); expect(await ui.notExpiredTable.find()).toBeInTheDocument(); diff --git a/public/app/features/alerting/unified/components/silences/SilencesEditor.tsx b/public/app/features/alerting/unified/components/silences/SilencesEditor.tsx index 4a87736a46f..1bad2e84fa6 100644 --- a/public/app/features/alerting/unified/components/silences/SilencesEditor.tsx +++ b/public/app/features/alerting/unified/components/silences/SilencesEditor.tsx @@ -2,7 +2,6 @@ import { css, cx } from '@emotion/css'; import { isEqual, pickBy } from 'lodash'; import React, { useEffect, useMemo, useState } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; -import { useHistory } from 'react-router'; import { useDebounce } from 'react-use'; import { @@ -14,8 +13,18 @@ import { isValidDate, parseDuration, } from '@grafana/data'; -import { config } from '@grafana/runtime'; -import { Button, Field, FieldSet, Input, LinkButton, TextArea, useStyles2 } from '@grafana/ui'; +import { config, isFetchError, locationService } from '@grafana/runtime'; +import { + Alert, + Button, + Field, + FieldSet, + Input, + LinkButton, + LoadingPlaceholder, + TextArea, + useStyles2, +} from '@grafana/ui'; import { alertSilencesApi } from 'app/features/alerting/unified/api/alertSilencesApi'; import { getDatasourceAPIUid } from 'app/features/alerting/unified/utils/datasource'; import { Matcher, MatcherOperator, Silence, SilenceCreatePayload } from 'app/plugins/datasource/alertmanager/types'; @@ -96,8 +105,9 @@ const getDefaultFormValues = (searchParams: URLSearchParams, silence?: Silence): }; export const SilencesEditor = ({ silenceId, alertManagerSourceName }: Props) => { - const history = useHistory(); - const [getSilence, { data: silence, isLoading: getSilenceIsLoading }] = + // Use a lazy query to fetch the Silence info, as we may not always require this + // (e.g. if creating a new one from scratch, we don't need to fetch anything) + const [getSilence, { data: silence, isLoading: getSilenceIsLoading, error: errorGettingExistingSilence }] = alertSilencesApi.endpoints.getSilence.useLazyQuery(); const [createSilence, { isLoading }] = alertSilencesApi.endpoints.createSilence.useMutation(); const [urlSearchParams] = useURLSearchParams(); @@ -129,7 +139,7 @@ export const SilencesEditor = ({ silenceId, alertManagerSourceName }: Props) => await createSilence({ datasourceUid: getDatasourceAPIUid(alertManagerSourceName), payload }) .unwrap() .then(() => { - history.push(makeAMLink('/alerting/silences', alertManagerSourceName)); + locationService.push(makeAMLink('/alerting/silences', alertManagerSourceName)); }); }; @@ -139,8 +149,10 @@ export const SilencesEditor = ({ silenceId, alertManagerSourceName }: Props) => const matcherFields = watch('matchers'); useEffect(() => { - // Allows the form to correctly initialise when an existing silence is fetch from the backend - reset(getDefaultFormValues(urlSearchParams, silence)); + if (silence) { + // Allows the form to correctly initialise when an existing silence is fetch from the backend + reset(getDefaultFormValues(urlSearchParams, silence)); + } }, [reset, silence, urlSearchParams]); useEffect(() => { @@ -190,13 +202,20 @@ export const SilencesEditor = ({ silenceId, alertManagerSourceName }: Props) => const userLogged = Boolean(config.bootData.user.isSignedIn && config.bootData.user.name); if (getSilenceIsLoading) { - return null; + return ; + } + + const existingSilenceNotFound = + isFetchError(errorGettingExistingSilence) && errorGettingExistingSilence.status === 404; + + if (existingSilenceNotFound) { + return ; } return (
-
+
{ const { data: alertManagerAlerts = [], isLoading: amAlertsIsLoading } = alertmanagerApi.endpoints.getAlertmanagerAlerts.useQuery( { amSourceName: alertManagerSourceName, filter: { silenced: true, active: true, inhibited: true } }, - { pollingInterval: SILENCES_POLL_INTERVAL_MS } + API_QUERY_OPTIONS ); const { @@ -49,9 +51,7 @@ const SilencesTable = ({ alertManagerSourceName }: Props) => { error, } = alertSilencesApi.endpoints.getSilences.useQuery( { datasourceUid: getDatasourceAPIUid(alertManagerSourceName) }, - { - pollingInterval: SILENCES_POLL_INTERVAL_MS, - } + API_QUERY_OPTIONS ); const { currentData: amFeatures } = featureDiscoveryApi.useDiscoverAmFeaturesQuery( @@ -60,7 +60,7 @@ const SilencesTable = ({ alertManagerSourceName }: Props) => { ); const mimirLazyInitError = - isFetchError(error) && error?.message?.includes('the Alertmanager is not configured') && amFeatures?.lazyConfigInit; + stringifyErrorLike(error).includes('the Alertmanager is not configured') && amFeatures?.lazyConfigInit; const styles = useStyles2(getStyles); const [queryParams] = useQueryParams(); @@ -97,7 +97,7 @@ const SilencesTable = ({ alertManagerSourceName }: Props) => { }, [filteredSilencesExpired, alertManagerAlerts]); if (isLoading || amAlertsIsLoading) { - return null; + return ; } if (mimirLazyInitError) { @@ -218,9 +218,6 @@ const useFilteredSilences = (silences: Silence[], expired = false) => { }; const getStyles = (theme: GrafanaTheme2) => ({ - addNewSilence: css({ - margin: theme.spacing(2, 0), - }), callout: css({ backgroundColor: theme.colors.background.secondary, borderTop: `3px solid ${theme.colors.info.border}`,