From 55ed53579f9b7c22e818c49eead34c5cffceeb8f Mon Sep 17 00:00:00 2001 From: Rupenieks Date: Wed, 24 Jun 2020 15:46:56 +0200 Subject: [PATCH] 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",