diff --git a/public/app/features/alerting/unified/utils/receiver-form.test.ts b/public/app/features/alerting/unified/utils/receiver-form.test.ts index 7db175181df..06ee33629a2 100644 --- a/public/app/features/alerting/unified/utils/receiver-form.test.ts +++ b/public/app/features/alerting/unified/utils/receiver-form.test.ts @@ -1,8 +1,17 @@ +import 'core-js/stable/structured-clone'; + import { NotifierDTO } from 'app/types'; -import { GrafanaChannelValues, ReceiverFormValues } from '../types/receiver-form'; +import { Receiver } from '../../../../plugins/datasource/alertmanager/types'; +import { CloudChannelValues, GrafanaChannelValues, ReceiverFormValues } from '../types/receiver-form'; -import { formValuesToGrafanaReceiver, omitEmptyValues, omitEmptyUnlessExisting } from './receiver-form'; +import { + formValuesToGrafanaReceiver, + omitEmptyValues, + omitEmptyUnlessExisting, + omitTemporaryIdentifiers, + formValuesToCloudReceiver, +} from './receiver-form'; describe('Receiver form utils', () => { describe('omitEmptyStringValues', () => { @@ -67,6 +76,91 @@ describe('Receiver form utils', () => { expect(omitEmptyUnlessExisting(original, existing)).toEqual(expected); }); }); + + describe('omitTemporaryIdentifiers', () => { + it('should remove __id from the root object', () => { + const original = { + __id: '1', + foo: 'bar', + }; + + const expected = { + foo: 'bar', + }; + + expect(omitTemporaryIdentifiers(original)).toEqual(expected); + }); + + it('should remove __id from nested objects', () => { + const original = { + foo: 'bar', + nested: { + __id: '1', + baz: 'qux', + doubleNested: { __id: '2', url: 'example.com' }, + }, + }; + + const expected = { + foo: 'bar', + nested: { + baz: 'qux', + doubleNested: { url: 'example.com' }, + }, + }; + + expect(omitTemporaryIdentifiers(original)).toEqual(expected); + }); + + it('should remove __id from objects in an array', () => { + const original = { + foo: 'bar', + array: [ + { + __id: '1', + baz: 'qux', + actions: [ + { __id: '3', type: 'email' }, + { __id: '4', type: 'slack' }, + ], + }, + { __id: '2', quux: 'quuz' }, + ], + }; + + const expected = { + foo: 'bar', + array: [ + { + baz: 'qux', + actions: [{ type: 'email' }, { type: 'slack' }], + }, + { + quux: 'quuz', + }, + ], + }; + + expect(omitTemporaryIdentifiers(original)).toEqual(expected); + }); + + it('should return a new object and keep the original intact', () => { + const original = { + foo: 'bar', + nested: { + __id: '1', + baz: 'qux', + doubleNested: { __id: '2', url: 'example.com' }, + }, + }; + + const withOmitted = omitTemporaryIdentifiers(original); + + expect(withOmitted).not.toBe(original); + expect(original.nested.__id).toBe('1'); + expect(original.nested.doubleNested.__id).toBe('2'); + }); + }); }); describe('formValuesToGrafanaReceiver', () => { @@ -111,3 +205,50 @@ describe('formValuesToGrafanaReceiver', () => { expect(formValuesToGrafanaReceiver(formValues, channelMap, {}, notifiers)).toMatchSnapshot(); }); }); + +describe('formValuesToCloudReceiver', () => { + it('should remove temporary ids from receivers and settings', () => { + const formValues: ReceiverFormValues = { + name: 'my-receiver', + items: [ + { + __id: '1', + type: 'slack', + settings: { + url: 'https://slack.example.com/', + actions: [{ __id: '2', text: 'Acknowledge', type: 'button' }], + fields: [{ __id: '10', title: 'priority', value: '1' }], + }, + secureFields: {}, + secureSettings: {}, + sendResolved: true, + }, + ], + }; + + const defaults: CloudChannelValues = { + __id: '1', + type: 'slack', + settings: { + url: 'https://slack.example.com/', + }, + secureFields: {}, + secureSettings: {}, + sendResolved: true, + }; + + const expected: Receiver = { + name: 'my-receiver', + slack_configs: [ + { + url: 'https://slack.example.com/', + actions: [{ text: 'Acknowledge', type: 'button' }], + fields: [{ title: 'priority', value: '1' }], + send_resolved: true, + }, + ], + }; + + expect(formValuesToCloudReceiver(formValues, defaults)).toEqual(expected); + }); +}); diff --git a/public/app/features/alerting/unified/utils/receiver-form.ts b/public/app/features/alerting/unified/utils/receiver-form.ts index d779c9f2cb8..b7e4b35233b 100644 --- a/public/app/features/alerting/unified/utils/receiver-form.ts +++ b/public/app/features/alerting/unified/utils/receiver-form.ts @@ -108,7 +108,7 @@ export function formValuesToCloudReceiver( }; values.items.forEach(({ __id, type, settings, sendResolved }) => { const channel = omitEmptyValues({ - ...settings, + ...omitTemporaryIdentifiers(settings), send_resolved: sendResolved ?? defaults.sendResolved, }); @@ -292,3 +292,21 @@ export function omitEmptyValues(obj: T): T { export function omitEmptyUnlessExisting(settings = {}, existing = {}): Record { return omitBy(settings, (value, key) => isUnacceptableValue(value) && !(key in existing)); } + +export function omitTemporaryIdentifiers(object: Readonly): T { + function omitIdentifiers(obj: T) { + if (isArray(obj)) { + obj.forEach(omitIdentifiers); + } else if (typeof obj === 'object' && obj !== null) { + if ('__id' in obj) { + delete obj.__id; + } + Object.values(obj).forEach(omitIdentifiers); + } + } + + const objectCopy = structuredClone(object); + omitIdentifiers(objectCopy); + + return objectCopy; +}