mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Fix saving telegram contact point to Cloud AM config (#89182)
* Add test for saving telegram contact point to AM config * Fix default value for select options in AM config
This commit is contained in:
parent
84590901e5
commit
26233e98a8
74
public/app/features/alerting/unified/Receivers.test.tsx
Normal file
74
public/app/features/alerting/unified/Receivers.test.tsx
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { selectOptionInTest } from 'test/helpers/selectOptionInTest';
|
||||||
|
import { render, screen, waitFor, userEvent } from 'test/test-utils';
|
||||||
|
|
||||||
|
import {
|
||||||
|
EXTERNAL_VANILLA_ALERTMANAGER_UID,
|
||||||
|
setupVanillaAlertmanagerServer,
|
||||||
|
} from 'app/features/alerting/unified/components/settings/__mocks__/server';
|
||||||
|
import { setupMswServer } from 'app/features/alerting/unified/mockApi';
|
||||||
|
import { grantUserPermissions, mockDataSource } from 'app/features/alerting/unified/mocks';
|
||||||
|
import { setupDataSources } from 'app/features/alerting/unified/testSetup/datasources';
|
||||||
|
import { DataSourceType } from 'app/features/alerting/unified/utils/datasource';
|
||||||
|
import { AlertManagerDataSourceJsonData, AlertManagerImplementation } from 'app/plugins/datasource/alertmanager/types';
|
||||||
|
import { AccessControlAction } from 'app/types';
|
||||||
|
|
||||||
|
import ContactPoints from './Receivers';
|
||||||
|
|
||||||
|
import 'core-js/stable/structured-clone';
|
||||||
|
|
||||||
|
const server = setupMswServer();
|
||||||
|
|
||||||
|
const mockDataSources = {
|
||||||
|
[EXTERNAL_VANILLA_ALERTMANAGER_UID]: mockDataSource<AlertManagerDataSourceJsonData>({
|
||||||
|
uid: EXTERNAL_VANILLA_ALERTMANAGER_UID,
|
||||||
|
name: EXTERNAL_VANILLA_ALERTMANAGER_UID,
|
||||||
|
type: DataSourceType.Alertmanager,
|
||||||
|
jsonData: {
|
||||||
|
implementation: AlertManagerImplementation.prometheus,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
grantUserPermissions([
|
||||||
|
AccessControlAction.AlertingNotificationsRead,
|
||||||
|
AccessControlAction.AlertingNotificationsWrite,
|
||||||
|
AccessControlAction.AlertingNotificationsExternalRead,
|
||||||
|
AccessControlAction.AlertingNotificationsExternalWrite,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can save a contact point with a select dropdown', async () => {
|
||||||
|
setupVanillaAlertmanagerServer(server);
|
||||||
|
setupDataSources(mockDataSources[EXTERNAL_VANILLA_ALERTMANAGER_UID]);
|
||||||
|
|
||||||
|
const user = userEvent.setup();
|
||||||
|
|
||||||
|
render(<ContactPoints />, {
|
||||||
|
historyOptions: {
|
||||||
|
initialEntries: [`/alerting/notifications/receivers/new?alertmanager=${EXTERNAL_VANILLA_ALERTMANAGER_UID}`],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fill out contact point name
|
||||||
|
const contactPointName = await screen.findByPlaceholderText(/name/i);
|
||||||
|
await user.type(contactPointName, 'contact point with select');
|
||||||
|
|
||||||
|
// Select Telegram option (this is we expect the form to contain a dropdown)
|
||||||
|
const integrationDropdown = screen.getByLabelText(/integration/i);
|
||||||
|
await selectOptionInTest(integrationDropdown, /telegram/i);
|
||||||
|
|
||||||
|
// Fill out basic fields necessary for contact point to be saved
|
||||||
|
const botToken = await screen.findByLabelText(/bot token/i);
|
||||||
|
const chatId = await screen.findByLabelText(/chat id/i);
|
||||||
|
|
||||||
|
await user.type(botToken, 'sometoken');
|
||||||
|
await user.type(chatId, '-123');
|
||||||
|
|
||||||
|
await user.click(await screen.findByRole('button', { name: /save contact point/i }));
|
||||||
|
|
||||||
|
// TODO: Have a better way to assert that the contact point was saved. This is instead asserting on some
|
||||||
|
// text that's present on the list page, as there's a lot of overlap in text between the form and the list page
|
||||||
|
await waitFor(() => expect(screen.getByText(/search by name or type/i)).toBeInTheDocument(), { timeout: 2000 });
|
||||||
|
});
|
@ -3,7 +3,6 @@ import { Route, Switch } from 'react-router-dom';
|
|||||||
|
|
||||||
import { withErrorBoundary } from '@grafana/ui';
|
import { withErrorBoundary } from '@grafana/ui';
|
||||||
import { SafeDynamicImport } from 'app/core/components/DynamicImports/SafeDynamicImport';
|
import { SafeDynamicImport } from 'app/core/components/DynamicImports/SafeDynamicImport';
|
||||||
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
|
||||||
|
|
||||||
import { AlertmanagerPageWrapper } from './components/AlertingPageWrapper';
|
import { AlertmanagerPageWrapper } from './components/AlertingPageWrapper';
|
||||||
|
|
||||||
@ -12,7 +11,7 @@ const EditContactPoint = SafeDynamicImport(() => import('./components/contact-po
|
|||||||
const NewContactPoint = SafeDynamicImport(() => import('./components/contact-points/NewContactPoint'));
|
const NewContactPoint = SafeDynamicImport(() => import('./components/contact-points/NewContactPoint'));
|
||||||
const GlobalConfig = SafeDynamicImport(() => import('./components/contact-points/components/GlobalConfig'));
|
const GlobalConfig = SafeDynamicImport(() => import('./components/contact-points/components/GlobalConfig'));
|
||||||
|
|
||||||
const ContactPoints = (_props: GrafanaRouteComponentProps): JSX.Element => (
|
const ContactPoints = (): JSX.Element => (
|
||||||
<AlertmanagerPageWrapper navId="receivers" accessType="notification">
|
<AlertmanagerPageWrapper navId="receivers" accessType="notification">
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route exact={true} path="/alerting/notifications" component={ContactPointsV2} />
|
<Route exact={true} path="/alerting/notifications" component={ContactPointsV2} />
|
||||||
|
@ -161,7 +161,7 @@ const OptionInput: FC<Props & { id: string; pathIndex?: string }> = ({
|
|||||||
)}
|
)}
|
||||||
control={control}
|
control={control}
|
||||||
name={name}
|
name={name}
|
||||||
defaultValue={option.defaultValue}
|
defaultValue={option.defaultValue?.value}
|
||||||
rules={{
|
rules={{
|
||||||
validate: {
|
validate: {
|
||||||
customValidator: (v) => (customValidator ? customValidator(v) : true),
|
customValidator: (v) => (customValidator ? customValidator(v) : true),
|
||||||
|
@ -2,7 +2,13 @@ import { delay, http, HttpResponse } from 'msw';
|
|||||||
import { SetupServerApi } from 'msw/lib/node';
|
import { SetupServerApi } from 'msw/lib/node';
|
||||||
|
|
||||||
import { setDataSourceSrv } from '@grafana/runtime';
|
import { setDataSourceSrv } from '@grafana/runtime';
|
||||||
import { AlertManagerDataSourceJsonData, AlertManagerImplementation } from 'app/plugins/datasource/alertmanager/types';
|
import {
|
||||||
|
AlertManagerCortexConfig,
|
||||||
|
AlertManagerDataSourceJsonData,
|
||||||
|
AlertManagerImplementation,
|
||||||
|
AlertmanagerReceiver,
|
||||||
|
Receiver,
|
||||||
|
} from 'app/plugins/datasource/alertmanager/types';
|
||||||
|
|
||||||
import { mockDataSource, MockDataSourceSrv } from '../../../mocks';
|
import { mockDataSource, MockDataSourceSrv } from '../../../mocks';
|
||||||
import * as config from '../../../utils/config';
|
import * as config from '../../../utils/config';
|
||||||
@ -70,7 +76,7 @@ export function setupVanillaAlertmanagerServer(server: SetupServerApi) {
|
|||||||
|
|
||||||
server.use(
|
server.use(
|
||||||
createVanillaAlertmanagerConfigurationHandler(EXTERNAL_VANILLA_ALERTMANAGER_UID),
|
createVanillaAlertmanagerConfigurationHandler(EXTERNAL_VANILLA_ALERTMANAGER_UID),
|
||||||
...createAlertmanagerConfigurationHandlers(PROVISIONED_MIMIR_ALERTMANAGER_UID)
|
...createAlertmanagerConfigurationHandlers()
|
||||||
);
|
);
|
||||||
|
|
||||||
return server;
|
return server;
|
||||||
@ -82,11 +88,33 @@ const createExternalAlertmanagersHandler = () => {
|
|||||||
return http.get('/api/v1/ngalert/alertmanagers', () => HttpResponse.json(alertmanagers));
|
return http.get('/api/v1/ngalert/alertmanagers', () => HttpResponse.json(alertmanagers));
|
||||||
};
|
};
|
||||||
|
|
||||||
const createAlertmanagerConfigurationHandlers = (name = 'grafana') => {
|
const createAlertmanagerConfigurationHandlers = () => {
|
||||||
|
// Dirty check to type guard against us having a non-Grafana managed receiver
|
||||||
|
const contactPointIsAMReceiver = (receiver: Receiver): receiver is AlertmanagerReceiver => {
|
||||||
|
return !receiver.grafana_managed_receiver_configs;
|
||||||
|
};
|
||||||
|
|
||||||
return [
|
return [
|
||||||
http.get(`/api/alertmanager/${name}/config/api/v1/alerts`, () => HttpResponse.json(internalAlertmanagerConfig)),
|
http.get(`/api/alertmanager/:name/config/api/v1/alerts`, () => HttpResponse.json(internalAlertmanagerConfig)),
|
||||||
http.post(`/api/alertmanager/${name}/config/api/v1/alerts`, async () => {
|
http.post<never, AlertManagerCortexConfig>(`/api/alertmanager/:name/config/api/v1/alerts`, async ({ request }) => {
|
||||||
await delay(1000); // simulate some time
|
await delay(1000); // simulate some time
|
||||||
|
|
||||||
|
// Specifically mock and check for the case of an invalid telegram config,
|
||||||
|
// and return a 400 error in this case
|
||||||
|
// This is to test against us accidentally sending a `{label, value}` object instead of a string
|
||||||
|
const body = await request.json();
|
||||||
|
const invalidConfig = body.alertmanager_config.receivers?.some((receiver) => {
|
||||||
|
if (!contactPointIsAMReceiver(receiver)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (receiver.telegram_configs || []).some((config) => typeof config.parse_mode === 'object');
|
||||||
|
});
|
||||||
|
|
||||||
|
if (invalidConfig) {
|
||||||
|
return HttpResponse.json({ message: 'bad request data' }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
return HttpResponse.json({ message: 'configuration created' });
|
return HttpResponse.json({ message: 'configuration created' });
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
Loading…
Reference in New Issue
Block a user