diff --git a/public/app/features/alerting/unified/Silences.test.tsx b/public/app/features/alerting/unified/Silences.test.tsx index e478cb8baea..4b5ade80ec8 100644 --- a/public/app/features/alerting/unified/Silences.test.tsx +++ b/public/app/features/alerting/unified/Silences.test.tsx @@ -15,6 +15,8 @@ import { SilenceState } from '../../../plugins/datasource/alertmanager/types'; import Silences from './Silences'; import { createOrUpdateSilence, fetchAlerts, fetchSilences } from './api/alertmanager'; import { grantUserPermissions, mockAlertmanagerAlert, mockDataSource, MockDataSourceSrv, mockSilence } from './mocks'; +import { AlertmanagerProvider } from './state/AlertmanagerContext'; +import { setupDataSources } from './testSetup/datasources'; import { parseMatchers } from './utils/alertmanager'; import { DataSourceType } from './utils/datasource'; @@ -37,7 +39,9 @@ const renderSilences = (location = '/alerting/silences/') => { return render( - + + + ); }; @@ -218,7 +222,7 @@ describe('Silence edit', () => { beforeEach(() => { setUserLogged(true); - setDataSourceSrv(new MockDataSourceSrv(dataSources)); + setupDataSources(dataSources.am); }); it('Should not render createdBy if user is logged in and has a name', async () => { @@ -325,4 +329,32 @@ describe('Silence edit', () => { }, TEST_TIMEOUT ); + + it( + 'silences page should contain alertmanager parameter after creating a silence', + async () => { + const user = userEvent.setup(); + + renderSilences(`${baseUrlPath}?alertmanager=Alertmanager`); + await waitFor(() => expect(ui.editor.durationField.query()).not.toBeNull()); + + await user.type(ui.editor.matcherName.getAll()[0], 'foo'); + await user.type(ui.editor.matcherOperatorSelect.getAll()[0], '='); + await user.type(ui.editor.matcherValue.getAll()[0], 'bar'); + + await user.click(ui.editor.submit.get()); + + await waitFor(() => + expect(mocks.api.createOrUpdateSilence).toHaveBeenCalledWith( + 'Alertmanager', + expect.objectContaining({ + matchers: [{ isEqual: true, isRegex: false, name: 'foo', value: 'bar' }], + }) + ) + ); + + expect(locationService.getSearch().get('alertmanager')).toBe('Alertmanager'); + }, + TEST_TIMEOUT + ); }); diff --git a/public/app/features/alerting/unified/state/AlertmanagerContext.tsx b/public/app/features/alerting/unified/state/AlertmanagerContext.tsx index 05fdc334d94..f2d6e774ce8 100644 --- a/public/app/features/alerting/unified/state/AlertmanagerContext.tsx +++ b/public/app/features/alerting/unified/state/AlertmanagerContext.tsx @@ -1,10 +1,10 @@ import * as React from 'react'; -import { useQueryParams } from 'app/core/hooks/useQueryParams'; import store from 'app/core/store'; import { AlertManagerDataSourceJsonData, AlertManagerImplementation } from 'app/plugins/datasource/alertmanager/types'; import { useAlertManagersByPermission } from '../hooks/useAlertManagerSources'; +import { useURLSearchParams } from '../hooks/useURLSearchParams'; import { ALERTMANAGER_NAME_LOCAL_STORAGE_KEY, ALERTMANAGER_NAME_QUERY_KEY } from '../utils/constants'; import { AlertManagerDataSource, @@ -30,7 +30,7 @@ interface Props extends React.PropsWithChildren { } const AlertmanagerProvider = ({ children, accessType, alertmanagerSourceName }: Props) => { - const [queryParams, updateQueryParams] = useQueryParams(); + const [queryParams, updateQueryParams] = useURLSearchParams(); const allAvailableAlertManagers = useAlertManagersByPermission(accessType); const availableAlertManagers = allAvailableAlertManagers.availableInternalDataSources.concat( allAvailableAlertManagers.availableExternalDataSources @@ -44,7 +44,7 @@ const AlertmanagerProvider = ({ children, accessType, alertmanagerSourceName }: if (selectedAlertManager === GRAFANA_RULES_SOURCE_NAME) { store.delete(ALERTMANAGER_NAME_LOCAL_STORAGE_KEY); - updateQueryParams({ [ALERTMANAGER_NAME_QUERY_KEY]: null }); + updateQueryParams({ [ALERTMANAGER_NAME_QUERY_KEY]: undefined }); } else { store.set(ALERTMANAGER_NAME_LOCAL_STORAGE_KEY, selectedAlertManager); updateQueryParams({ [ALERTMANAGER_NAME_QUERY_KEY]: selectedAlertManager }); @@ -53,10 +53,19 @@ const AlertmanagerProvider = ({ children, accessType, alertmanagerSourceName }: [availableAlertManagers, updateQueryParams] ); - const sourceFromQuery = queryParams[ALERTMANAGER_NAME_QUERY_KEY]; + const sourceFromQuery = queryParams.get(ALERTMANAGER_NAME_QUERY_KEY); const sourceFromStore = store.get(ALERTMANAGER_NAME_LOCAL_STORAGE_KEY); const defaultSource = GRAFANA_RULES_SOURCE_NAME; + // This overrides AM in the store to be in sync with the one in the URL + // When the user uses multiple tabs with different AMs, the store will be changing all the time + // It's safest to always use URLs with alertmanager query param + React.useEffect(() => { + if (sourceFromQuery && sourceFromQuery !== sourceFromStore) { + store.set(ALERTMANAGER_NAME_LOCAL_STORAGE_KEY, sourceFromQuery); + } + }, [sourceFromQuery, sourceFromStore]); + // queryParam > localStorage > default const desiredAlertmanager = alertmanagerSourceName ?? sourceFromQuery ?? sourceFromStore ?? defaultSource; const selectedAlertmanager = isAlertManagerAvailable(availableAlertManagers, desiredAlertmanager) diff --git a/public/app/features/alerting/unified/state/actions.ts b/public/app/features/alerting/unified/state/actions.ts index f6cb7a5e0a3..04dadc73769 100644 --- a/public/app/features/alerting/unified/state/actions.ts +++ b/public/app/features/alerting/unified/state/actions.ts @@ -575,7 +575,7 @@ export const createOrUpdateSilenceAction = createAsyncThunk { await createOrUpdateSilence(alertManagerSourceName, payload); if (exitOnSave) { - locationService.push('/alerting/silences'); + locationService.push(makeAMLink('/alerting/silences', alertManagerSourceName)); } })() ), diff --git a/public/app/features/alerting/unified/utils/misc.ts b/public/app/features/alerting/unified/utils/misc.ts index d3051a0de69..1b0017f3601 100644 --- a/public/app/features/alerting/unified/utils/misc.ts +++ b/public/app/features/alerting/unified/utils/misc.ts @@ -105,7 +105,7 @@ export function makeAMLink(path: string, alertManagerName?: string, options?: UR const search = new URLSearchParams(options); if (alertManagerName) { - search.append(ALERTMANAGER_NAME_QUERY_KEY, alertManagerName); + search.set(ALERTMANAGER_NAME_QUERY_KEY, alertManagerName); } return `${path}?${search.toString()}`; }