From 3badf73b45037b46ef6fdbd3382b9bf940735883 Mon Sep 17 00:00:00 2001 From: Tom Ratcliffe Date: Tue, 18 Jun 2024 14:24:23 +0100 Subject: [PATCH] Alerting: Fix setting of existing Telegram Chat ID value (#89287) --- .../alerting/unified/Receivers.test.tsx | 65 +++++++++++++------ .../provisioned/config/api/v1/alerts.json | 26 ++++++++ .../components/settings/__mocks__/server.ts | 17 ++++- .../cloud-alertmanager-notifier-types.ts | 2 +- 4 files changed, 85 insertions(+), 25 deletions(-) create mode 100644 public/app/features/alerting/unified/components/settings/__mocks__/api/alertmanager/provisioned/config/api/v1/alerts.json diff --git a/public/app/features/alerting/unified/Receivers.test.tsx b/public/app/features/alerting/unified/Receivers.test.tsx index ac966805830..982b4488eec 100644 --- a/public/app/features/alerting/unified/Receivers.test.tsx +++ b/public/app/features/alerting/unified/Receivers.test.tsx @@ -3,14 +3,13 @@ import { selectOptionInTest } from 'test/helpers/selectOptionInTest'; import { render, screen, waitFor, userEvent } from 'test/test-utils'; import { - EXTERNAL_VANILLA_ALERTMANAGER_UID, + PROVISIONED_MIMIR_ALERTMANAGER_UID, + mockDataSources, 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 { grantUserPermissions } 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'; @@ -19,15 +18,15 @@ import 'core-js/stable/structured-clone'; const server = setupMswServer(); -const mockDataSources = { - [EXTERNAL_VANILLA_ALERTMANAGER_UID]: mockDataSource({ - uid: EXTERNAL_VANILLA_ALERTMANAGER_UID, - name: EXTERNAL_VANILLA_ALERTMANAGER_UID, - type: DataSourceType.Alertmanager, - jsonData: { - implementation: AlertManagerImplementation.prometheus, - }, - }), +const assertSaveWasSuccessful = async () => { + // 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 + return waitFor(() => expect(screen.getByText(/search by name or type/i)).toBeInTheDocument(), { timeout: 2000 }); +}; + +const saveContactPoint = async () => { + const user = userEvent.setup(); + return user.click(await screen.findByRole('button', { name: /save contact point/i })); }; beforeEach(() => { @@ -37,17 +36,22 @@ beforeEach(() => { AccessControlAction.AlertingNotificationsExternalRead, AccessControlAction.AlertingNotificationsExternalWrite, ]); + + setupVanillaAlertmanagerServer(server); + setupDataSources(mockDataSources[PROVISIONED_MIMIR_ALERTMANAGER_UID]); }); it('can save a contact point with a select dropdown', async () => { - setupVanillaAlertmanagerServer(server); - setupDataSources(mockDataSources[EXTERNAL_VANILLA_ALERTMANAGER_UID]); - const user = userEvent.setup(); render(, { historyOptions: { - initialEntries: [`/alerting/notifications/receivers/new?alertmanager=${EXTERNAL_VANILLA_ALERTMANAGER_UID}`], + initialEntries: [ + { + pathname: `/alerting/notifications/receivers/new`, + search: `?alertmanager=${PROVISIONED_MIMIR_ALERTMANAGER_UID}`, + }, + ], }, }); @@ -66,9 +70,28 @@ it('can save a contact point with a select dropdown', async () => { await user.type(botToken, 'sometoken'); await user.type(chatId, '-123'); - await user.click(await screen.findByRole('button', { name: /save contact point/i })); + await saveContactPoint(); - // 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 }); + await assertSaveWasSuccessful(); +}); + +it('can save existing Telegram contact point', async () => { + render(, { + historyOptions: { + initialEntries: [ + { + pathname: `/alerting/notifications/receivers/Telegram/edit`, + search: `?alertmanager=${PROVISIONED_MIMIR_ALERTMANAGER_UID}`, + }, + ], + }, + }); + + // Here, we're implicitly testing that our parsing of an existing Telegram integration works correctly + // Our mock server will reject a request if we've sent the Chat ID as `0`, + // so opening and trying to save an existing Telegram integration should + // trigger this error if it regresses + await saveContactPoint(); + + await assertSaveWasSuccessful(); }); diff --git a/public/app/features/alerting/unified/components/settings/__mocks__/api/alertmanager/provisioned/config/api/v1/alerts.json b/public/app/features/alerting/unified/components/settings/__mocks__/api/alertmanager/provisioned/config/api/v1/alerts.json new file mode 100644 index 00000000000..7d3a346d87d --- /dev/null +++ b/public/app/features/alerting/unified/components/settings/__mocks__/api/alertmanager/provisioned/config/api/v1/alerts.json @@ -0,0 +1,26 @@ +{ + "template_files": {}, + "alertmanager_config": { + "global": {}, + "receivers": [ + { + "name": "default" + }, + { + "name": "Telegram", + "telegram_configs": [ + { + "bot_token": "abc", + "chat_id": -123, + "disable_notifications": false, + "parse_mode": "MarkdownV2", + "send_resolved": true + } + ] + } + ], + "route": { + "receiver": "default" + } + } +} diff --git a/public/app/features/alerting/unified/components/settings/__mocks__/server.ts b/public/app/features/alerting/unified/components/settings/__mocks__/server.ts index 41ead147008..bb34baac74b 100644 --- a/public/app/features/alerting/unified/components/settings/__mocks__/server.ts +++ b/public/app/features/alerting/unified/components/settings/__mocks__/server.ts @@ -16,6 +16,7 @@ import { DataSourceType } from '../../../utils/datasource'; import internalAlertmanagerConfig from './api/alertmanager/grafana/config/api/v1/alerts.json'; import history from './api/alertmanager/grafana/config/history.json'; +import cloudAlertmanagerConfig from './api/alertmanager/provisioned/config/api/v1/alerts.json'; import vanillaAlertmanagerConfig from './api/alertmanager/vanilla prometheus/api/v2/status.json'; import datasources from './api/datasources.json'; import admin_config from './api/v1/ngalert/admin_config.json'; @@ -37,7 +38,7 @@ const mocks = { getAllDataSources: jest.mocked(config.getAllDataSources), }; -const mockDataSources = { +export const mockDataSources = { [EXTERNAL_VANILLA_ALERTMANAGER_UID]: mockDataSource({ uid: EXTERNAL_VANILLA_ALERTMANAGER_UID, name: EXTERNAL_VANILLA_ALERTMANAGER_UID, @@ -95,7 +96,12 @@ const createAlertmanagerConfigurationHandlers = () => { }; return [ - http.get(`/api/alertmanager/:name/config/api/v1/alerts`, () => HttpResponse.json(internalAlertmanagerConfig)), + http.get<{ name: string }>(`/api/alertmanager/:name/config/api/v1/alerts`, ({ params }) => { + if (params.name === 'grafana') { + return HttpResponse.json(internalAlertmanagerConfig); + } + return HttpResponse.json(cloudAlertmanagerConfig); + }), http.post(`/api/alertmanager/:name/config/api/v1/alerts`, async ({ request }) => { await delay(1000); // simulate some time @@ -108,7 +114,12 @@ const createAlertmanagerConfigurationHandlers = () => { return false; } - return (receiver.telegram_configs || []).some((config) => typeof config.parse_mode === 'object'); + const invalidParseMode = (receiver.telegram_configs || []).some( + (config) => typeof config.parse_mode === 'object' + ); + const invalidChatId = (receiver.telegram_configs || []).some((config) => Number(config.chat_id) >= 0); + + return invalidParseMode || invalidChatId; }); if (invalidConfig) { diff --git a/public/app/features/alerting/unified/utils/cloud-alertmanager-notifier-types.ts b/public/app/features/alerting/unified/utils/cloud-alertmanager-notifier-types.ts index 06ee6893057..0ff45bb14e9 100644 --- a/public/app/features/alerting/unified/utils/cloud-alertmanager-notifier-types.ts +++ b/public/app/features/alerting/unified/utils/cloud-alertmanager-notifier-types.ts @@ -410,7 +410,7 @@ export const cloudNotifierTypes: Array> = [ }), option('chat_id', 'Chat ID', 'ID of the chat where to send the messages', { required: true, - setValueAs: (value) => (typeof value === 'string' ? parseInt(value, 10) : 0), + setValueAs: (value) => (typeof value === 'string' ? parseInt(value, 10) : value), }), option('message', 'Message', 'Message template', { placeholder: '{{ template "webex.default.message" .}}',