From e234d098e9fb08852190daa79232defdd2606308 Mon Sep 17 00:00:00 2001 From: Sonia Aguilar <33540275+soniaAguilarPeiron@users.noreply.github.com> Date: Tue, 9 Apr 2024 09:02:44 +0200 Subject: [PATCH] Alerting: Get oncall metada only when we have alert manager configuration data (#85622) * Get oncall metada from url in settings only when we have alert manager configuration * Add test in useContactPointsWithStatus hook * Fix tests --- .../contact-points/ContactPoints.test.tsx | 12 +- .../__mocks__/alertmanager.config.mock.json | 13 ++ .../NewContactPoint.test.tsx.snap | 13 ++ .../useContactPoints.test.tsx.snap | 197 ++++++++++++++++++ .../contact-points/useContactPoints.test.tsx | 50 ++++- .../components/contact-points/utils.ts | 7 +- .../useReceiversMetadata.ts | 9 +- .../app/features/alerting/unified/mockApi.ts | 4 + 8 files changed, 293 insertions(+), 12 deletions(-) diff --git a/public/app/features/alerting/unified/components/contact-points/ContactPoints.test.tsx b/public/app/features/alerting/unified/components/contact-points/ContactPoints.test.tsx index 325867253b8..7ef6152ab2b 100644 --- a/public/app/features/alerting/unified/components/contact-points/ContactPoints.test.tsx +++ b/public/app/features/alerting/unified/components/contact-points/ContactPoints.test.tsx @@ -65,7 +65,7 @@ describe('contact points', () => { }); expect(screen.getByText('grafana-default-email')).toBeInTheDocument(); - expect(screen.getAllByTestId('contact-point')).toHaveLength(4); + expect(screen.getAllByTestId('contact-point')).toHaveLength(5); // check for available actions – our mock 4 contact points, 1 of them is provisioned expect(screen.getByRole('link', { name: 'add contact point' })).toBeInTheDocument(); @@ -73,20 +73,20 @@ describe('contact points', () => { // 2 of them are unused by routes in the mock response const unusedBadge = screen.getAllByLabelText('unused'); - expect(unusedBadge).toHaveLength(2); + expect(unusedBadge).toHaveLength(3); const viewProvisioned = screen.getByRole('link', { name: 'view-action' }); expect(viewProvisioned).toBeInTheDocument(); expect(viewProvisioned).not.toBeDisabled(); const editButtons = screen.getAllByRole('link', { name: 'edit-action' }); - expect(editButtons).toHaveLength(3); + expect(editButtons).toHaveLength(4); editButtons.forEach((button) => { expect(button).not.toBeDisabled(); }); const moreActionsButtons = screen.getAllByRole('button', { name: 'more-actions' }); - expect(moreActionsButtons).toHaveLength(4); + expect(moreActionsButtons).toHaveLength(5); moreActionsButtons.forEach((button) => { expect(button).not.toBeDisabled(); }); @@ -115,11 +115,11 @@ describe('contact points', () => { // there should be view buttons though const viewButtons = screen.getAllByRole('link', { name: 'view-action' }); - expect(viewButtons).toHaveLength(4); + expect(viewButtons).toHaveLength(5); // delete should be disabled in the "more" actions const moreButtons = screen.queryAllByRole('button', { name: 'more-actions' }); - expect(moreButtons).toHaveLength(4); + expect(moreButtons).toHaveLength(5); // check if all of the delete buttons are disabled for await (const button of moreButtons) { diff --git a/public/app/features/alerting/unified/components/contact-points/__mocks__/alertmanager.config.mock.json b/public/app/features/alerting/unified/components/contact-points/__mocks__/alertmanager.config.mock.json index 5bfacc2cdcc..bc99b0531b9 100644 --- a/public/app/features/alerting/unified/components/contact-points/__mocks__/alertmanager.config.mock.json +++ b/public/app/features/alerting/unified/components/contact-points/__mocks__/alertmanager.config.mock.json @@ -73,6 +73,19 @@ "secureFields": { "token": true } } ] + }, + { + "name": "OnCall Conctact point", + "grafana_managed_receiver_configs": [ + { + "name": "Oncall-integration", + "type": "oncall", + "settings": { + "url": "https://oncall-endpoint.example.com" + }, + "disableResolveMessage": false + } + ] } ] } diff --git a/public/app/features/alerting/unified/components/contact-points/__snapshots__/NewContactPoint.test.tsx.snap b/public/app/features/alerting/unified/components/contact-points/__snapshots__/NewContactPoint.test.tsx.snap index 028e88fcac9..8739185052a 100644 --- a/public/app/features/alerting/unified/components/contact-points/__snapshots__/NewContactPoint.test.tsx.snap +++ b/public/app/features/alerting/unified/components/contact-points/__snapshots__/NewContactPoint.test.tsx.snap @@ -115,6 +115,19 @@ exports[`should be able to test and save a receiver 2`] = ` ], "name": "Slack with multiple channels", }, + { + "grafana_managed_receiver_configs": [ + { + "disableResolveMessage": false, + "name": "Oncall-integration", + "settings": { + "url": "https://oncall-endpoint.example.com", + }, + "type": "oncall", + }, + ], + "name": "OnCall Conctact point", + }, { "grafana_managed_receiver_configs": [ { diff --git a/public/app/features/alerting/unified/components/contact-points/__snapshots__/useContactPoints.test.tsx.snap b/public/app/features/alerting/unified/components/contact-points/__snapshots__/useContactPoints.test.tsx.snap index ea4fc89ca90..2f94d12cef6 100644 --- a/public/app/features/alerting/unified/components/contact-points/__snapshots__/useContactPoints.test.tsx.snap +++ b/public/app/features/alerting/unified/components/contact-points/__snapshots__/useContactPoints.test.tsx.snap @@ -60,6 +60,32 @@ exports[`useContactPoints should return contact points with status 1`] = ` "name": "lotsa-emails", "numberOfPolicies": 0, }, + { + "grafana_managed_receiver_configs": [ + { + "disableResolveMessage": false, + "name": "Oncall-integration", + "settings": { + "url": "https://oncall-endpoint.example.com", + }, + "type": "oncall", + Symbol(receiver_status): undefined, + Symbol(receiver_metadata): { + "description": undefined, + "name": "Oncall", + }, + Symbol(receiver_plugin_metadata): { + "description": "grafana-integration", + "externalUrl": "/a/grafana-oncall-app/integrations/ABC123", + "icon": "public/img/alerting/oncall_logo.svg", + "title": "Grafana OnCall", + "warning": undefined, + }, + }, + ], + "name": "OnCall Conctact point", + "numberOfPolicies": 0, + }, { "grafana_managed_receiver_configs": [ { @@ -147,3 +173,174 @@ exports[`useContactPoints should return contact points with status 1`] = ` "refetchReceivers": [Function], } `; + +exports[`useContactPoints when having oncall plugin installed and no alert manager config data should return contact points with oncall metadata 1`] = ` +{ + "contactPoints": [ + { + "grafana_managed_receiver_configs": [ + { + "disableResolveMessage": false, + "name": "grafana-default-email", + "secureFields": {}, + "settings": { + "addresses": "gilles.demey@grafana.com", + "singleEmail": false, + }, + "type": "email", + "uid": "xeKQrBrnk", + Symbol(receiver_status): { + "lastNotifyAttempt": "2023-07-02T21:35:34.841+02:00", + "lastNotifyAttemptDuration": "1ms", + "lastNotifyAttemptError": "failed to send notification to email addresses: gilles.demey@grafana.com: dial tcp 192.168.1.21:1025: connect: connection refused", + "name": "email", + "sendResolved": true, + }, + Symbol(receiver_metadata): { + "description": "Sends notifications using Grafana server configured SMTP settings", + "name": "Email", + }, + Symbol(receiver_plugin_metadata): undefined, + }, + ], + "name": "grafana-default-email", + "numberOfPolicies": undefined, + }, + { + "grafana_managed_receiver_configs": [ + { + "disableResolveMessage": false, + "name": "lotsa-emails", + "secureFields": {}, + "settings": { + "addresses": "gilles.demey+1@grafana.com, gilles.demey+2@grafana.com, gilles.demey+3@grafana.com, gilles.demey+4@grafana.com", + "singleEmail": false, + }, + "type": "email", + "uid": "af306c96-35a2-4d6e-908a-4993e245dbb2", + Symbol(receiver_status): { + "lastNotifyAttempt": "", + "lastNotifyAttemptDuration": "", + "name": "email", + "sendResolved": true, + }, + Symbol(receiver_metadata): { + "description": "Sends notifications using Grafana server configured SMTP settings", + "name": "Email", + }, + Symbol(receiver_plugin_metadata): undefined, + }, + ], + "name": "lotsa-emails", + "numberOfPolicies": undefined, + }, + { + "grafana_managed_receiver_configs": [ + { + "disableResolveMessage": false, + "name": "Oncall-integration", + "settings": { + "url": "https://oncall-endpoint.example.com", + }, + "type": "oncall", + Symbol(receiver_status): undefined, + Symbol(receiver_metadata): { + "description": undefined, + "name": "Oncall", + }, + Symbol(receiver_plugin_metadata): { + "icon": "public/img/alerting/oncall_logo.svg", + "title": "Grafana OnCall", + }, + }, + ], + "name": "OnCall Conctact point", + "numberOfPolicies": undefined, + }, + { + "grafana_managed_receiver_configs": [ + { + "disableResolveMessage": false, + "name": "provisioned-contact-point", + "provenance": "api", + "secureFields": {}, + "settings": { + "addresses": "gilles.demey@grafana.com", + "singleEmail": false, + }, + "type": "email", + "uid": "s8SdCVjnk", + Symbol(receiver_status): { + "lastNotifyAttempt": "", + "lastNotifyAttemptDuration": "", + "name": "email", + "sendResolved": true, + }, + Symbol(receiver_metadata): { + "description": "Sends notifications using Grafana server configured SMTP settings", + "name": "Email", + }, + Symbol(receiver_plugin_metadata): undefined, + }, + ], + "name": "provisioned-contact-point", + "numberOfPolicies": undefined, + }, + { + "grafana_managed_receiver_configs": [ + { + "disableResolveMessage": false, + "name": "Slack with multiple channels", + "secureFields": { + "token": true, + }, + "settings": { + "recipient": "test-alerts", + }, + "type": "slack", + "uid": "c02ad56a-31da-46b9-becb-4348ec0890fd", + Symbol(receiver_status): { + "lastNotifyAttempt": "", + "lastNotifyAttemptDuration": "", + "name": "slack", + "sendResolved": true, + }, + Symbol(receiver_metadata): { + "description": "Sends notifications to Slack", + "name": "Slack", + }, + Symbol(receiver_plugin_metadata): undefined, + }, + { + "disableResolveMessage": false, + "name": "Slack with multiple channels", + "secureFields": { + "token": true, + }, + "settings": { + "recipient": "test-alerts2", + }, + "type": "slack", + "uid": "b286a3be-f690-49e2-8605-b075cbace2df", + Symbol(receiver_status): { + "lastNotifyAttempt": "", + "lastNotifyAttemptDuration": "", + "name": "slack", + "sendResolved": true, + }, + Symbol(receiver_metadata): { + "description": "Sends notifications to Slack", + "name": "Slack", + }, + Symbol(receiver_plugin_metadata): undefined, + }, + ], + "name": "Slack with multiple channels", + "numberOfPolicies": undefined, + }, + ], + "error": undefined, + "isLoading": false, + "refetchReceivers": [Function], +} +`; diff --git a/public/app/features/alerting/unified/components/contact-points/useContactPoints.test.tsx b/public/app/features/alerting/unified/components/contact-points/useContactPoints.test.tsx index 50f4fdbd03d..a4188d51789 100644 --- a/public/app/features/alerting/unified/components/contact-points/useContactPoints.test.tsx +++ b/public/app/features/alerting/unified/components/contact-points/useContactPoints.test.tsx @@ -2,10 +2,12 @@ import { renderHook, waitFor } from '@testing-library/react'; import React from 'react'; import { TestProvider } from 'test/helpers/TestProvider'; +import alertmanagerMock from 'app/features/alerting/unified/components/contact-points/__mocks__/alertmanager.config.mock.json'; import { AccessControlAction } from 'app/types'; -import { setupMswServer } from '../../mockApi'; -import { grantUserPermissions } from '../../mocks'; +import { ONCALL_INTEGRATION_V2_FEATURE } from '../../api/onCallApi'; +import { mockApi, setupMswServer } from '../../mockApi'; +import { grantUserPermissions, onCallPluginMetaMock } from '../../mocks'; import { AlertmanagerProvider } from '../../state/AlertmanagerContext'; import setupGrafanaManagedServer from './__mocks__/grafanaManagedServer'; @@ -23,6 +25,16 @@ describe('useContactPoints', () => { }); it('should return contact points with status', async () => { + mockApi(server).plugins.getPluginSettings({ ...onCallPluginMetaMock, enabled: true }); + mockApi(server).oncall.features([ONCALL_INTEGRATION_V2_FEATURE]); + mockApi(server).oncall.getOnCallIntegrations([ + { + display_name: 'grafana-integration', + value: 'ABC123', + integration_url: 'https://oncall-endpoint.example.com', + }, + ]); + mockApi(server).getContactPointsList(receivers); const { result } = renderHook(() => useContactPointsWithStatus(), { wrapper: ({ children }) => ( @@ -32,10 +44,42 @@ describe('useContactPoints', () => { ), }); - await waitFor(() => { expect(result.current.isLoading).toBe(false); expect(result.current).toMatchSnapshot(); }); }); + describe('when having oncall plugin installed and no alert manager config data', () => { + it('should return contact points with oncall metadata', async () => { + mockApi(server).plugins.getPluginSettings({ ...onCallPluginMetaMock, enabled: true }); + mockApi(server).oncall.features([ONCALL_INTEGRATION_V2_FEATURE]); + mockApi(server).oncall.getOnCallIntegrations([ + { + display_name: 'grafana-integration', + value: 'ABC123', + integration_url: 'https://oncall-endpoint.example.com', + }, + ]); + mockApi(server).getContactPointsList(receivers); + const { result } = renderHook( + () => useContactPointsWithStatus({ includePoliciesCount: false, receiverStatusPollingInterval: 0 }), + { + wrapper: ({ children }) => ( + + + {children} + + + ), + } + ); + + await waitFor(() => { + expect(result.current.isLoading).toBe(false); + expect(result.current).toMatchSnapshot(); + }); + }); + }); }); + +const receivers = JSON.parse(JSON.stringify(alertmanagerMock)).alertmanager_config.receivers; diff --git a/public/app/features/alerting/unified/components/contact-points/utils.ts b/public/app/features/alerting/unified/components/contact-points/utils.ts index a07e3cc03dc..6af5766d473 100644 --- a/public/app/features/alerting/unified/components/contact-points/utils.ts +++ b/public/app/features/alerting/unified/components/contact-points/utils.ts @@ -137,12 +137,17 @@ export function enhanceContactPointsWithMetadata( alertmanagerConfiguration && usedContactPointsByName && (usedContactPointsByName[contactPoint.name] ?? 0), grafana_managed_receiver_configs: receivers.map((receiver, index) => { const isOnCallReceiver = receiver.type === ReceiverTypes.OnCall; + // if we don't have alertmanagerConfiguration we can't get the metadata for oncall receivers, + // because we don't have the url, as we are not using the alertmanager configuration + // but the contact points returned by the read only permissions contact points endpoint (/api/v1/notifications/receivers) return { ...receiver, [RECEIVER_STATUS_KEY]: statusForReceiver?.integrations[index], [RECEIVER_META_KEY]: getNotifierMetadata(notifiers, receiver), // if OnCall plugin is installed, we'll add it to the receiver's plugin metadata - [RECEIVER_PLUGIN_META_KEY]: isOnCallReceiver ? getOnCallMetadata(onCallIntegrations, receiver) : undefined, + [RECEIVER_PLUGIN_META_KEY]: isOnCallReceiver + ? getOnCallMetadata(onCallIntegrations, receiver, Boolean(alertmanagerConfiguration)) + : undefined, }; }), }; diff --git a/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/useReceiversMetadata.ts b/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/useReceiversMetadata.ts index ba3f923ea1a..dc803813b4d 100644 --- a/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/useReceiversMetadata.ts +++ b/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/useReceiversMetadata.ts @@ -16,15 +16,20 @@ export interface ReceiverPluginMetadata { const onCallReceiverICon = GRAFANA_APP_RECEIVERS_SOURCE_IMAGE[SupportedPlugin.OnCall]; const onCallReceiverTitle = 'Grafana OnCall'; -const onCallReceiverMeta: ReceiverPluginMetadata = { +export const onCallReceiverMeta: ReceiverPluginMetadata = { title: onCallReceiverTitle, icon: onCallReceiverICon, }; export function getOnCallMetadata( onCallIntegrations: OnCallIntegrationDTO[] | undefined | null, - receiver: GrafanaManagedReceiverConfig + receiver: GrafanaManagedReceiverConfig, + hasAlertManagerConfigData = true ): ReceiverPluginMetadata { + if (!hasAlertManagerConfigData) { + return onCallReceiverMeta; + } + // oncall status is still loading if (onCallIntegrations === undefined) { return onCallReceiverMeta; diff --git a/public/app/features/alerting/unified/mockApi.ts b/public/app/features/alerting/unified/mockApi.ts index 98bef2dccec..7aabf1c3184 100644 --- a/public/app/features/alerting/unified/mockApi.ts +++ b/public/app/features/alerting/unified/mockApi.ts @@ -20,6 +20,7 @@ import { AlertManagerCortexConfig, AlertmanagerReceiver, EmailConfig, + GrafanaManagedContactPoint, GrafanaManagedReceiverConfig, MatcherOperator, Route, @@ -218,6 +219,9 @@ export function mockApi(server: SetupServer) { server.use(http.get(`api/plugins/${response.id}/settings`, () => HttpResponse.json(response))); }, }, + getContactPointsList: (response: GrafanaManagedContactPoint[]) => { + server.use(http.get(`/api/v1/notifications/receivers`, () => HttpResponse.json(response))); + }, oncall: { getOnCallIntegrations: (response: OnCallIntegrationDTO[]) => {