diff --git a/src/lib/addons/__snapshots__/datadog.test.ts.snap b/src/lib/addons/__snapshots__/datadog.test.ts.snap index 35432af758..57c3116b66 100644 --- a/src/lib/addons/__snapshots__/datadog.test.ts.snap +++ b/src/lib/addons/__snapshots__/datadog.test.ts.snap @@ -7,3 +7,13 @@ exports[`Should call datadog webhook for archived toggle with project info 1`] exports[`Should call datadog webhook 1`] = `"{"text":"%%% \\n some@user.com created feature toggle [some-toggle](http://some-url.com/projects//features/some-toggle) in project *undefined* \\n %%% ","title":"Unleash notification update"}"`; exports[`Should call datadog webhook for toggled environment 1`] = `"{"text":"%%% \\n some@user.com *disabled* [some-toggle](http://some-url.com/projects/default/features/some-toggle) in *development* environment in project *default* \\n %%% ","title":"Unleash notification update"}"`; + +exports[`Should include customHeaders in headers when calling service 1`] = `"{"text":"%%% \\n some@user.com *disabled* [some-toggle](http://some-url.com/projects/default/features/some-toggle) in *development* environment in project *default* \\n %%% ","title":"Unleash notification update"}"`; + +exports[`Should include customHeaders in headers when calling service 2`] = ` +{ + "Content-Type": "application/json", + "DD-API-KEY": "fakeKey", + "MY_CUSTOM_HEADER": "MY_CUSTOM_VALUE", +} +`; diff --git a/src/lib/addons/__snapshots__/slack.test.ts.snap b/src/lib/addons/__snapshots__/slack.test.ts.snap index 40817e1d1a..ef1dfa2ef3 100644 --- a/src/lib/addons/__snapshots__/slack.test.ts.snap +++ b/src/lib/addons/__snapshots__/slack.test.ts.snap @@ -1,9 +1,18 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Should call slack webhook 1`] = `"{"username":"Unleash","icon_emoji":":unleash:","text":"some@user.com created feature toggle in project *default*","channel":"#undefined","attachments":[{"actions":[{"name":"featureToggle","text":"Open in Unleash","type":"button","value":"featureToggle","style":"primary","url":"http://some-url.com/projects/default/features/some-toggle"}]}]}"`; +exports[`Should call slack webhook 1`] = `"{"username":"Unleash","icon_emoji":":unleash:","text":"some@user.com created feature toggle in project *default*","channel":"#general","attachments":[{"actions":[{"name":"featureToggle","text":"Open in Unleash","type":"button","value":"featureToggle","style":"primary","url":"http://some-url.com/projects/default/features/some-toggle"}]}]}"`; -exports[`Should call slack webhook for archived toggle 1`] = `"{"username":"Unleash","icon_emoji":":unleash:","text":" some@user.com just archived feature toggle **","channel":"#undefined","attachments":[{"actions":[{"name":"featureToggle","text":"Open in Unleash","type":"button","value":"featureToggle","style":"primary","url":"http://some-url.com/archive"}]}]}"`; +exports[`Should call slack webhook for archived toggle 1`] = `"{"username":"Unleash","icon_emoji":":unleash:","text":" some@user.com just archived feature toggle **","channel":"#general","attachments":[{"actions":[{"name":"featureToggle","text":"Open in Unleash","type":"button","value":"featureToggle","style":"primary","url":"http://some-url.com/archive"}]}]}"`; -exports[`Should call slack webhook for archived toggle with project info 1`] = `"{"username":"Unleash","icon_emoji":":unleash:","text":" some@user.com just archived feature toggle **","channel":"#undefined","attachments":[{"actions":[{"name":"featureToggle","text":"Open in Unleash","type":"button","value":"featureToggle","style":"primary","url":"http://some-url.com/projects/some-project/archive"}]}]}"`; +exports[`Should call slack webhook for archived toggle with project info 1`] = `"{"username":"Unleash","icon_emoji":":unleash:","text":" some@user.com just archived feature toggle **","channel":"#general","attachments":[{"actions":[{"name":"featureToggle","text":"Open in Unleash","type":"button","value":"featureToggle","style":"primary","url":"http://some-url.com/projects/some-project/archive"}]}]}"`; -exports[`Should call webhook for toggled environment 1`] = `"{"username":"Unleash","icon_emoji":":unleash:","text":"some@user.com *disabled* in *development* environment in project *default*","channel":"#undefined","attachments":[{"actions":[{"name":"featureToggle","text":"Open in Unleash","type":"button","value":"featureToggle","style":"primary","url":"http://some-url.com/projects/default/features/some-toggle"}]}]}"`; +exports[`Should call webhook for toggled environment 1`] = `"{"username":"Unleash","icon_emoji":":unleash:","text":"some@user.com *disabled* in *development* environment in project *default*","channel":"#general","attachments":[{"actions":[{"name":"featureToggle","text":"Open in Unleash","type":"button","value":"featureToggle","style":"primary","url":"http://some-url.com/projects/default/features/some-toggle"}]}]}"`; + +exports[`Should include custom headers from parameters in call to service 1`] = `"{"username":"Unleash","icon_emoji":":unleash:","text":"some@user.com *disabled* in *development* environment in project *default*","channel":"#general","attachments":[{"actions":[{"name":"featureToggle","text":"Open in Unleash","type":"button","value":"featureToggle","style":"primary","url":"http://some-url.com/projects/default/features/some-toggle"}]}]}"`; + +exports[`Should include custom headers from parameters in call to service 2`] = ` +{ + "Content-Type": "application/json", + "MY_CUSTOM_HEADER": "MY_CUSTOM_VALUE", +} +`; diff --git a/src/lib/addons/__snapshots__/teams.test.ts.snap b/src/lib/addons/__snapshots__/teams.test.ts.snap index feb53a5d90..76877e1d36 100644 --- a/src/lib/addons/__snapshots__/teams.test.ts.snap +++ b/src/lib/addons/__snapshots__/teams.test.ts.snap @@ -7,3 +7,12 @@ exports[`Should call teams webhook for archived toggle 1`] = `"{"themeColor":"00 exports[`Should call teams webhook for archived toggle with project info 1`] = `"{"themeColor":"0076D7","summary":"Message","sections":[{"activityTitle":" some@user.com just archived feature toggle *[some-toggle](http://some-url.com/projects/some-project/archive)*","activitySubtitle":"Unleash notification update","facts":[{"name":"User","value":"some@user.com"},{"name":"Action","value":"feature-archived"}]}],"potentialAction":[{"@type":"OpenUri","name":"Go to feature","targets":[{"os":"default","uri":"http://some-url.com/projects/some-project/archive"}]}]}"`; exports[`Should call teams webhook for toggled environment 1`] = `"{"themeColor":"0076D7","summary":"Message","sections":[{"activityTitle":"some@user.com *disabled* [some-toggle](http://some-url.com/projects/default/features/some-toggle) in *development* environment in project *default*","activitySubtitle":"Unleash notification update","facts":[{"name":"User","value":"some@user.com"},{"name":"Action","value":"feature-environment-disabled"}]}],"potentialAction":[{"@type":"OpenUri","name":"Go to feature","targets":[{"os":"default","uri":"http://some-url.com/projects/default/features/some-toggle"}]}]}"`; + +exports[`Should include custom headers in call to teams 1`] = `"{"themeColor":"0076D7","summary":"Message","sections":[{"activityTitle":"some@user.com *disabled* [some-toggle](http://some-url.com/projects/default/features/some-toggle) in *development* environment in project *default*","activitySubtitle":"Unleash notification update","facts":[{"name":"User","value":"some@user.com"},{"name":"Action","value":"feature-environment-disabled"}]}],"potentialAction":[{"@type":"OpenUri","name":"Go to feature","targets":[{"os":"default","uri":"http://some-url.com/projects/default/features/some-toggle"}]}]}"`; + +exports[`Should include custom headers in call to teams 2`] = ` +{ + "Content-Type": "application/json", + "MY_CUSTOM_HEADER": "MY_CUSTOM_VALUE", +} +`; diff --git a/src/lib/addons/datadog-definition.ts b/src/lib/addons/datadog-definition.ts index 16ef412164..8c00af3872 100644 --- a/src/lib/addons/datadog-definition.ts +++ b/src/lib/addons/datadog-definition.ts @@ -40,6 +40,18 @@ const dataDogDefinition: IAddonDefinition = { required: true, sensitive: true, }, + { + name: 'customHeaders', + displayName: 'Extra HTTP Headers', + placeholder: `{ + "ISTIO_USER_KEY": "hunter2", + "SOME_OTHER_CUSTOM_HTTP_HEADER": "SOMEVALUE" + }`, + description: `(Optional) Used to add extra HTTP Headers to the request the plugin fires off. Format here needs to be a valid json object of key value pairs where both key and value are strings`, + required: false, + sensitive: true, + type: 'textfield', + }, ], events: [ FEATURE_CREATED, diff --git a/src/lib/addons/datadog.test.ts b/src/lib/addons/datadog.test.ts index 2cd70ae595..5f52a215cb 100644 --- a/src/lib/addons/datadog.test.ts +++ b/src/lib/addons/datadog.test.ts @@ -55,6 +55,7 @@ test('Should call datadog webhook', async () => { const parameters = { url: 'http://api.datadoghq.com/api/v1/events', + apiKey: 'fakeKey', }; await addon.handleEvent(event, parameters); @@ -81,6 +82,7 @@ test('Should call datadog webhook for archived toggle', async () => { const parameters = { url: 'http://api.datadoghq.com/api/v1/events', + apiKey: 'fakeKey', }; await addon.handleEvent(event, parameters); @@ -108,6 +110,7 @@ test('Should call datadog webhook for archived toggle with project info', async const parameters = { url: 'http://api.datadoghq.com/api/v1/events', + apiKey: 'fakeKey', }; await addon.handleEvent(event, parameters); @@ -136,6 +139,7 @@ test(`Should call datadog webhook for toggled environment`, async () => { const parameters = { url: 'http://hooks.slack.com', + apiKey: 'fakeKey', }; await addon.handleEvent(event, parameters); @@ -144,3 +148,34 @@ test(`Should call datadog webhook for toggled environment`, async () => { expect(fetchRetryCalls[0].options.body).toMatch(/disabled/); expect(fetchRetryCalls[0].options.body).toMatchSnapshot(); }); + +test(`Should include customHeaders in headers when calling service`, async () => { + const addon = new DatadogAddon({ + getLogger: noLogger, + unleashUrl: 'http://some-url.com', + }); + const event: IEvent = { + id: 2, + createdAt: new Date(), + type: FEATURE_ENVIRONMENT_DISABLED, + createdBy: 'some@user.com', + environment: 'development', + project: 'default', + featureName: 'some-toggle', + data: { + name: 'some-toggle', + }, + }; + + const parameters = { + url: 'http://hooks.slack.com', + apiKey: 'fakeKey', + customHeaders: `{ "MY_CUSTOM_HEADER": "MY_CUSTOM_VALUE" }`, + }; + await addon.handleEvent(event, parameters); + expect(fetchRetryCalls).toHaveLength(1); + expect(fetchRetryCalls[0].url).toBe(parameters.url); + expect(fetchRetryCalls[0].options.body).toMatch(/disabled/); + expect(fetchRetryCalls[0].options.body).toMatchSnapshot(); + expect(fetchRetryCalls[0].options.headers).toMatchSnapshot(); +}); diff --git a/src/lib/addons/datadog.ts b/src/lib/addons/datadog.ts index f8b15b60b1..4393321565 100644 --- a/src/lib/addons/datadog.ts +++ b/src/lib/addons/datadog.ts @@ -9,6 +9,12 @@ import { } from './feature-event-formatter-md'; import { IEvent } from '../types/events'; +interface IDatadogParameters { + url: string; + apiKey: string; + customHeaders?: string; +} + export default class DatadogAddon extends Addon { private msgFormatter: FeatureEventFormatter; @@ -21,9 +27,15 @@ export default class DatadogAddon extends Addon { } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types - async handleEvent(event: IEvent, parameters: any): Promise { - const { url = 'https://api.datadoghq.com/api/v1/events', apiKey } = - parameters; + async handleEvent( + event: IEvent, + parameters: IDatadogParameters, + ): Promise { + const { + url = 'https://api.datadoghq.com/api/v1/events', + apiKey, + customHeaders, + } = parameters; const text = this.msgFormatter.format(event); @@ -35,12 +47,22 @@ export default class DatadogAddon extends Addon { title: 'Unleash notification update', tags, }; - + let extraHeaders = {}; + if (typeof customHeaders === 'string' && customHeaders.length > 1) { + try { + extraHeaders = JSON.parse(customHeaders); + } catch (e) { + this.logger.warn( + `Could not parse the json in the customHeaders parameter. [${customHeaders}]`, + ); + } + } const requestOpts = { method: 'POST', headers: { 'Content-Type': 'application/json', 'DD-API-KEY': apiKey, + ...extraHeaders, }, body: JSON.stringify(body), }; diff --git a/src/lib/addons/slack-definition.ts b/src/lib/addons/slack-definition.ts index f8f3839755..d6b79211ec 100644 --- a/src/lib/addons/slack-definition.ts +++ b/src/lib/addons/slack-definition.ts @@ -59,6 +59,18 @@ const slackDefinition: IAddonDefinition = { required: true, sensitive: false, }, + { + name: 'customHeaders', + displayName: 'Extra HTTP Headers', + placeholder: `{ + "ISTIO_USER_KEY": "hunter2", + "SOME_OTHER_CUSTOM_HTTP_HEADER": "SOMEVALUE" + }`, + description: `(Optional) Used to add extra HTTP Headers to the request the plugin fires off. Format here needs to be a valid json object of key value pairs where both key and value are strings`, + required: false, + sensitive: true, + type: 'textfield', + }, ], events: [ FEATURE_CREATED, diff --git a/src/lib/addons/slack.test.ts b/src/lib/addons/slack.test.ts index 14bc626d4c..416ffc1d70 100644 --- a/src/lib/addons/slack.test.ts +++ b/src/lib/addons/slack.test.ts @@ -57,6 +57,7 @@ test('Should call slack webhook', async () => { const parameters = { url: 'http://hooks.slack.com', + defaultChannel: 'general', }; await addon.handleEvent(event, parameters); @@ -83,6 +84,7 @@ test('Should call slack webhook for archived toggle', async () => { const parameters = { url: 'http://hooks.slack.com', + defaultChannel: 'general', }; await addon.handleEvent(event, parameters); @@ -110,6 +112,7 @@ test('Should call slack webhook for archived toggle with project info', async () const parameters = { url: 'http://hooks.slack.com', + defaultChannel: 'general', }; await addon.handleEvent(event, parameters); @@ -138,6 +141,7 @@ test(`Should call webhook for toggled environment`, async () => { const parameters = { url: 'http://hooks.slack.com', + defaultChannel: 'general', }; await addon.handleEvent(event, parameters); @@ -255,3 +259,35 @@ test('Should post to all channels in tags', async () => { expect(req1.channel).toBe('#another-channel-1'); expect(req2.channel).toBe('#another-channel-2'); }); + +test('Should include custom headers from parameters in call to service', async () => { + const addon = new SlackAddon({ + getLogger: noLogger, + unleashUrl: 'http://some-url.com', + }); + const event: IEvent = { + id: 2, + createdAt: new Date(), + type: FEATURE_ENVIRONMENT_DISABLED, + createdBy: 'some@user.com', + environment: 'development', + project: 'default', + featureName: 'some-toggle', + data: { + name: 'some-toggle', + }, + }; + + const parameters = { + url: 'http://hooks.slack.com', + defaultChannel: 'general', + customHeaders: `{ "MY_CUSTOM_HEADER": "MY_CUSTOM_VALUE" }`, + }; + + await addon.handleEvent(event, parameters); + expect(fetchRetryCalls).toHaveLength(1); + expect(fetchRetryCalls[0].url).toBe(parameters.url); + expect(fetchRetryCalls[0].options.body).toMatch(/disabled/); + expect(fetchRetryCalls[0].options.body).toMatchSnapshot(); + expect(fetchRetryCalls[0].options.headers).toMatchSnapshot(); +}); diff --git a/src/lib/addons/slack.ts b/src/lib/addons/slack.ts index 45795d2548..4400b704e2 100644 --- a/src/lib/addons/slack.ts +++ b/src/lib/addons/slack.ts @@ -10,6 +10,13 @@ import { } from './feature-event-formatter-md'; import { IEvent } from '../types/events'; +interface ISlackAddonParameters { + url: string; + username?: string; + defaultChannel: string; + emojiIcon?: string; + customHeaders?: string; +} export default class SlackAddon extends Addon { private msgFormatter: FeatureEventFormatter; @@ -22,12 +29,16 @@ export default class SlackAddon extends Addon { } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types - async handleEvent(event: IEvent, parameters: any): Promise { + async handleEvent( + event: IEvent, + parameters: ISlackAddonParameters, + ): Promise { const { url, defaultChannel, username = 'Unleash', - iconEmoji = ':unleash:', + emojiIcon = ':unleash:', + customHeaders, } = parameters; const slackChannels = this.findSlackChannels(event); @@ -42,7 +53,7 @@ export default class SlackAddon extends Addon { const requests = slackChannels.map((channel) => { const body = { username, - icon_emoji: iconEmoji, // eslint-disable-line camelcase + icon_emoji: emojiIcon, // eslint-disable-line camelcase text, channel: `#${channel}`, attachments: [ @@ -60,10 +71,22 @@ export default class SlackAddon extends Addon { }, ], }; - + let extraHeaders = {}; + if (typeof customHeaders === 'string' && customHeaders.length > 1) { + try { + extraHeaders = JSON.parse(customHeaders); + } catch (e) { + this.logger.warn( + `Could not parse the json in the customHeaders parameter. [${customHeaders}]`, + ); + } + } const requestOpts = { method: 'POST', - headers: { 'Content-Type': 'application/json' }, + headers: { + 'Content-Type': 'application/json', + ...extraHeaders, + }, body: JSON.stringify(body), }; diff --git a/src/lib/addons/teams-definition.ts b/src/lib/addons/teams-definition.ts index 67d7f4c2d0..b37c3a1c8b 100644 --- a/src/lib/addons/teams-definition.ts +++ b/src/lib/addons/teams-definition.ts @@ -30,6 +30,18 @@ const teamsDefinition: IAddonDefinition = { required: true, sensitive: true, }, + { + name: 'customHeaders', + displayName: 'Extra HTTP Headers', + placeholder: `{ + "ISTIO_USER_KEY": "hunter2", + "SOME_OTHER_CUSTOM_HTTP_HEADER": "SOMEVALUE" + }`, + description: `(Optional) Used to add extra HTTP Headers to the request the plugin fires off. Format here needs to be a valid json object of key value pairs where both key and value are strings`, + required: false, + sensitive: true, + type: 'textfield', + }, ], events: [ FEATURE_CREATED, diff --git a/src/lib/addons/teams.test.ts b/src/lib/addons/teams.test.ts index 9a7f167684..61f43f8e0f 100644 --- a/src/lib/addons/teams.test.ts +++ b/src/lib/addons/teams.test.ts @@ -145,3 +145,34 @@ test(`Should call teams webhook for toggled environment`, async () => { expect(fetchRetryCalls[0].options.body).toMatch(/disabled/); expect(fetchRetryCalls[0].options.body).toMatchSnapshot(); }); + +test('Should include custom headers in call to teams', async () => { + const addon = new TeamsAddon({ + getLogger: noLogger, + unleashUrl: 'http://some-url.com', + }); + const event: IEvent = { + id: 2, + createdAt: new Date(), + type: FEATURE_ENVIRONMENT_DISABLED, + createdBy: 'some@user.com', + environment: 'development', + project: 'default', + featureName: 'some-toggle', + data: { + name: 'some-toggle', + }, + }; + + const parameters = { + url: 'http://hooks.slack.com', + customHeaders: `{ "MY_CUSTOM_HEADER": "MY_CUSTOM_VALUE" }`, + }; + + await addon.handleEvent(event, parameters); + expect(fetchRetryCalls).toHaveLength(1); + expect(fetchRetryCalls[0].url).toBe(parameters.url); + expect(fetchRetryCalls[0].options.body).toMatch(/disabled/); + expect(fetchRetryCalls[0].options.body).toMatchSnapshot(); + expect(fetchRetryCalls[0].options.headers).toMatchSnapshot(); +}); diff --git a/src/lib/addons/teams.ts b/src/lib/addons/teams.ts index c1a2aad781..44c284ee3f 100644 --- a/src/lib/addons/teams.ts +++ b/src/lib/addons/teams.ts @@ -8,6 +8,10 @@ import { } from './feature-event-formatter-md'; import { IEvent } from '../types/events'; +interface ITeamsParameters { + url: string; + customHeaders?: string; +} export default class TeamsAddon extends Addon { private msgFormatter: FeatureEventFormatter; @@ -17,8 +21,11 @@ export default class TeamsAddon extends Addon { } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types - async handleEvent(event: IEvent, parameters: any): Promise { - const { url } = parameters; + async handleEvent( + event: IEvent, + parameters: ITeamsParameters, + ): Promise { + const { url, customHeaders } = parameters; const { createdBy } = event; const text = this.msgFormatter.format(event); const featureLink = this.msgFormatter.featureLink(event); @@ -56,9 +63,20 @@ export default class TeamsAddon extends Addon { ], }; + let extraHeaders = {}; + if (typeof customHeaders === 'string' && customHeaders.length > 1) { + try { + extraHeaders = JSON.parse(customHeaders); + } catch (e) { + this.logger.warn( + `Could not parse the json in the customHeaders parameter. [${customHeaders}]`, + ); + } + } + const requestOpts = { method: 'POST', - headers: { 'Content-Type': 'application/json' }, + headers: { 'Content-Type': 'application/json', ...extraHeaders }, body: JSON.stringify(body), }; const res = await this.fetchRetry(url, requestOpts); diff --git a/src/lib/addons/webhook-definition.ts b/src/lib/addons/webhook-definition.ts index 75bf8415d1..f85a1f0a0a 100644 --- a/src/lib/addons/webhook-definition.ts +++ b/src/lib/addons/webhook-definition.ts @@ -78,6 +78,18 @@ const webhookDefinition: IAddonDefinition = { required: false, sensitive: false, }, + { + name: 'customHeaders', + displayName: 'Extra HTTP Headers', + placeholder: `{ + "ISTIO_USER_KEY": "hunter2", + "SOME_OTHER_CUSTOM_HTTP_HEADER": "SOMEVALUE" + }`, + description: `(Optional) Used to add extra HTTP Headers to the request the plugin fires off. Format here needs to be a valid json object of key value pairs where both key and value are strings`, + required: false, + sensitive: true, + type: 'textfield', + }, ], events: [ FEATURE_CREATED, diff --git a/src/lib/addons/webhook.test.ts b/src/lib/addons/webhook.test.ts index 197e52381e..43174b95df 100644 --- a/src/lib/addons/webhook.test.ts +++ b/src/lib/addons/webhook.test.ts @@ -114,3 +114,35 @@ test('Should format event with "authorization"', () => { expect(call.options.headers.Authorization).toBe(parameters.authorization); expect(call.options.body).toBe('feature-created on toggle some-toggle'); }); + +test('Should handle custom headers', async () => { + const addon = new WebhookAddon({ getLogger: noLogger }); + const event: IEvent = { + id: 1, + createdAt: new Date(), + type: FEATURE_CREATED, + createdBy: 'some@user.com', + featureName: 'some-toggle', + data: { + name: 'some-toggle', + enabled: false, + strategies: [{ name: 'default' }], + }, + }; + + const parameters = { + url: 'http://test.webhook.com/plain', + bodyTemplate: '{{event.type}} on toggle {{event.data.name}}', + contentType: 'text/plain', + authorization: 'API KEY 123abc', + customHeaders: `{ "MY_CUSTOM_HEADER": "MY_CUSTOM_VALUE" }`, + }; + + addon.handleEvent(event, parameters); + const call = fetchRetryCalls[0]; + expect(fetchRetryCalls.length).toBe(1); + expect(call.url).toBe(parameters.url); + expect(call.options.headers.Authorization).toBe(parameters.authorization); + expect(call.options.headers.MY_CUSTOM_HEADER).toBe('MY_CUSTOM_VALUE'); + expect(call.options.body).toBe('feature-created on toggle some-toggle'); +}); diff --git a/src/lib/addons/webhook.ts b/src/lib/addons/webhook.ts index 5241610c98..ae4000a57b 100644 --- a/src/lib/addons/webhook.ts +++ b/src/lib/addons/webhook.ts @@ -9,6 +9,7 @@ interface IParameters { bodyTemplate?: string; contentType?: string; authorization?: string; + customHeaders?: string; } export default class Webhook extends Addon { @@ -17,7 +18,8 @@ export default class Webhook extends Addon { } async handleEvent(event: IEvent, parameters: IParameters): Promise { - const { url, bodyTemplate, contentType, authorization } = parameters; + const { url, bodyTemplate, contentType, authorization, customHeaders } = + parameters; const context = { event, }; @@ -30,11 +32,22 @@ export default class Webhook extends Addon { body = JSON.stringify(event); } + let extraHeaders = {}; + if (typeof customHeaders === 'string' && customHeaders.length > 1) { + try { + extraHeaders = JSON.parse(customHeaders); + } catch (e) { + this.logger.warn( + `Could not parse the json in the customHeaders parameter. [${customHeaders}]`, + ); + } + } const requestOpts = { method: 'POST', headers: { 'Content-Type': contentType || 'application/json', Authorization: authorization || undefined, + ...extraHeaders, }, body, };