Alerting: Fix alertmanager query param when returning to silences list (#80021)

* Add alertmanager query param when returning to silences list. Sync query and storage alertmanager param

* Remove unused imports
This commit is contained in:
Konrad Lalik 2024-01-08 10:16:37 +01:00 committed by GitHub
parent 9cdd519e53
commit 6fd0ae0474
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 49 additions and 8 deletions

View File

@ -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(
<TestProvider>
<Silences />
<AlertmanagerProvider accessType="instance">
<Silences />
</AlertmanagerProvider>
</TestProvider>
);
};
@ -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
);
});

View File

@ -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)

View File

@ -575,7 +575,7 @@ export const createOrUpdateSilenceAction = createAsyncThunk<void, UpdateSilenceA
(async () => {
await createOrUpdateSilence(alertManagerSourceName, payload);
if (exitOnSave) {
locationService.push('/alerting/silences');
locationService.push(makeAMLink('/alerting/silences', alertManagerSourceName));
}
})()
),

View File

@ -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()}`;
}