From 70aa8fe6b3f4239f489e0f553761417d1ba5de8f Mon Sep 17 00:00:00 2001 From: Gilles De Mey Date: Wed, 14 Feb 2024 13:12:04 +0100 Subject: [PATCH] Alerting: Fix slack double pound and email summary (#82333) --- .../components/contact-points/utils.test.ts | 157 ++++++++++++++++++ .../components/contact-points/utils.ts | 30 ++-- 2 files changed, 176 insertions(+), 11 deletions(-) create mode 100644 public/app/features/alerting/unified/components/contact-points/utils.test.ts diff --git a/public/app/features/alerting/unified/components/contact-points/utils.test.ts b/public/app/features/alerting/unified/components/contact-points/utils.test.ts new file mode 100644 index 00000000000..4e340e33577 --- /dev/null +++ b/public/app/features/alerting/unified/components/contact-points/utils.test.ts @@ -0,0 +1,157 @@ +import { GrafanaManagedContactPoint } from 'app/plugins/datasource/alertmanager/types'; + +import { ReceiverTypes } from '../receivers/grafanaAppReceivers/onCall/onCall'; + +import { RECEIVER_META_KEY, RECEIVER_PLUGIN_META_KEY } from './useContactPoints'; +import { + ReceiverConfigWithMetadata, + getReceiverDescription, + isAutoGeneratedPolicy, + isProvisioned, + summarizeEmailAddresses, +} from './utils'; + +describe('isProvisioned', () => { + it('should return true when at least one receiver is provisioned', () => { + const contactPoint: GrafanaManagedContactPoint = { + name: 'my-contact-point', + grafana_managed_receiver_configs: [ + { name: 'email', provenance: 'api', type: 'email', disableResolveMessage: false, settings: {} }, + ], + }; + + expect(isProvisioned(contactPoint)).toBe(true); + }); + + it('should return false when no receiver was provisioned', () => { + const contactPoint: GrafanaManagedContactPoint = { + name: 'my-contact-point', + grafana_managed_receiver_configs: [ + { name: 'email', provenance: undefined, type: 'email', disableResolveMessage: false, settings: {} }, + ], + }; + + expect(isProvisioned(contactPoint)).toBe(false); + }); +}); + +describe('isAutoGeneratedPolicy', () => { + it('should return false when not enabled', () => { + expect(isAutoGeneratedPolicy({})).toBe(false); + }); +}); + +describe('getReceiverDescription', () => { + it('should show multiple email addresses', () => { + const receiver: ReceiverConfigWithMetadata = { + name: 'email', + provenance: undefined, + type: 'email', + disableResolveMessage: false, + settings: { addresses: 'test1@test.com,test2@test.com,test3@test.com,test4@test.com' }, + [RECEIVER_META_KEY]: { + name: 'Email', + description: 'The email receiver', + }, + }; + + expect(getReceiverDescription(receiver)).toBe('test1@test.com, test2@test.com, test3@test.com, +1 more'); + }); + + it('should work for Slack', () => { + const output = '#channel'; + const receiver1: ReceiverConfigWithMetadata = { + name: 'slack', + provenance: undefined, + type: 'slack', + disableResolveMessage: false, + settings: { recipient: '#channel' }, + [RECEIVER_META_KEY]: { + name: 'Slack', + description: 'The Slack receiver', + }, + }; + + const receiver2: ReceiverConfigWithMetadata = { + name: 'slack', + provenance: undefined, + type: 'slack', + disableResolveMessage: false, + settings: { recipient: 'channel' }, + [RECEIVER_META_KEY]: { + name: 'Slack', + description: 'The Slack receiver', + }, + }; + + expect(getReceiverDescription(receiver1)).toBe(output); + expect(getReceiverDescription(receiver2)).toBe(output); + }); + + it('should work for OnCall', () => { + const output = 'The OnCall receiver'; + const input: ReceiverConfigWithMetadata = { + name: 'my oncall', + provenance: undefined, + type: ReceiverTypes.OnCall, + disableResolveMessage: false, + settings: {}, + [RECEIVER_PLUGIN_META_KEY]: { + description: output, + icon: '', + title: '', + }, + [RECEIVER_META_KEY]: { + name: '', + }, + }; + + expect(getReceiverDescription(input)).toBe(output); + }); + + it('should work for any type', () => { + const output = 'Some description of the receiver'; + const input: ReceiverConfigWithMetadata = { + name: 'some receiver', + provenance: undefined, + type: 'some', + disableResolveMessage: false, + settings: {}, + [RECEIVER_META_KEY]: { + name: 'Some Receiver', + description: output, + }, + }; + + expect(getReceiverDescription(input)).toBe(output); + }); + + it('should work for any type with no description', () => { + const input: ReceiverConfigWithMetadata = { + name: 'some receiver', + provenance: undefined, + type: 'some', + disableResolveMessage: false, + settings: {}, + [RECEIVER_META_KEY]: { + name: 'Some Receiver', + }, + }; + + expect(getReceiverDescription(input)).toBe(undefined); + }); +}); + +describe('summarizeEmailAddresses', () => { + it('should work with one email address', () => { + expect(summarizeEmailAddresses('test@test.com')).toBe('test@test.com'); + }); + + it('should work with multiple types of separators', () => { + const output = 'foo@foo.com, bar@bar.com'; + + expect(summarizeEmailAddresses('foo@foo.com, bar@bar.com')).toBe(output); + expect(summarizeEmailAddresses(' foo@foo.com; bar@bar.com')).toBe(output); + expect(summarizeEmailAddresses('foo@foo.com\n bar@bar.com ')).toBe(output); + }); +}); 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 201351e1451..479bde2b494 100644 --- a/public/app/features/alerting/unified/components/contact-points/utils.ts +++ b/public/app/features/alerting/unified/components/contact-points/utils.ts @@ -1,4 +1,4 @@ -import { countBy, split, trim, upperFirst } from 'lodash'; +import { countBy, difference, take, trim, upperFirst } from 'lodash'; import { ReactNode } from 'react'; import { config } from '@grafana/runtime'; @@ -40,15 +40,21 @@ export function getReceiverDescription(receiver: ReceiverConfigWithMetadata): Re return hasEmailAddresses ? summarizeEmailAddresses(receiver.settings['addresses']) : undefined; } case 'slack': { - const channelName = receiver.settings['recipient']; - return channelName ? `#${channelName}` : undefined; + const recipient: string | undefined = receiver.settings['recipient']; + if (!recipient) { + return; + } + + // Slack channel name might have a "#" in the recipient already + const channelName = recipient.replace(/^#/, ''); + return `#${channelName}`; } case 'kafka': { - const topicName = receiver.settings['kafkaTopic']; + const topicName: string | undefined = receiver.settings['kafkaTopic']; return topicName; } case 'webhook': { - const url = receiver.settings['url']; + const url: string | undefined = receiver.settings['url']; return url; } case ReceiverTypes.OnCall: { @@ -61,20 +67,22 @@ export function getReceiverDescription(receiver: ReceiverConfigWithMetadata): Re // input: foo+1@bar.com, foo+2@bar.com, foo+3@bar.com, foo+4@bar.com // output: foo+1@bar.com, foo+2@bar.com, +2 more -function summarizeEmailAddresses(addresses: string): string { +export function summarizeEmailAddresses(addresses: string): string { const MAX_ADDRESSES_SHOWN = 3; const SUPPORTED_SEPARATORS = /,|;|\n+/g; + // split all email addresses const emails = addresses.trim().split(SUPPORTED_SEPARATORS).map(trim); - const notShown = emails.length - MAX_ADDRESSES_SHOWN; + // grab the first 3 and the rest + const summary = take(emails, MAX_ADDRESSES_SHOWN); + const rest = difference(emails, summary); - const truncatedAddresses = split(addresses, SUPPORTED_SEPARATORS, MAX_ADDRESSES_SHOWN); - if (notShown > 0) { - truncatedAddresses.push(`+${notShown} more`); + if (rest.length) { + summary.push(`+${rest.length} more`); } - return truncatedAddresses.join(', '); + return summary.join(', '); } // Grafana Managed contact points have receivers with additional diagnostics