From 0d1b4611c8a32214f6c9bfd5103ae885e39d08e7 Mon Sep 17 00:00:00 2001 From: Tanay Pant <7481165+tanay1337@users.noreply.github.com> Date: Thu, 18 Jun 2020 10:08:31 +0200 Subject: [PATCH 1/8] =?UTF-8?q?=E2=9C=8D=EF=B8=8F=20Make=20a=20few=20langu?= =?UTF-8?q?age=20changes=20(#681)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CONTRIBUTING.md | 10 +++++----- LICENSE.md | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 568f8fc9a7..fcc046beef 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,7 +38,7 @@ The most important directories: execution, active webhooks and workflows - [/packages/editor-ui](/packages/editor-ui) - Vue frontend workflow editor - - [/packages/node-dev](/packages/node-dev) - Simple CLI to create new n8n-nodes + - [/packages/node-dev](/packages/node-dev) - CLI to create new n8n-nodes - [/packages/nodes-base](/packages/nodes-base) - Base n8n nodes - [/packages/workflow](/packages/workflow) - Workflow code with interfaces which get used by front- & backend @@ -159,7 +159,7 @@ tests of all packages. ## Create Custom Nodes -It is very easy to create own nodes for n8n. More information about that can +It is very straightforward to create your own nodes for n8n. More information about that can be found in the documentation of "n8n-node-dev" which is a small CLI which helps with n8n-node-development. @@ -177,9 +177,9 @@ If you want to create a node which should be added to n8n follow these steps: 1. Create a new folder for the new node. For a service named "Example" the folder would be called: `/packages/nodes-base/nodes/Example` - 1. If there is already a similar node simply copy the existing one in the new folder and rename it. If none exists yet, create a boilerplate node with [n8n-node-dev](https://github.com/n8n-io/n8n/tree/master/packages/node-dev) and copy that one in the folder. + 1. If there is already a similar node, copy the existing one in the new folder and rename it. If none exists yet, create a boilerplate node with [n8n-node-dev](https://github.com/n8n-io/n8n/tree/master/packages/node-dev) and copy that one in the folder. - 1. If the node needs credentials because it has to authenticate with an API or similar create new ones. Existing ones can be found in folder `/packages/nodes-base/credentials`. Also there it is the easiest to simply copy existing similar ones. + 1. If the node needs credentials because it has to authenticate with an API or similar create new ones. Existing ones can be found in folder `/packages/nodes-base/credentials`. Also there it is the easiest to copy existing similar ones. 1. Add the path to the new node (and optionally credentials) to package.json of `nodes-base`. It already contains a property `n8n` with its own keys `credentials` and `nodes`. @@ -236,6 +236,6 @@ docsify serve ./docs That we do not have any potential problems later it is sadly necessary to sign a [Contributor License Agreement](CONTRIBUTOR_LICENSE_AGREEMENT.md). That can be done literally with the push of a button. -We used the most simple one that exists. It is from [Indie Open Source](https://indieopensource.com/forms/cla) which uses plain English and is literally just a few lines long. +We used the most simple one that exists. It is from [Indie Open Source](https://indieopensource.com/forms/cla) which uses plain English and is literally only a few lines long. A bot will automatically comment on the pull request once it got opened asking for the agreement to be signed. Before it did not get signed it is sadly not possible to merge it in. diff --git a/LICENSE.md b/LICENSE.md index aac54547eb..c2aec2148e 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -215,7 +215,7 @@ Licensor: n8n GmbH same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright [2020] [n8n GmbH] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 55ed53579f9b7c22e818c49eead34c5cffceeb8f Mon Sep 17 00:00:00 2001 From: Rupenieks Date: Wed, 24 Jun 2020 15:46:56 +0200 Subject: [PATCH 2/8] Postmark trigger --- .../credentials/PostmarkApi.credentials.ts | 18 ++ .../nodes/Postmark/GenericFunctions.ts | 47 +++ .../nodes/Postmark/PostmarkTrigger.node.ts | 275 ++++++++++++++++++ .../nodes-base/nodes/Postmark/postmark.png | Bin 0 -> 3301 bytes packages/nodes-base/package.json | 2 + 5 files changed, 342 insertions(+) create mode 100644 packages/nodes-base/credentials/PostmarkApi.credentials.ts create mode 100644 packages/nodes-base/nodes/Postmark/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts create mode 100644 packages/nodes-base/nodes/Postmark/postmark.png diff --git a/packages/nodes-base/credentials/PostmarkApi.credentials.ts b/packages/nodes-base/credentials/PostmarkApi.credentials.ts new file mode 100644 index 0000000000..88df53aa30 --- /dev/null +++ b/packages/nodes-base/credentials/PostmarkApi.credentials.ts @@ -0,0 +1,18 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + + +export class PostmarkApi implements ICredentialType { + name = 'postmarkApi'; + displayName = 'Postmark API'; + properties = [ + { + displayName: 'Server Token', + name: 'serverToken', + type: 'string' as NodePropertyTypes, + default: '', + }, + ]; +} diff --git a/packages/nodes-base/nodes/Postmark/GenericFunctions.ts b/packages/nodes-base/nodes/Postmark/GenericFunctions.ts new file mode 100644 index 0000000000..d65777d881 --- /dev/null +++ b/packages/nodes-base/nodes/Postmark/GenericFunctions.ts @@ -0,0 +1,47 @@ +import { + OptionsWithUri, + } from 'request'; + +import { + IExecuteFunctions, + ILoadOptionsFunctions, +} from 'n8n-core'; + +import { + IDataObject, + IHookFunctions, + IWebhookFunctions +} from 'n8n-workflow'; + + +export async function postmarkApiRequest(this: IExecuteFunctions | IWebhookFunctions | IHookFunctions | ILoadOptionsFunctions, method : string, endpoint : string, body: any = {}, option: IDataObject = {}): Promise { // tslint:disable-line:no-any + const credentials = this.getCredentials('postmarkApi'); + + if (credentials === undefined) { + throw new Error('No credentials got returned!'); + } + + let options: OptionsWithUri = { + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'X-Postmark-Server-Token' : credentials.serverToken + }, + method, + body, + uri: 'https://api.postmarkapp.com' + endpoint, + json: true + }; + if (body === {}) { + delete options.body; + } + options = Object.assign({}, options, option); + + try { + return await this.helpers.request!(options); + } catch (error) { + throw new Error(`Postmark: ${error.statusCode} Message: ${error.message}`); + } +} + + diff --git a/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts b/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts new file mode 100644 index 0000000000..15af530cad --- /dev/null +++ b/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts @@ -0,0 +1,275 @@ +import { + IHookFunctions, + IWebhookFunctions, +} from 'n8n-core'; + +import { + INodeTypeDescription, + INodeType, + IWebhookResponseData, +} from 'n8n-workflow'; + +import { + postmarkApiRequest, +} from './GenericFunctions'; + +export class PostmarkTrigger implements INodeType { + description: INodeTypeDescription = { + displayName: 'Postmark Trigger', + name: 'postmarkTrigger', + icon: 'file:postmark.png', + group: ['trigger'], + version: 1, + description: 'Starts the workflow when Postmark events occur.', + defaults: { + name: 'Postmark Trigger', + color: '#fedd00', + }, + inputs: [], + outputs: ['main'], + credentials: [ + { + name: 'postmarkApi', + required: true, + }, + ], + webhooks: [ + { + name: 'default', + httpMethod: 'POST', + responseMode: 'onReceived', + path: 'webhook', + }, + ], + properties: [ + { + displayName: 'Open', + name: 'open', + type: 'boolean', + default: false, + description: 'Listing for if the Open webhook is enabled/disabled.', + }, + { + displayName: 'First Open Only', + name: 'postFirstOpenOnly', + type: 'boolean', + default: false, + displayOptions: { + show: { + open: [ + true + ], + }, + }, + description: 'Webhook will only post on first open if enabled.', + }, + { + displayName: 'Click', + name: 'click', + type: 'boolean', + default: false, + description: 'Listing for if the Click webhook is enabled/disabled.', + }, + { + displayName: 'Delivery', + name: 'delivery', + type: 'boolean', + default: false, + description: 'Listing for if the Delivery webhook is enabled/disabled.', + }, + { + displayName: 'Bounce', + name: 'bounce', + type: 'boolean', + default: false, + description: 'Listing for if the Bounce webhook is enabled/disabled.', + }, + { + displayName: 'Bounce Include Content', + name: 'bounceIncludeContent', + type: 'boolean', + default: false, + displayOptions: { + show: { + bounce: [ + true + ], + }, + }, + description: 'Webhook will send full bounce content if IncludeContent is enabled.', + }, + { + displayName: 'Spam Complaint', + name: 'spamComplaint', + type: 'boolean', + default: false, + description: 'Listing for if the Spam webhook is enabled/disabled.', + }, + { + displayName: 'Spam Complaint Include Content', + name: 'spamComplaintIncludeContent', + type: 'boolean', + default: false, + displayOptions: { + show: { + spamComplaint: [ + true + ], + }, + }, + description: 'Webhook will send full spam content if IncludeContent is enabled.', + }, + { + displayName: 'Subscription Change', + name: 'subscriptionChange', + type: 'boolean', + default: false, + description: 'Listing for if the Subscription Change webhook is enabled/disabled.', + }, + ], + + }; + + // @ts-ignore (because of request) + webhookMethods = { + default: { + async checkExists(this: IHookFunctions): Promise { + const webhookData = this.getWorkflowStaticData('node'); + + if (webhookData.webhookId === undefined) { + // No webhook id is set so no webhook can exist + return false; + } + + // Webhook got created before so check if it still exists + const endpoint = `/webhooks/${webhookData.webhookId}`; + + const responseData = await postmarkApiRequest.call(this, 'GET', endpoint, {}); + + if (responseData.ID === undefined) { + return false; + } + else if (responseData.ID === webhookData.id) { + return true; + } + return false; + }, + async create(this: IHookFunctions): Promise { + const webhookUrl = this.getNodeWebhookUrl('default'); + + const endpoint = `/webhooks`; + + // tslint:disable-next-line: no-any + const body : any = { + Url: webhookUrl, + Triggers: { + Open:{ + Enabled: false, + PostFirstOpenOnly: false + }, + Click:{ + Enabled: false + }, + Delivery:{ + Enabled: false + }, + Bounce:{ + Enabled: false, + IncludeContent: false + }, + SpamComplaint:{ + Enabled: false, + IncludeContent: false + }, + SubscriptionChange: { + Enabled: false + } + } + }; + + const open = this.getNodeParameter('open', 0); + const postFirstOpenOnly = this.getNodeParameter('postFirstOpenOnly', 0); + const click = this.getNodeParameter('click', 0); + const delivery = this.getNodeParameter('delivery', 0); + const bounce = this.getNodeParameter('bounce', 0); + const bounceIncludeContent = this.getNodeParameter('bounceIncludeContent', 0); + const spamComplaint = this.getNodeParameter('spamComplaint', 0); + const spamComplaintIncludeContent = this.getNodeParameter('spamComplaintIncludeContent', 0); + const subscriptionChange = this.getNodeParameter('subscriptionChange', 0); + + if (open) { + body.Triggers.Open.Enabled = true; + + if (postFirstOpenOnly) { + body.Triggers.Open.PostFirstOpenOnly = true; + } + } + if (click) { + body.Triggers.Click.Enabled = true; + } + if (delivery) { + body.Triggers.Delivery.Enabled = true; + } + if (bounce) { + body.Triggers.Bounce.Enabled = true; + + if (bounceIncludeContent) { + body.Triggers.Bounce.IncludeContent = true; + } + } + if (spamComplaint) { + body.Triggers.SpamComplaint.Enabled = true; + + if (spamComplaintIncludeContent) { + body.Triggers.SpamComplaint.IncludeContent = true; + } + } + if (subscriptionChange) { + body.Triggers.SubscriptionChange.Enabled = true; + } + + const responseData = await postmarkApiRequest.call(this, 'POST', endpoint, body); + + if (responseData.ID === undefined) { + // Required data is missing so was not successful + return false; + } + + const webhookData = this.getWorkflowStaticData('node'); + webhookData.webhookId = responseData.ID as string; + + return true; + }, + async delete(this: IHookFunctions): Promise { + const webhookData = this.getWorkflowStaticData('node'); + + if (webhookData.webhookId !== undefined) { + const endpoint = `/webhooks/${webhookData.webhookId}`; + const body = {}; + + try { + await postmarkApiRequest.call(this, 'DELETE', endpoint, body); + } catch (e) { + return false; + } + + // Remove from the static workflow data so that it is clear + // that no webhooks are registred anymore + delete webhookData.webhookId; + delete webhookData.webhookEvents; + } + + return true; + }, + }, + }; + + async webhook(this: IWebhookFunctions): Promise { + const req = this.getRequestObject(); + return { + workflowData: [ + this.helpers.returnJsonArray(req.body) + ], + }; + } +} diff --git a/packages/nodes-base/nodes/Postmark/postmark.png b/packages/nodes-base/nodes/Postmark/postmark.png new file mode 100644 index 0000000000000000000000000000000000000000..44f90b2a6c25f66c011e849512dbbbea5f128dfa GIT binary patch literal 3301 zcmcInc~lek7LKec0xB+`h%v4RlVq|%0s#vcpcQE>sZ`X+3z=bg6Cf8LxkGryVNcfb4HW$rn% z*Wb_EeCDE=7!1bT*T*XWy}Rnq=_crv@$Qx!z0HvMY*t_}W>)%h8s^w{)) zIUJba7sO47=F)jMcQ>r7nt>8XU?qfAOT zfG%geT(1e+-n(X81}KN=V9ueZ81CR1{t$;xVXP zkVcP$A^~SEOaWjrg$K|fQX~M8xFisUBFT{uZ%h|z?}cz<_36{wkGCxk;i4Q<8Dh{$ zFi0lDAOM0+bb!XC5CMo!rU4WZnH0;AVU;XpA#Phcp#rd1wjx3og;Br9+$yK>G&E(@1nFeWv=UY`UB?>)?j-neA_ zyvvvO?fW_>7q_1iCbpu8idHR>_&zZ`_UV4MX5EciWb3oEoE+;)k$9fxo`T}rrq{A^ z76n8H3g=igdj;ZZ=bG<5dHk^VfT6u?Fx;-f^=i!fq0qSY0DR&bg9?``5P|G+zE>r9 z-9RTRY`9Nc7e>E%dbf3WxFq)B)%nqvzgn{3MIn#R=QIn0d_qFfKQp1ryZ40uoRw9z zroOSUnC0!gn?j{t=)-MFfAG1$=C((MaB09cBMh}`IxCYaZ(hH? ze5Pmif!RMbHu^RDNF-;5JI^`x4Gu1}WSu&F`llB!Gy=_rc7q>t)Ej;Fl-J*Nc<*m6F|9x}f zZgPED$H*&lmP;pJS$O!fjv2=6ndQ`^2x5x4#-QZXsjC4C9P!%qALJJrgbynkzS>Yx z7r0qtk?!zl&v#)Ds2raNqr4@+#+9#%^E@lh{tA{e4a$D;l;)qg zbcvN1TW#sUO!#!|=&z=$s?OhXbzfU)8|M4|=@+AEmJfDnT6rbel@5m_I$m9lVQU@{ zRYK(0+uPr0X~DH7-#6$vdJHeWu;Pg8%iP>|icNWT{k<*PZg)D#D2Sj<4bF(Q3(NcR z^=70h!eFuWeo^kplXY$J&PVNNUif#ta)dk^(%OCrO-u} z(3zai+U^~3_EuGVe0*?hY*9t1Q$ba4$u4W=$jQxvU6=2ryt;2^T^$uBVhVIF#N^7Q z$+f57++2Mxg?b1CsxQ2Das7@e%XkuQ^kTz;j8{py-6K}dcc(_}ZHsL0yO#H1sI_Lh z$CT6`G+wZuIhC2p&-g4_L^ry^eE0E|G>b?(bGFp11m|K zQr^Cy=bE+185F}1MuDLK-3tFG?;khFUf>o>mr_vV_ui*VuZRF-|n^I}eN zk7R$Yut9soBqN%0%&hD1VjJM%&ia8v@|qtX|NhvrGoYel!58XB2AO%wmgG6!KR2|^ zI1yhw?A)`Ana=9xe3M|Njg7Xh7+I?&@3&}g9j@`L5$YZmzN}@i+=CTOk2kQ2=4+C= z+J3mmfB80s#bh6HD|z<)rlxoI5{@11X?d=W>8EUPS-y6=bJN3yC+h?QYR46Aq&aD7 ze4BikvE^P)5oJfJv6bDb+3oY~MvF07A%wCO#2;-}KWk-2*}N$)J#n|=?V>h=?Z^gK ziTvWcO9CH*r4CLhLxma6r=HSR8-DOIC_Ad&8eZG~VCa#9h6cwtee+fqO=fqM#Yg;b z{@doB{Vy^*zk0!_*=wBn+CPm@U$|xDrbDEPHrqxwuXhx*TwGt6M5rw&b&u;pdNA!p zvj_WwHx%4)yP`YTfBpIl+XLl*=KaZozd^W^@G#(_G^gm)aGckd%C>pd`DXt@jt`f9 z+tVAD>ulL#qb+Qzi4%3vY7JH0%Ven>1C@A2YSHVbcQam^UawkXkz@Sy5rnl&H|ZOe wby@5&eYzTx^7f#2SJEY`lBH3`11HoU3?)ewA9`$Vj{S)EuJ`lG_lVf>Z!h~rQvd(} literal 0 HcmV?d00001 diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index a09d97d032..99d390ee09 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -107,6 +107,7 @@ "dist/credentials/PayPalApi.credentials.js", "dist/credentials/PipedriveApi.credentials.js", "dist/credentials/Postgres.credentials.js", + "dist/credentials/PostmarkApi.credentials.js", "dist/credentials/Redis.credentials.js", "dist/credentials/RocketchatApi.credentials.js", "dist/credentials/RundeckApi.credentials.js", @@ -248,6 +249,7 @@ "dist/nodes/Pipedrive/Pipedrive.node.js", "dist/nodes/Pipedrive/PipedriveTrigger.node.js", "dist/nodes/Postgres/Postgres.node.js", + "dist/nodes/Postmark/PostmarkTrigger.node.js", "dist/nodes/ReadBinaryFile.node.js", "dist/nodes/ReadBinaryFiles.node.js", "dist/nodes/ReadPdf.node.js", From d199e1e638a62c909ec9a3034635c4f5409097db Mon Sep 17 00:00:00 2001 From: Rupenieks Date: Fri, 26 Jun 2020 13:32:48 +0200 Subject: [PATCH 3/8] checkExists modified --- .../nodes/Postmark/GenericFunctions.ts | 48 +++++ .../nodes/Postmark/PostmarkTrigger.node.ts | 197 +++++++----------- 2 files changed, 128 insertions(+), 117 deletions(-) diff --git a/packages/nodes-base/nodes/Postmark/GenericFunctions.ts b/packages/nodes-base/nodes/Postmark/GenericFunctions.ts index d65777d881..5844d28d7c 100644 --- a/packages/nodes-base/nodes/Postmark/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Postmark/GenericFunctions.ts @@ -37,6 +37,8 @@ export async function postmarkApiRequest(this: IExecuteFunctions | IWebhookFunct } options = Object.assign({}, options, option); + console.log(options); + try { return await this.helpers.request!(options); } catch (error) { @@ -44,4 +46,50 @@ export async function postmarkApiRequest(this: IExecuteFunctions | IWebhookFunct } } +// tslint:disable-next-line: no-any +export function convertTriggerObjectToStringArray (webhookObject : any) : string[] { + const triggers = webhookObject.Triggers; + const webhookEvents : string[] = []; + + // Translate Webhook trigger settings to string array + if (triggers.Open.Enabled) { + webhookEvents.push('open'); + } + if (triggers.Open.PostFirstOpenOnly) { + webhookEvents.push('firstOpen'); + } + if (triggers.Click.Enabled) { + webhookEvents.push('click'); + } + if (triggers.Delivery.Enabled) { + webhookEvents.push('delivery'); + } + if (triggers.Bounce.Enabled) { + webhookEvents.push('bounce'); + } + if (triggers.Bounce.IncludeContent) { + webhookEvents.push('bounceContent'); + } + if (triggers.SpamComplaint.Enabled) { + webhookEvents.push('spamComplaint'); + } + if (triggers.SpamComplaint.IncludeContent) { + webhookEvents.push('spamComplaintContent'); + } + if (triggers.SubscriptionChange.Enabled) { + webhookEvents.push('subscriptionChange'); + } + + return webhookEvents; +} + +export function eventExists (currentEvents : string[], webhookEvents: string[]) { + for (const currentEvent of currentEvents) { + if (!webhookEvents.includes(currentEvent)) { + return false; + } + } + return true; +} + diff --git a/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts b/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts index 15af530cad..f6f09afdd4 100644 --- a/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts +++ b/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts @@ -10,7 +10,9 @@ import { } from 'n8n-workflow'; import { - postmarkApiRequest, + convertTriggerObjectToStringArray, + eventExists, + postmarkApiRequest } from './GenericFunctions'; export class PostmarkTrigger implements INodeType { @@ -43,88 +45,54 @@ export class PostmarkTrigger implements INodeType { ], properties: [ { - displayName: 'Open', - name: 'open', - type: 'boolean', - default: false, - description: 'Listing for if the Open webhook is enabled/disabled.', - }, - { - displayName: 'First Open Only', - name: 'postFirstOpenOnly', - type: 'boolean', - default: false, - displayOptions: { - show: { - open: [ - true - ], + displayName: 'Events', + name: 'events', + type: 'multiOptions', + options: [ + { + name: 'Open', + value: 'open', + description: 'Trigger webhook on open.' }, - }, - description: 'Webhook will only post on first open if enabled.', - }, - { - displayName: 'Click', - name: 'click', - type: 'boolean', - default: false, - description: 'Listing for if the Click webhook is enabled/disabled.', - }, - { - displayName: 'Delivery', - name: 'delivery', - type: 'boolean', - default: false, - description: 'Listing for if the Delivery webhook is enabled/disabled.', - }, - { - displayName: 'Bounce', - name: 'bounce', - type: 'boolean', - default: false, - description: 'Listing for if the Bounce webhook is enabled/disabled.', - }, - { - displayName: 'Bounce Include Content', - name: 'bounceIncludeContent', - type: 'boolean', - default: false, - displayOptions: { - show: { - bounce: [ - true - ], + { + name: 'First Open', + value: 'firstOpen', + description: 'Trigger on first open only.' }, - }, - description: 'Webhook will send full bounce content if IncludeContent is enabled.', - }, - { - displayName: 'Spam Complaint', - name: 'spamComplaint', - type: 'boolean', - default: false, - description: 'Listing for if the Spam webhook is enabled/disabled.', - }, - { - displayName: 'Spam Complaint Include Content', - name: 'spamComplaintIncludeContent', - type: 'boolean', - default: false, - displayOptions: { - show: { - spamComplaint: [ - true - ], + { + name: 'Click', + value: 'click', }, - }, - description: 'Webhook will send full spam content if IncludeContent is enabled.', - }, - { - displayName: 'Subscription Change', - name: 'subscriptionChange', - type: 'boolean', - default: false, - description: 'Listing for if the Subscription Change webhook is enabled/disabled.', + { + name: 'Delivery', + value: 'delivery', + }, + { + name: 'Bounce', + value: 'bounce', + }, + { + name: 'Bounce Content', + value: 'bounceContent', + description: 'Webhook will send full bounce content.' + }, + { + name: 'Spam Complaint', + value: 'spamComplaint', + }, + { + name: 'Spam Complaint Content', + value: 'spamComplaintContent', + description: 'Webhook will send full bounce content.' + }, + { + name: 'Subscription Change', + value: 'subscriptionChange', + }, + ], + default: [], + required: true, + description: 'Webhook events that will be enabled for that endpoint.', }, ], @@ -135,23 +103,28 @@ export class PostmarkTrigger implements INodeType { default: { async checkExists(this: IHookFunctions): Promise { const webhookData = this.getWorkflowStaticData('node'); + const webhookUrl = this.getNodeWebhookUrl('default'); + const events = this.getNodeParameter('events') as string[]; - if (webhookData.webhookId === undefined) { - // No webhook id is set so no webhook can exist - return false; - } - - // Webhook got created before so check if it still exists - const endpoint = `/webhooks/${webhookData.webhookId}`; + // Get all webhooks + const endpoint = `/webhooks`; const responseData = await postmarkApiRequest.call(this, 'GET', endpoint, {}); - if (responseData.ID === undefined) { + // No webhooks exist + if (responseData.Webhooks.length === 0) { return false; } - else if (responseData.ID === webhookData.id) { - return true; + + // If webhooks exist, check if any match current settings + for (const webhook of responseData.Webhooks) { + if (webhook.Url === webhookUrl && eventExists(events, convertTriggerObjectToStringArray(webhook))) { + webhookData.webhookId = webhook.ID; + // webhook identical to current settings. re-assign webhook id to found webhook. + return true; + } } + return false; }, async create(this: IHookFunctions): Promise { @@ -187,44 +160,34 @@ export class PostmarkTrigger implements INodeType { } }; - const open = this.getNodeParameter('open', 0); - const postFirstOpenOnly = this.getNodeParameter('postFirstOpenOnly', 0); - const click = this.getNodeParameter('click', 0); - const delivery = this.getNodeParameter('delivery', 0); - const bounce = this.getNodeParameter('bounce', 0); - const bounceIncludeContent = this.getNodeParameter('bounceIncludeContent', 0); - const spamComplaint = this.getNodeParameter('spamComplaint', 0); - const spamComplaintIncludeContent = this.getNodeParameter('spamComplaintIncludeContent', 0); - const subscriptionChange = this.getNodeParameter('subscriptionChange', 0); + const events = this.getNodeParameter('events') as string[]; - if (open) { + if (events.includes('open')) { body.Triggers.Open.Enabled = true; - - if (postFirstOpenOnly) { - body.Triggers.Open.PostFirstOpenOnly = true; - } } - if (click) { + if (events.includes('firstOpen')) { + body.Triggers.Open.Enabled = true; + body.Triggers.Open.PostFirstOpenOnly = true; + } + if (events.includes('click')) { body.Triggers.Click.Enabled = true; } - if (delivery) { + if (events.includes('delivery')) { body.Triggers.Delivery.Enabled = true; } - if (bounce) { + if (events.includes('bounce')) { body.Triggers.Bounce.Enabled = true; - - if (bounceIncludeContent) { - body.Triggers.Bounce.IncludeContent = true; - } } - if (spamComplaint) { + if (events.includes('bounceContent')) { + body.Triggers.Bounce.IncludeContent = true; + } + if (events.includes('spamComplaint')) { body.Triggers.SpamComplaint.Enabled = true; - - if (spamComplaintIncludeContent) { - body.Triggers.SpamComplaint.IncludeContent = true; - } } - if (subscriptionChange) { + if (events.includes('spamComplaintContent')) { + body.Triggers.SpamComplaint.IncludeContent = true; + } + if (events.includes('subscriptionChange')) { body.Triggers.SubscriptionChange.Enabled = true; } From 7c9ecdb1722f5c0f80327cab6b43b91459c235d4 Mon Sep 17 00:00:00 2001 From: Rupenieks Date: Fri, 26 Jun 2020 14:56:24 +0200 Subject: [PATCH 4/8] Removed console log --- packages/nodes-base/nodes/Postmark/GenericFunctions.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/nodes-base/nodes/Postmark/GenericFunctions.ts b/packages/nodes-base/nodes/Postmark/GenericFunctions.ts index 5844d28d7c..115fac51f0 100644 --- a/packages/nodes-base/nodes/Postmark/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Postmark/GenericFunctions.ts @@ -37,8 +37,6 @@ export async function postmarkApiRequest(this: IExecuteFunctions | IWebhookFunct } options = Object.assign({}, options, option); - console.log(options); - try { return await this.helpers.request!(options); } catch (error) { From 87b67d66701e1e820e24e08513cd917a44c7f867 Mon Sep 17 00:00:00 2001 From: Rupenieks Date: Tue, 7 Jul 2020 09:35:15 +0200 Subject: [PATCH 5/8] :white_check_mark: Replaced png and added descriptions to all events --- .../nodes/Postmark/PostmarkTrigger.node.ts | 5 +++++ .../nodes-base/nodes/Postmark/postmark.png | Bin 3301 -> 1297 bytes 2 files changed, 5 insertions(+) diff --git a/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts b/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts index f6f09afdd4..9cd8f7b01c 100644 --- a/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts +++ b/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts @@ -62,14 +62,17 @@ export class PostmarkTrigger implements INodeType { { name: 'Click', value: 'click', + description: 'Trigger on click.' }, { name: 'Delivery', value: 'delivery', + description: 'Trigger on delivery.' }, { name: 'Bounce', value: 'bounce', + description: 'Trigger on bounce.' }, { name: 'Bounce Content', @@ -79,6 +82,7 @@ export class PostmarkTrigger implements INodeType { { name: 'Spam Complaint', value: 'spamComplaint', + description: 'Trigger on spam complaint.' }, { name: 'Spam Complaint Content', @@ -88,6 +92,7 @@ export class PostmarkTrigger implements INodeType { { name: 'Subscription Change', value: 'subscriptionChange', + description: 'Trigger on subscription change.' }, ], default: [], diff --git a/packages/nodes-base/nodes/Postmark/postmark.png b/packages/nodes-base/nodes/Postmark/postmark.png index 44f90b2a6c25f66c011e849512dbbbea5f128dfa..6298b4ae94bdbd1b930a5bdd870200fa9f8f157a 100644 GIT binary patch delta 1269 zcmV2c7k>o9NklH0HK@eA>;KxcAA})#x!JQ}wZbd=6&_clvw1|S}LX?VP zYCo(=o3#0Al4&v@@7;S`%rqTaXx==gGnUSOF&~dR^L}&Qx##@veM3Esn@(P{0e_OH z1>GpMpc|zYbfeUQZj@TkYlwi><%<$P6JYrvE(Ena8Xy!MunS^D_jD*dWl}+oHR-F7Mm3 zbno_DTVSD@AZL>0w2}~`rk0DWFn`4o#xHo|Q*Gb@dl z$;F)id52*yrlUwmVDVaX@q9Jg;>dgt|tOlR>5gVHH;%igiEjt>s6`|Wt9QcILU z5FUK#+@rhaVQWb4Bfrge5B3t#GOoI6+mu6GC)nqlZSrk)hJTclqmB#)y)d zFL+;mpWSoMQddQE^;t=U za1B}ntRcPHOsgtX0WvUA@A`0b*YbgVb9as80b8q?#*KhZ1%Ga^uefTOLHM3)4_r1A z4fI7@Hy5_uk>5N}^t=cT$|OtlIzu~$Ed%*CUpbpeSNt>y5qbu`ufAs?4hpzR`(y24 zM%|$mG#jVLbb^AhjxpXwWdO!ht1L|*F-9jC)$!bA2N-gL{sRi9A fwV)fNn-cn8xOMsu?|pBW00000NkvXXu0mjfo}+Cw literal 3301 zcmcInc~lek7LKec0xB+`h%v4RlVq|%0s#vcpcQE>sZ`X+3z=bg6Cf8LxkGryVNcfb4HW$rn% z*Wb_EeCDE=7!1bT*T*XWy}Rnq=_crv@$Qx!z0HvMY*t_}W>)%h8s^w{)) zIUJba7sO47=F)jMcQ>r7nt>8XU?qfAOT zfG%geT(1e+-n(X81}KN=V9ueZ81CR1{t$;xVXP zkVcP$A^~SEOaWjrg$K|fQX~M8xFisUBFT{uZ%h|z?}cz<_36{wkGCxk;i4Q<8Dh{$ zFi0lDAOM0+bb!XC5CMo!rU4WZnH0;AVU;XpA#Phcp#rd1wjx3og;Br9+$yK>G&E(@1nFeWv=UY`UB?>)?j-neA_ zyvvvO?fW_>7q_1iCbpu8idHR>_&zZ`_UV4MX5EciWb3oEoE+;)k$9fxo`T}rrq{A^ z76n8H3g=igdj;ZZ=bG<5dHk^VfT6u?Fx;-f^=i!fq0qSY0DR&bg9?``5P|G+zE>r9 z-9RTRY`9Nc7e>E%dbf3WxFq)B)%nqvzgn{3MIn#R=QIn0d_qFfKQp1ryZ40uoRw9z zroOSUnC0!gn?j{t=)-MFfAG1$=C((MaB09cBMh}`IxCYaZ(hH? ze5Pmif!RMbHu^RDNF-;5JI^`x4Gu1}WSu&F`llB!Gy=_rc7q>t)Ej;Fl-J*Nc<*m6F|9x}f zZgPED$H*&lmP;pJS$O!fjv2=6ndQ`^2x5x4#-QZXsjC4C9P!%qALJJrgbynkzS>Yx z7r0qtk?!zl&v#)Ds2raNqr4@+#+9#%^E@lh{tA{e4a$D;l;)qg zbcvN1TW#sUO!#!|=&z=$s?OhXbzfU)8|M4|=@+AEmJfDnT6rbel@5m_I$m9lVQU@{ zRYK(0+uPr0X~DH7-#6$vdJHeWu;Pg8%iP>|icNWT{k<*PZg)D#D2Sj<4bF(Q3(NcR z^=70h!eFuWeo^kplXY$J&PVNNUif#ta)dk^(%OCrO-u} z(3zai+U^~3_EuGVe0*?hY*9t1Q$ba4$u4W=$jQxvU6=2ryt;2^T^$uBVhVIF#N^7Q z$+f57++2Mxg?b1CsxQ2Das7@e%XkuQ^kTz;j8{py-6K}dcc(_}ZHsL0yO#H1sI_Lh z$CT6`G+wZuIhC2p&-g4_L^ry^eE0E|G>b?(bGFp11m|K zQr^Cy=bE+185F}1MuDLK-3tFG?;khFUf>o>mr_vV_ui*VuZRF-|n^I}eN zk7R$Yut9soBqN%0%&hD1VjJM%&ia8v@|qtX|NhvrGoYel!58XB2AO%wmgG6!KR2|^ zI1yhw?A)`Ana=9xe3M|Njg7Xh7+I?&@3&}g9j@`L5$YZmzN}@i+=CTOk2kQ2=4+C= z+J3mmfB80s#bh6HD|z<)rlxoI5{@11X?d=W>8EUPS-y6=bJN3yC+h?QYR46Aq&aD7 ze4BikvE^P)5oJfJv6bDb+3oY~MvF07A%wCO#2;-}KWk-2*}N$)J#n|=?V>h=?Z^gK ziTvWcO9CH*r4CLhLxma6r=HSR8-DOIC_Ad&8eZG~VCa#9h6cwtee+fqO=fqM#Yg;b z{@doB{Vy^*zk0!_*=wBn+CPm@U$|xDrbDEPHrqxwuXhx*TwGt6M5rw&b&u;pdNA!p zvj_WwHx%4)yP`YTfBpIl+XLl*=KaZozd^W^@G#(_G^gm)aGckd%C>pd`DXt@jt`f9 z+tVAD>ulL#qb+Qzi4%3vY7JH0%Ven>1C@A2YSHVbcQam^UawkXkz@Sy5rnl&H|ZOe wby@5&eYzTx^7f#2SJEY`lBH3`11HoU3?)ewA9`$Vj{S)EuJ`lG_lVf>Z!h~rQvd(} From d74e59801a6e5de7ecd5ec0d2d5c7d14e2bbeed3 Mon Sep 17 00:00:00 2001 From: Rupenieks Date: Tue, 7 Jul 2020 10:48:56 +0200 Subject: [PATCH 6/8] Credentials name to Server API Token --- packages/nodes-base/credentials/PostmarkApi.credentials.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/credentials/PostmarkApi.credentials.ts b/packages/nodes-base/credentials/PostmarkApi.credentials.ts index 88df53aa30..b5f73621c4 100644 --- a/packages/nodes-base/credentials/PostmarkApi.credentials.ts +++ b/packages/nodes-base/credentials/PostmarkApi.credentials.ts @@ -9,7 +9,7 @@ export class PostmarkApi implements ICredentialType { displayName = 'Postmark API'; properties = [ { - displayName: 'Server Token', + displayName: 'Server API Token', name: 'serverToken', type: 'string' as NodePropertyTypes, default: '', From 657d5498d6c2e18cd3de942decdf6504e1a326f1 Mon Sep 17 00:00:00 2001 From: Rupenieks Date: Tue, 7 Jul 2020 11:44:55 +0200 Subject: [PATCH 7/8] :construction: Incorrect event description fixed --- packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts b/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts index 9cd8f7b01c..ed902a1347 100644 --- a/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts +++ b/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts @@ -87,7 +87,7 @@ export class PostmarkTrigger implements INodeType { { name: 'Spam Complaint Content', value: 'spamComplaintContent', - description: 'Webhook will send full bounce content.' + description: 'Webhook will send full spam complaint content.' }, { name: 'Subscription Change', From 97c8af661c6616a999cbfd58f6df00497c6e0dba Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Tue, 7 Jul 2020 20:35:58 +0200 Subject: [PATCH 8/8] :zap: Small improvement on PostmarkTrigger-Node --- .../nodes/Postmark/GenericFunctions.ts | 8 +- .../nodes/Postmark/PostmarkTrigger.node.ts | 83 +++++++++++-------- 2 files changed, 52 insertions(+), 39 deletions(-) diff --git a/packages/nodes-base/nodes/Postmark/GenericFunctions.ts b/packages/nodes-base/nodes/Postmark/GenericFunctions.ts index 115fac51f0..df1e3a1f09 100644 --- a/packages/nodes-base/nodes/Postmark/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Postmark/GenericFunctions.ts @@ -66,13 +66,15 @@ export function convertTriggerObjectToStringArray (webhookObject : any) : string webhookEvents.push('bounce'); } if (triggers.Bounce.IncludeContent) { - webhookEvents.push('bounceContent'); + webhookEvents.push('includeContent'); } if (triggers.SpamComplaint.Enabled) { webhookEvents.push('spamComplaint'); } if (triggers.SpamComplaint.IncludeContent) { - webhookEvents.push('spamComplaintContent'); + if (!webhookEvents.includes('IncludeContent')) { + webhookEvents.push('includeContent'); + } } if (triggers.SubscriptionChange.Enabled) { webhookEvents.push('subscriptionChange'); @@ -89,5 +91,3 @@ export function eventExists (currentEvents : string[], webhookEvents: string[]) } return true; } - - diff --git a/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts b/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts index ed902a1347..4956d647b7 100644 --- a/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts +++ b/packages/nodes-base/nodes/Postmark/PostmarkTrigger.node.ts @@ -50,55 +50,69 @@ export class PostmarkTrigger implements INodeType { type: 'multiOptions', options: [ { - name: 'Open', - value: 'open', - description: 'Trigger webhook on open.' - }, - { - name: 'First Open', - value: 'firstOpen', - description: 'Trigger on first open only.' + name: 'Bounce', + value: 'bounce', + description: 'Trigger on bounce.', }, { name: 'Click', value: 'click', - description: 'Trigger on click.' + description: 'Trigger on click.', }, { name: 'Delivery', value: 'delivery', - description: 'Trigger on delivery.' + description: 'Trigger on delivery.', }, { - name: 'Bounce', - value: 'bounce', - description: 'Trigger on bounce.' - }, - { - name: 'Bounce Content', - value: 'bounceContent', - description: 'Webhook will send full bounce content.' + name: 'Open', + value: 'open', + description: 'Trigger webhook on open.', }, { name: 'Spam Complaint', value: 'spamComplaint', - description: 'Trigger on spam complaint.' - }, - { - name: 'Spam Complaint Content', - value: 'spamComplaintContent', - description: 'Webhook will send full spam complaint content.' + description: 'Trigger on spam complaint.', }, { name: 'Subscription Change', value: 'subscriptionChange', - description: 'Trigger on subscription change.' + description: 'Trigger on subscription change.', }, ], default: [], required: true, description: 'Webhook events that will be enabled for that endpoint.', }, + { + displayName: 'First Open', + name: 'firstOpen', + description: 'Only fires on first open for event "Open".', + type: 'boolean', + default: false, + displayOptions: { + show: { + events: [ + 'open', + ], + }, + }, + }, + { + displayName: 'Include Content', + name: 'includeContent', + description: 'Includes message content for events "Bounce" and "Spam Complaint".', + type: 'boolean', + default: false, + displayOptions: { + show: { + events: [ + 'bounce', + 'spamComplaint', + ], + }, + }, + }, ], }; @@ -110,6 +124,12 @@ export class PostmarkTrigger implements INodeType { const webhookData = this.getWorkflowStaticData('node'); const webhookUrl = this.getNodeWebhookUrl('default'); const events = this.getNodeParameter('events') as string[]; + if (this.getNodeParameter('includeContent') as boolean) { + events.push('includeContent'); + } + if (this.getNodeParameter('firstOpen') as boolean) { + events.push('firstOpen'); + } // Get all webhooks const endpoint = `/webhooks`; @@ -169,10 +189,7 @@ export class PostmarkTrigger implements INodeType { if (events.includes('open')) { body.Triggers.Open.Enabled = true; - } - if (events.includes('firstOpen')) { - body.Triggers.Open.Enabled = true; - body.Triggers.Open.PostFirstOpenOnly = true; + body.Triggers.Open.PostFirstOpenOnly = this.getNodeParameter('firstOpen') as boolean; } if (events.includes('click')) { body.Triggers.Click.Enabled = true; @@ -182,15 +199,11 @@ export class PostmarkTrigger implements INodeType { } if (events.includes('bounce')) { body.Triggers.Bounce.Enabled = true; - } - if (events.includes('bounceContent')) { - body.Triggers.Bounce.IncludeContent = true; + body.Triggers.Bounce.IncludeContent = this.getNodeParameter('includeContent') as boolean; } if (events.includes('spamComplaint')) { body.Triggers.SpamComplaint.Enabled = true; - } - if (events.includes('spamComplaintContent')) { - body.Triggers.SpamComplaint.IncludeContent = true; + body.Triggers.SpamComplaint.IncludeContent = this.getNodeParameter('includeContent') as boolean; } if (events.includes('subscriptionChange')) { body.Triggers.SubscriptionChange.Enabled = true;