From 6a96587c5041ed9a6c51e84fc87c92179e79f460 Mon Sep 17 00:00:00 2001 From: Ron <43244104+rons4@users.noreply.github.com> Date: Tue, 23 Jun 2020 17:18:01 +0200 Subject: [PATCH 1/6] Initial version. --- .../nodes/SIGNL4/GenericFunctions.ts | 64 +++++ .../nodes-base/nodes/SIGNL4/signl4.node.ts | 233 ++++++++++++++++++ packages/nodes-base/nodes/SIGNL4/signl4.png | Bin 0 -> 3045 bytes 3 files changed, 297 insertions(+) create mode 100644 packages/nodes-base/nodes/SIGNL4/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/SIGNL4/signl4.node.ts create mode 100644 packages/nodes-base/nodes/SIGNL4/signl4.png diff --git a/packages/nodes-base/nodes/SIGNL4/GenericFunctions.ts b/packages/nodes-base/nodes/SIGNL4/GenericFunctions.ts new file mode 100644 index 0000000000..525a0b1ef9 --- /dev/null +++ b/packages/nodes-base/nodes/SIGNL4/GenericFunctions.ts @@ -0,0 +1,64 @@ +import { + IExecuteFunctions, + IHookFunctions, +} from 'n8n-core'; + +import { + ICredentialDataDecryptedObject, + IDataObject, +} from 'n8n-workflow'; + +/** + * Make an API request to MSG91 + * + * @param {IHookFunctions | IExecuteFunctions} this + * @param {object} message + * @returns {Promise} + */ +export async function SIGNL4ApiRequest(this: IHookFunctions | IExecuteFunctions, message: IDataObject): Promise { // tslint:disable-line:no-any + const credentials = this.getCredentials('SIGNL4Api'); + if (credentials === undefined) { + throw new Error('No credentials got returned!'); + } + + const teamsecret = credentials!.teamSecret as string; + + var response = null; + try { + response = await this.helpers.request({ + headers: {'Content-Type': 'application/json'}, + method: 'POST', + body: message, + qs: {}, + uri: `https://connect.signl4.com/webhook/${teamsecret}`, + json: true + }); + } + catch (error) { + + throw new Error(`I am behind. Status: ${JSON.stringify(teamsecret)}`); + + if (error && error.message) { + throw new Error(`Error sending the SIGNL4 request. Error: ${JSON.stringify(error.message)}`); + } + + throw new Error('Error sending the SIGNL4 request.'); + + throw error; + } + + return response; +} + + +function setPayload(credentials: ICredentialDataDecryptedObject, o?: IDataObject) { + if (!o) { + o = {}; + } + + o.p = credentials!.teamSecret as string; + o.json = 1; + o.sendwith = 'n8n'; + + return o; +} diff --git a/packages/nodes-base/nodes/SIGNL4/signl4.node.ts b/packages/nodes-base/nodes/SIGNL4/signl4.node.ts new file mode 100644 index 0000000000..cc665eadd4 --- /dev/null +++ b/packages/nodes-base/nodes/SIGNL4/signl4.node.ts @@ -0,0 +1,233 @@ +import {IExecuteFunctions,} from 'n8n-core'; +import {IDataObject, INodeExecutionData, INodeType, INodeTypeDescription,} from 'n8n-workflow'; +import {SIGNL4ApiRequest} from './GenericFunctions'; + +export class SIGNL4 implements INodeType { + description: INodeTypeDescription = { + displayName: 'SIGNL4', + name: 'SIGNL4', + icon: 'file:signl4.png', + group: ['transform'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Send SIGNL4 alert.', + defaults: { + name: 'SIGNL4', + color: '#0000FF', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'SIGNL4Api', + required: true, + } + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Alert', + value: 'alert', + }, + ], + default: 'alert', + description: 'The resource to operate on.', + }, + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'alert', + ], + }, + }, + options: [ + { + name: 'Send Alert', + value: 'send', + description: 'Send SIGNL4 alert.', + }, + ], + default: 'send', + description: 'The operation to perform.', + }, + { + displayName: 'Subject', + name: 'subject', + type: 'string', + default: 'SIGNL4 Alert', + placeholder: 'SIGNL4 Alert', + required: true, + displayOptions: { + show: { + operation: [ + 'send', + ], + resource: [ + 'alert', + ], + }, + }, + description: 'The subject of the SIGNL4 alert.', + }, + { + displayName: 'Body', + name: 'body', + type: 'string', + default: '', + placeholder: 'Alert description.', + required: false, + displayOptions: { + show: { + operation: [ + 'send', + ], + resource: [ + 'alert', + ], + }, + }, + description: 'A more detailed description for the alert.', + }, + { + displayName: 'S4-Service', + name: 'xS4Service', + type: 'string', + default: '', + required: false, + displayOptions: { + show: { + operation: [ + 'send', + ], + resource: [ + 'alert', + ], + }, + }, + description: 'Assigns the alert to the service/system category with the specified name.', + }, + { + displayName: 'S4-Location', + name: 'xS4Location', + type: 'string', + default: '', + required: false, + displayOptions: { + show: { + operation: [ + 'send', + ], + resource: [ + 'alert', + ], + }, + }, + description: 'Transmit location information (\'latitude, longitude\') with your event and display a map in the mobile app.', + }, + { + displayName: 'S4-AlertingScenario', + name: 'xS4AlertingScenario', + type: 'options', + options: [ + { + name: 'single_ack', + value: 'single_ack', + }, + { + name: 'multi_ack', + value: 'multi_ack', + }, + ], + default: 'single_ack', + required: false, + displayOptions: { + show: { + operation: [ + 'send', + ], + resource: [ + 'alert', + ], + }, + }, + description: 'Pass \'single_ack\' if only one person needs to confirm this Signl. Pass \'multi_ack\' in case this alert must be confirmed by the number of people who are on duty at the time this Singl is raised.', + }, + { + displayName: 'S4-ExternalID', + name: 'xS4ExternalId', + type: 'string', + default: '', + required: false, + displayOptions: { + show: { + operation: [ + 'send', + ], + resource: [ + 'alert', + ], + }, + }, + description: 'If the event originates from a record in a 3rd party system, use this parameter to pass the unique ID of that record. That ID will be communicated in outbound webhook notifications from SIGNL4, which is great for correlation/synchronization of that record with the alert.', + }, + { + displayName: 'S4-Filtering', + name: 'xS4Filtering', + type: 'boolean', + default: 'false', + required: false, + displayOptions: { + show: { + operation: [ + 'send', + ], + resource: [ + 'alert', + ], + }, + }, + description: 'Specify a boolean value of true or false to apply event filtering for this event, or not. If set to true, the event will only trigger a notification to the team, if it contains at least one keyword from one of your services and system categories (i.e. it is whitelisted)', + } + ], + }; + + async execute(this: IExecuteFunctions): Promise { + const returnData: IDataObject[] = []; + + for (let i = 0; i < this.getInputData().length; i++) { + const resource = this.getNodeParameter('resource', i); + if ('alert' !== resource) { + throw new Error(`The resource "${resource}" is not known!`); + } + + const operation = this.getNodeParameter('operation', i); + if ('send' !== operation) { + throw new Error(`The operation "${operation}" is not known!`); + } + + // Assemble JSON data + var message = { + Subject: this.getNodeParameter('subject', i), + Body: this.getNodeParameter('body', i) + } as IDataObject; + message['X-S4-Service'] = this.getNodeParameter('xS4Service', i); + message['X-S4-Location'] = this.getNodeParameter('xS4Location', i); + message['X-S4-AlertingScenario'] = this.getNodeParameter('xS4AlertingScenario', i); + message['X-S4-ExternalID'] = this.getNodeParameter('xS4ExternalId', i); + message['X-S4-Filtering'] = this.getNodeParameter('xS4Filtering', i); + + const responseData = await SIGNL4ApiRequest.call(this, message); + + returnData.push(responseData); + } + return [this.helpers.returnJsonArray(returnData)]; + } +} diff --git a/packages/nodes-base/nodes/SIGNL4/signl4.png b/packages/nodes-base/nodes/SIGNL4/signl4.png new file mode 100644 index 0000000000000000000000000000000000000000..205c94a5d2ba0e8d25d7b792050e341a8c8ab29b GIT binary patch literal 3045 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 Date: Tue, 23 Jun 2020 17:19:17 +0200 Subject: [PATCH 2/6] Add files via upload --- packages/nodes-base/nodes/SIGNL4/signl4.node.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/SIGNL4/signl4.node.ts b/packages/nodes-base/nodes/SIGNL4/signl4.node.ts index cc665eadd4..5b91df3814 100644 --- a/packages/nodes-base/nodes/SIGNL4/signl4.node.ts +++ b/packages/nodes-base/nodes/SIGNL4/signl4.node.ts @@ -10,7 +10,7 @@ export class SIGNL4 implements INodeType { group: ['transform'], version: 1, subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', - description: 'Send SIGNL4 alert.', + description: 'Send SIGNL4 alert. Find our more at https://www.signl4.com.', defaults: { name: 'SIGNL4', color: '#0000FF', From 474eac5005d36f973820cd89487c6a862feac03b Mon Sep 17 00:00:00 2001 From: Ron <43244104+rons4@users.noreply.github.com> Date: Tue, 23 Jun 2020 17:20:21 +0200 Subject: [PATCH 3/6] Initial version. --- .../credentials/SIGNL4Api.credentials.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 packages/nodes-base/credentials/SIGNL4Api.credentials.ts diff --git a/packages/nodes-base/credentials/SIGNL4Api.credentials.ts b/packages/nodes-base/credentials/SIGNL4Api.credentials.ts new file mode 100644 index 0000000000..4fa31c1422 --- /dev/null +++ b/packages/nodes-base/credentials/SIGNL4Api.credentials.ts @@ -0,0 +1,18 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class SIGNL4Api implements ICredentialType { + name = 'SIGNL4Api'; + displayName = 'SIGNL4 Webhook'; + properties = [ + { + displayName: 'Team Secret', + name: 'teamSecret', + type: 'string' as NodePropertyTypes, + default: '', + description: 'The team secret is the last part of your SIGNL4 webhook URL.' + }, + ]; +} From d59658ade0531c20eb6a71eedb36fc7dbe217b41 Mon Sep 17 00:00:00 2001 From: Ron <43244104+rons4@users.noreply.github.com> Date: Tue, 23 Jun 2020 17:23:39 +0200 Subject: [PATCH 4/6] Updated for the SIGNL4 node. --- packages/nodes-base/package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index a09d97d032..1b4a18a5cc 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-base", - "version": "0.65.0", + "version": "0.66.0", "description": "Base nodes of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", @@ -119,6 +119,7 @@ "dist/credentials/StripeApi.credentials.js", "dist/credentials/SalesmateApi.credentials.js", "dist/credentials/SegmentApi.credentials.js", + "dist/credentials/SIGNL4Api.credentials.js", "dist/credentials/SurveyMonkeyApi.credentials.js", "dist/credentials/SurveyMonkeyOAuth2Api.credentials.js", "dist/credentials/TelegramApi.credentials.js", @@ -260,6 +261,7 @@ "dist/nodes/Set.node.js", "dist/nodes/Shopify/Shopify.node.js", "dist/nodes/Shopify/ShopifyTrigger.node.js", + "dist/nodes/SIGNL4/SIGNL4.node.js", "dist/nodes/Slack/Slack.node.js", "dist/nodes/Sms77/Sms77.node.js", "dist/nodes/SplitInBatches.node.js", From 8f75087f8c454cb07f906feeabec47ae69ccb9c0 Mon Sep 17 00:00:00 2001 From: Ron <43244104+rons4@users.noreply.github.com> Date: Wed, 24 Jun 2020 08:45:39 +0200 Subject: [PATCH 5/6] Fixed some old debug output. --- packages/nodes-base/nodes/SIGNL4/GenericFunctions.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/nodes-base/nodes/SIGNL4/GenericFunctions.ts b/packages/nodes-base/nodes/SIGNL4/GenericFunctions.ts index 525a0b1ef9..e04e3b8393 100644 --- a/packages/nodes-base/nodes/SIGNL4/GenericFunctions.ts +++ b/packages/nodes-base/nodes/SIGNL4/GenericFunctions.ts @@ -36,8 +36,6 @@ export async function SIGNL4ApiRequest(this: IHookFunctions | IExecuteFunctions, } catch (error) { - throw new Error(`I am behind. Status: ${JSON.stringify(teamsecret)}`); - if (error && error.message) { throw new Error(`Error sending the SIGNL4 request. Error: ${JSON.stringify(error.message)}`); } From 5b269b45f1d247e4bbf8dfe453da5258d98dd717 Mon Sep 17 00:00:00 2001 From: ricardo Date: Thu, 25 Jun 2020 00:06:48 -0400 Subject: [PATCH 6/6] :sparkles: SIGNL4-Node --- .../credentials/SIGNL4Api.credentials.ts | 4 +- .../nodes/SIGNL4/GenericFunctions.ts | 72 ++-- .../nodes-base/nodes/SIGNL4/signl4.node.ts | 368 +++++++++++------- packages/nodes-base/package.json | 6 +- 4 files changed, 266 insertions(+), 184 deletions(-) diff --git a/packages/nodes-base/credentials/SIGNL4Api.credentials.ts b/packages/nodes-base/credentials/SIGNL4Api.credentials.ts index 4fa31c1422..842136de02 100644 --- a/packages/nodes-base/credentials/SIGNL4Api.credentials.ts +++ b/packages/nodes-base/credentials/SIGNL4Api.credentials.ts @@ -3,8 +3,8 @@ import { NodePropertyTypes, } from 'n8n-workflow'; -export class SIGNL4Api implements ICredentialType { - name = 'SIGNL4Api'; +export class Signl4Api implements ICredentialType { + name = 'signl4Api'; displayName = 'SIGNL4 Webhook'; properties = [ { diff --git a/packages/nodes-base/nodes/SIGNL4/GenericFunctions.ts b/packages/nodes-base/nodes/SIGNL4/GenericFunctions.ts index e04e3b8393..5281ae8c25 100644 --- a/packages/nodes-base/nodes/SIGNL4/GenericFunctions.ts +++ b/packages/nodes-base/nodes/SIGNL4/GenericFunctions.ts @@ -1,62 +1,52 @@ import { IExecuteFunctions, - IHookFunctions, } from 'n8n-core'; import { - ICredentialDataDecryptedObject, IDataObject, } from 'n8n-workflow'; +import { + OptionsWithUri, + } from 'request'; + /** - * Make an API request to MSG91 + * Make an API request to SIGNL4 * * @param {IHookFunctions | IExecuteFunctions} this * @param {object} message * @returns {Promise} */ -export async function SIGNL4ApiRequest(this: IHookFunctions | IExecuteFunctions, message: IDataObject): Promise { // tslint:disable-line:no-any - const credentials = this.getCredentials('SIGNL4Api'); - if (credentials === undefined) { - throw new Error('No credentials got returned!'); - } - - const teamsecret = credentials!.teamSecret as string; - var response = null; - try { - response = await this.helpers.request({ - headers: {'Content-Type': 'application/json'}, - method: 'POST', - body: message, - qs: {}, - uri: `https://connect.signl4.com/webhook/${teamsecret}`, - json: true - }); +export async function SIGNL4ApiRequest(this: IExecuteFunctions, method: string, resource: string, body: any = {}, query: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any + + let options: OptionsWithUri = { + headers: { + 'Accept': '*/*', + }, + method, + body, + qs: query, + uri: uri || ``, + json: true, + }; + + if (!Object.keys(body).length) { + delete options.body; } - catch (error) { - - if (error && error.message) { - throw new Error(`Error sending the SIGNL4 request. Error: ${JSON.stringify(error.message)}`); + if (!Object.keys(query).length) { + delete options.qs; + } + options = Object.assign({}, options, option); + + try { + return await this.helpers.request!(options); + } catch (error) { + + if (error.response && error.response.body && error.response.body.details) { + throw new Error(`SIGNL4 error response [${error.statusCode}]: ${error.response.body.details}`); } - - throw new Error('Error sending the SIGNL4 request.'); throw error; } - - return response; -} - - -function setPayload(credentials: ICredentialDataDecryptedObject, o?: IDataObject) { - if (!o) { - o = {}; - } - - o.p = credentials!.teamSecret as string; - o.json = 1; - o.sendwith = 'n8n'; - - return o; } diff --git a/packages/nodes-base/nodes/SIGNL4/signl4.node.ts b/packages/nodes-base/nodes/SIGNL4/signl4.node.ts index 5b91df3814..5a2ec290e3 100644 --- a/packages/nodes-base/nodes/SIGNL4/signl4.node.ts +++ b/packages/nodes-base/nodes/SIGNL4/signl4.node.ts @@ -1,27 +1,40 @@ -import {IExecuteFunctions,} from 'n8n-core'; -import {IDataObject, INodeExecutionData, INodeType, INodeTypeDescription,} from 'n8n-workflow'; -import {SIGNL4ApiRequest} from './GenericFunctions'; +import { + IExecuteFunctions, + BINARY_ENCODING, +} from 'n8n-core'; -export class SIGNL4 implements INodeType { +import { + IDataObject, + INodeExecutionData, + INodeType, + INodeTypeDescription, + IBinaryKeyData, +} from 'n8n-workflow'; + +import { + SIGNL4ApiRequest, +} from './GenericFunctions'; + +export class Signl4 implements INodeType { description: INodeTypeDescription = { displayName: 'SIGNL4', - name: 'SIGNL4', + name: 'signl4', icon: 'file:signl4.png', group: ['transform'], version: 1, subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', - description: 'Send SIGNL4 alert. Find our more at https://www.signl4.com.', + description: 'Consume SIGNL4 API.', defaults: { name: 'SIGNL4', - color: '#0000FF', + color: '#53afe8', }, inputs: ['main'], outputs: ['main'], credentials: [ { - name: 'SIGNL4Api', + name: 'signl4Api', required: true, - } + }, ], properties: [ { @@ -50,39 +63,22 @@ export class SIGNL4 implements INodeType { }, options: [ { - name: 'Send Alert', + name: 'Send', value: 'send', - description: 'Send SIGNL4 alert.', + description: 'Send an alert.', }, ], default: 'send', description: 'The operation to perform.', }, { - displayName: 'Subject', - name: 'subject', + displayName: 'Message', + name: 'message', type: 'string', - default: 'SIGNL4 Alert', - placeholder: 'SIGNL4 Alert', - required: true, - displayOptions: { - show: { - operation: [ - 'send', - ], - resource: [ - 'alert', - ], - }, + typeOptions: { + alwaysOpenEditWindow: true, }, - description: 'The subject of the SIGNL4 alert.', - }, - { - displayName: 'Body', - name: 'body', - type: 'string', default: '', - placeholder: 'Alert description.', required: false, displayOptions: { show: { @@ -97,11 +93,10 @@ export class SIGNL4 implements INodeType { description: 'A more detailed description for the alert.', }, { - displayName: 'S4-Service', - name: 'xS4Service', - type: 'string', - default: '', - required: false, + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', displayOptions: { show: { operation: [ @@ -112,121 +107,218 @@ export class SIGNL4 implements INodeType { ], }, }, - description: 'Assigns the alert to the service/system category with the specified name.', - }, - { - displayName: 'S4-Location', - name: 'xS4Location', - type: 'string', - default: '', - required: false, - displayOptions: { - show: { - operation: [ - 'send', - ], - resource: [ - 'alert', - ], - }, - }, - description: 'Transmit location information (\'latitude, longitude\') with your event and display a map in the mobile app.', - }, - { - displayName: 'S4-AlertingScenario', - name: 'xS4AlertingScenario', - type: 'options', + default: {}, options: [ { - name: 'single_ack', - value: 'single_ack', + displayName: 'Alerting Scenario', + name: 'alertingScenario', + type: 'options', + options: [ + { + name: 'Single ACK', + value: 'single_ack', + description: 'In case only one person needs to confirm this Signl.' + }, + { + name: 'Multi ACK', + value: 'multi_ack', + description: 'in case this alert must be confirmed by the number of people who are on duty at the time this Singl is raised', + }, + ], + default: 'single_ack', + required: false, }, { - name: 'multi_ack', - value: 'multi_ack', + displayName: 'Attachments', + name: 'attachmentsUi', + placeholder: 'Add Attachments', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + options: [ + { + name: 'attachmentsBinary', + displayName: 'Attachments Binary', + values: [ + { + displayName: 'Property Name', + name: 'property', + type: 'string', + placeholder: 'data', + default: '', + description: 'Name of the binary properties which contain data which should be added as attachment', + }, + ], + }, + ], + default: {}, + }, + { + displayName: 'External ID', + name: 'externalId', + type: 'string', + default: '', + description: `If the event originates from a record in a 3rd party system, use this parameter to pass
+ the unique ID of that record. That ID will be communicated in outbound webhook notifications from SIGNL4,
+ which is great for correlation/synchronization of that record with the alert.`, + }, + { + displayName: 'Filtering', + name: 'filtering', + type: 'boolean', + default: 'false', + description: `Specify a boolean value of true or false to apply event filtering for this event, or not.
+ If set to true, the event will only trigger a notification to the team, if it contains at least one keyword
+ from one of your services and system categories (i.e. it is whitelisted)`, + }, + { + displayName: 'Location', + name: 'locationFieldsUi', + type: 'fixedCollection', + placeholder: 'Add Location', + default: {}, + description: 'Transmit location information (\'latitude, longitude\') with your event and display a map in the mobile app.', + options: [ + { + name: 'locationFieldsValues', + displayName: 'Location', + values: [ + { + displayName: 'Latitude', + name: 'latitude', + type: 'string', + required: true, + description: 'The location latitude.', + default: '', + }, + { + displayName: 'Longitude', + name: 'longitude', + type: 'string', + required: true, + description: 'The location longitude.', + default: '', + }, + ], + } + ], + }, + { + displayName: 'Service', + name: 'service', + type: 'string', + default: '', + description: 'Assigns the alert to the service/system category with the specified name.', + }, + { + displayName: 'Title', + name: 'title', + type: 'string', + default: '', }, ], - default: 'single_ack', - required: false, - displayOptions: { - show: { - operation: [ - 'send', - ], - resource: [ - 'alert', - ], - }, - }, - description: 'Pass \'single_ack\' if only one person needs to confirm this Signl. Pass \'multi_ack\' in case this alert must be confirmed by the number of people who are on duty at the time this Singl is raised.', }, - { - displayName: 'S4-ExternalID', - name: 'xS4ExternalId', - type: 'string', - default: '', - required: false, - displayOptions: { - show: { - operation: [ - 'send', - ], - resource: [ - 'alert', - ], - }, - }, - description: 'If the event originates from a record in a 3rd party system, use this parameter to pass the unique ID of that record. That ID will be communicated in outbound webhook notifications from SIGNL4, which is great for correlation/synchronization of that record with the alert.', - }, - { - displayName: 'S4-Filtering', - name: 'xS4Filtering', - type: 'boolean', - default: 'false', - required: false, - displayOptions: { - show: { - operation: [ - 'send', - ], - resource: [ - 'alert', - ], - }, - }, - description: 'Specify a boolean value of true or false to apply event filtering for this event, or not. If set to true, the event will only trigger a notification to the team, if it contains at least one keyword from one of your services and system categories (i.e. it is whitelisted)', - } ], }; async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); const returnData: IDataObject[] = []; + const length = (items.length as unknown) as number; + const qs: IDataObject = {}; + let responseData; + const resource = this.getNodeParameter('resource', 0) as string; + const operation = this.getNodeParameter('operation', 0) as string; + for (let i = 0; i < length; i++) { + if (resource === 'alert') { + //https://connect.signl4.com/webhook/docs/index.html + if (operation === 'send') { + const message = this.getNodeParameter('message', i) as string; + const additionalFields = this.getNodeParameter('additionalFields',i) as IDataObject; - for (let i = 0; i < this.getInputData().length; i++) { - const resource = this.getNodeParameter('resource', i); - if ('alert' !== resource) { - throw new Error(`The resource "${resource}" is not known!`); + const data: IDataObject = { + message, + }; + + if (additionalFields.alertingScenario) { + data['X-S4-AlertingScenario'] = additionalFields.alertingScenario as string; + } + if (additionalFields.externalId) { + data['X-S4-ExternalID'] = additionalFields.externalId as string; + } + if (additionalFields.filtering) { + data['X-S4-Filtering'] = (additionalFields.filtering as boolean).toString(); + } + if (additionalFields.locationFieldsUi) { + const locationUi = (additionalFields.locationFieldsUi as IDataObject).locationFieldsValues as IDataObject; + if (locationUi) { + data['X-S4-Location'] = `${locationUi.latitude},${locationUi.longitude}`; + } + } + if (additionalFields.service) { + data['X-S4-Service'] = additionalFields.service as string; + } + if (additionalFields.title) { + data['title'] = additionalFields.title as string; + } + + const attachments = additionalFields.attachmentsUi as IDataObject; + + if (attachments) { + if (attachments.attachmentsBinary && items[i].binary) { + + const propertyName = (attachments.attachmentsBinary as IDataObject).property as string + + const binaryProperty = (items[i].binary as IBinaryKeyData)[propertyName]; + + if (binaryProperty) { + + const supportedFileExtension = ['png', 'jpg', 'txt']; + + if (!supportedFileExtension.includes(binaryProperty.fileExtension as string)) { + + throw new Error(`Invalid extension, just ${supportedFileExtension.join(',')} are supported}`); + } + + data['file'] = { + value: Buffer.from(binaryProperty.data, BINARY_ENCODING), + options: { + filename: binaryProperty.fileName, + contentType: binaryProperty.mimeType, + }, + } + + } else { + throw new Error(`Binary property ${propertyName} does not exist on input`); + } + } + } + + const credentials = this.getCredentials('signl4Api'); + + const endpoint = `https://connect.signl4.com/webhook/${credentials?.teamSecret}`; + + responseData = await SIGNL4ApiRequest.call( + this, + 'POST', + '', + {}, + {}, + endpoint, + { + formData: { + ...data, + }, + }, + ); + } } - - const operation = this.getNodeParameter('operation', i); - if ('send' !== operation) { - throw new Error(`The operation "${operation}" is not known!`); - } - - // Assemble JSON data - var message = { - Subject: this.getNodeParameter('subject', i), - Body: this.getNodeParameter('body', i) - } as IDataObject; - message['X-S4-Service'] = this.getNodeParameter('xS4Service', i); - message['X-S4-Location'] = this.getNodeParameter('xS4Location', i); - message['X-S4-AlertingScenario'] = this.getNodeParameter('xS4AlertingScenario', i); - message['X-S4-ExternalID'] = this.getNodeParameter('xS4ExternalId', i); - message['X-S4-Filtering'] = this.getNodeParameter('xS4Filtering', i); - - const responseData = await SIGNL4ApiRequest.call(this, message); - - returnData.push(responseData); + } + if (Array.isArray(responseData)) { + returnData.push.apply(returnData, responseData as IDataObject[]); + } else if (responseData !== undefined) { + returnData.push(responseData as IDataObject); } return [this.helpers.returnJsonArray(returnData)]; } diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 1b4a18a5cc..915b3c81da 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-base", - "version": "0.66.0", + "version": "0.65.0", "description": "Base nodes of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", @@ -119,7 +119,7 @@ "dist/credentials/StripeApi.credentials.js", "dist/credentials/SalesmateApi.credentials.js", "dist/credentials/SegmentApi.credentials.js", - "dist/credentials/SIGNL4Api.credentials.js", + "dist/credentials/Signl4Api.credentials.js", "dist/credentials/SurveyMonkeyApi.credentials.js", "dist/credentials/SurveyMonkeyOAuth2Api.credentials.js", "dist/credentials/TelegramApi.credentials.js", @@ -261,7 +261,7 @@ "dist/nodes/Set.node.js", "dist/nodes/Shopify/Shopify.node.js", "dist/nodes/Shopify/ShopifyTrigger.node.js", - "dist/nodes/SIGNL4/SIGNL4.node.js", + "dist/nodes/signl4/Signl4.node.js", "dist/nodes/Slack/Slack.node.js", "dist/nodes/Sms77/Sms77.node.js", "dist/nodes/SplitInBatches.node.js",