From 0c820d80d2ea918b17e0274c8768e2ee07292bd9 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Tue, 13 Oct 2020 02:56:24 -0400 Subject: [PATCH] :sparkles: Add Vonage-Node (#1033) * :sparkles: Vonage-Node * :zap: Improvements * :zap: fix issue with description --- .../credentials/VonageApi.credentials.ts | 23 + .../nodes/Vonage/GenericFunctions.ts | 50 ++ .../nodes-base/nodes/Vonage/Vonage.node.ts | 510 ++++++++++++++++++ packages/nodes-base/nodes/Vonage/vonage.png | Bin 0 -> 2633 bytes packages/nodes-base/package.json | 2 + 5 files changed, 585 insertions(+) create mode 100644 packages/nodes-base/credentials/VonageApi.credentials.ts create mode 100644 packages/nodes-base/nodes/Vonage/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/Vonage/Vonage.node.ts create mode 100644 packages/nodes-base/nodes/Vonage/vonage.png diff --git a/packages/nodes-base/credentials/VonageApi.credentials.ts b/packages/nodes-base/credentials/VonageApi.credentials.ts new file mode 100644 index 000000000..67fe4bae5 --- /dev/null +++ b/packages/nodes-base/credentials/VonageApi.credentials.ts @@ -0,0 +1,23 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class VonageApi implements ICredentialType { + name = 'vonageApi'; + displayName = 'Vonage API'; + properties = [ + { + displayName: 'API Key', + name: 'apiKey', + type: 'string' as NodePropertyTypes, + default: '', + }, + { + displayName: 'API Secret', + name: 'apiSecret', + type: 'string' as NodePropertyTypes, + default: '', + }, + ]; +} diff --git a/packages/nodes-base/nodes/Vonage/GenericFunctions.ts b/packages/nodes-base/nodes/Vonage/GenericFunctions.ts new file mode 100644 index 000000000..465620ccc --- /dev/null +++ b/packages/nodes-base/nodes/Vonage/GenericFunctions.ts @@ -0,0 +1,50 @@ +import { + OptionsWithUri, +} from 'request'; + +import { + IExecuteFunctions, + IExecuteSingleFunctions, + ILoadOptionsFunctions, +} from 'n8n-core'; + +import { + IDataObject, +} from 'n8n-workflow'; + +export async function vonageApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, path: string, body: any = {}, qs: IDataObject = {}, option = {}): Promise { // tslint:disable-line:no-any + + const credentials = this.getCredentials('vonageApi') as IDataObject; + + body.api_key = credentials.apiKey as string; + + body.api_secret = credentials.apiSecret as string; + + const options: OptionsWithUri = { + method, + form: body, + qs, + uri: `https://rest.nexmo.com${path}`, + json: true, + }; + + try { + if (Object.keys(body).length === 0) { + delete options.body; + } + //@ts-ignore + return await this.helpers.request.call(this, options); + } catch (error) { + if (error.response && error.response.body && error.response.body.error) { + + let errors = error.response.body.error.errors; + + errors = errors.map((e: IDataObject) => e.message); + // Try to return the error prettier + throw new Error( + `Vonage error response [${error.statusCode}]: ${errors.join('|')}` + ); + } + throw error; + } +} diff --git a/packages/nodes-base/nodes/Vonage/Vonage.node.ts b/packages/nodes-base/nodes/Vonage/Vonage.node.ts new file mode 100644 index 000000000..500b14d07 --- /dev/null +++ b/packages/nodes-base/nodes/Vonage/Vonage.node.ts @@ -0,0 +1,510 @@ +import { + IExecuteFunctions, +} from 'n8n-core'; + +import { + IDataObject, + INodeExecutionData, + INodeType, + INodeTypeDescription, +} from 'n8n-workflow'; + +import { + vonageApiRequest, +} from './GenericFunctions'; + +export class Vonage implements INodeType { + description: INodeTypeDescription = { + displayName: 'Vonage', + name: 'vonage', + icon: 'file:vonage.png', + group: ['input'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Consume Vonage API', + defaults: { + name: 'Vonage', + color: '#000000', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'vonageApi', + required: true, + }, + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'SMS', + value: 'sms', + }, + ], + default: 'sms', + description: 'The resource to operate on.' + }, + { + displayName: 'Operation', + name: 'operation', + type: 'options', + options: [ + { + name: 'Send', + value: 'send', + }, + ], + displayOptions: { + show: { + resource: [ + 'sms', + ], + }, + }, + default: 'send', + description: 'The resource to operate on.' + }, + { + displayName: 'From', + name: 'from', + type: 'string', + displayOptions: { + show: { + resource: [ + 'sms', + ], + operation: [ + 'send', + ], + }, + }, + default: '', + description: `The name or number the message should be sent from`, + }, + { + displayName: 'To', + name: 'to', + type: 'string', + displayOptions: { + show: { + resource: [ + 'sms', + ], + operation: [ + 'send', + ], + }, + }, + default: '', + description: `The number that the message should be sent to. Numbers are specified in E.164 format.`, + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + displayOptions: { + show: { + resource: [ + 'sms', + ], + operation: [ + 'send', + ], + }, + }, + options: [ + { + name: 'Binary', + value: 'binary', + }, + { + name: 'Text', + value: 'text', + }, + { + name: 'Wappush', + value: 'wappush', + }, + { + name: 'Unicode', + value: 'unicode', + }, + { + name: 'VCAL', + value: 'vcal', + }, + { + name: 'VCARD', + value: 'vcard', + }, + ], + default: 'text', + description: 'The format of the message body', + }, + // { + // displayName: 'Binary Property', + // name: 'binaryPropertyName', + // displayOptions: { + // show: { + // resource: [ + // 'sms', + // ], + // operation: [ + // 'send', + // ], + // type: [ + // 'binary', + // ], + // }, + // }, + // type: 'string', + // default: 'data', + // description: 'Object property name which holds binary data.', + // required: true, + // }, + { + displayName: 'Body', + name: 'body', + type: 'string', + displayOptions: { + show: { + resource: [ + 'sms', + ], + operation: [ + 'send', + ], + type: [ + 'binary', + ], + }, + }, + default: '', + description: 'Hex encoded binary data', + }, + { + displayName: 'UDH', + name: 'udh', + type: 'string', + displayOptions: { + show: { + resource: [ + 'sms', + ], + operation: [ + 'send', + ], + type: [ + 'binary', + ], + }, + }, + default: '', + description: 'Your custom Hex encoded User Data Header', + }, + { + displayName: 'Title', + name: 'title', + displayOptions: { + show: { + resource: [ + 'sms', + ], + operation: [ + 'send', + ], + type: [ + 'wappush', + ], + }, + }, + type: 'string', + default: '', + description: 'The title for a wappush SMS', + }, + { + displayName: 'URL', + name: 'url', + type: 'string', + displayOptions: { + show: { + resource: [ + 'sms', + ], + operation: [ + 'send', + ], + type: [ + 'wappush', + ], + }, + }, + default: '', + description: 'The URL of your website', + }, + { + displayName: 'Validity (in minutes)', + name: 'validity', + type: 'number', + default: 0, + displayOptions: { + show: { + resource: [ + 'sms', + ], + operation: [ + 'send', + ], + type: [ + 'wappush', + ], + }, + }, + description: 'The availability for an SMS in minutes', + }, + { + displayName: 'Message', + name: 'message', + type: 'string', + displayOptions: { + show: { + resource: [ + 'sms', + ], + operation: [ + 'send', + ], + type: [ + 'text', + 'unicode', + ], + }, + }, + default: '', + description: `The body of the message being sent`, + }, + { + displayName: 'VCard', + name: 'vcard', + type: 'string', + displayOptions: { + show: { + resource: [ + 'sms', + ], + operation: [ + 'send', + ], + type: [ + 'vcard', + ], + }, + }, + default: '', + description: 'A business card in vCard format', + }, + { + displayName: 'VCal', + name: 'vcal', + type: 'string', + displayOptions: { + show: { + resource: [ + 'sms', + ], + operation: [ + 'send', + ], + type: [ + 'vcal', + ], + }, + }, + default: '', + description: 'A calendar event in vCal format', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + displayOptions: { + show: { + resource: [ + 'sms', + ], + operation: [ + 'send', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Account Ref', + name: 'account-ref', + type: 'string', + default: '', + description: 'An optional string used to identify separate accounts using the SMS endpoint for billing purposes. To use this feature, please email support@nexmo.com', + }, + { + displayName: 'Callback', + name: 'callback', + type: 'string', + default: '', + description: 'The webhook endpoint the delivery receipt for this sms is sent to. This parameter overrides the webhook endpoint you set in Dashboard.', + }, + { + displayName: 'Client Ref', + name: 'client-ref', + type: 'string', + default: '', + description: 'You can optionally include your own reference of up to 40 characters.', + }, + { + displayName: 'Message Class', + name: 'message-class', + type: 'options', + options: [ + { + name: '0', + value: 0, + }, + { + name: '1', + value: 1, + }, + { + name: '2', + value: 2, + }, + { + name: '3', + value: 3, + }, + ], + default: '', + description: 'The Data Coding Scheme value of the message', + }, + { + displayName: 'Protocol ID', + name: 'protocol-id', + type: 'string', + default: '', + description: 'The value of the protocol identifier to use. Ensure that the value is aligned with udh.', + }, + { + displayName: 'Status Report Req', + name: 'status-report-req', + type: 'boolean', + default: false, + description: 'Boolean indicating if you like to receive a Delivery Receipt.', + }, + { + displayName: 'TTL (in minutes)', + name: 'ttl', + type: 'number', + default: 4320, + description: 'By default Nexmo attempt delivery for 72 hours', + }, + ], + }, + ], + }; + + 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 === 'sms') { + + if (operation === 'send') { + + const from = this.getNodeParameter('from', i) as string; + + const to = this.getNodeParameter('to', i) as string; + + const type = this.getNodeParameter('type', i) as string; + + const body: IDataObject = { + from, + to, + type, + }; + + if (type === 'text' || type === 'unicode') { + const message = this.getNodeParameter('message', i) as string; + + body.text = message; + } + + if (type === 'binary') { + const data = this.getNodeParameter('body', i) as string; + + const udh = this.getNodeParameter('udh', i) as string; + + body.udh = udh; + + body.body = data; + + } + + if (type === 'wappush') { + const title = this.getNodeParameter('title', i) as string; + + const url = this.getNodeParameter('url', i) as string; + + const validity = this.getNodeParameter('validity', i) as number; + + body.title = title; + + body.url = url; + + body.validity = validity * 60000; + } + + if (type === 'vcard') { + const vcard = this.getNodeParameter('vcard', i) as string; + + body.vcard = vcard; + } + + if (type === 'vcal') { + const vcal = this.getNodeParameter('vcal', i) as string; + + body.vcal = vcal; + } + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + Object.assign(body, additionalFields); + + if (body.ttl) { + // transform minutes to milliseconds + body.ttl = (body.ttl as number) * 60000; + } + + responseData = await vonageApiRequest.call(this, 'POST', '/sms/json', body); + + responseData = responseData.messages; + } + } + } + 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/nodes/Vonage/vonage.png b/packages/nodes-base/nodes/Vonage/vonage.png new file mode 100644 index 0000000000000000000000000000000000000000..3da692277e6884ce480e94b1e6174d8478d3c59b GIT binary patch literal 2633 zcmY*bc|25m8=oQ4FqXnX#m63?X9)F>{9ux(sHJ-3-Q35tZy&U%N)66b-Ik z>$NqOgvre{DC#0YxwdT8JJap$zVGjRp7Z_wp7VXa&+j?spL5IMyd@X}1pxp6u(g#r zj)Q1!2=H_6r~`Hw2Y?iur5T`h_{0)tlSp#5CfnHo)Hz%Lzy~}E;N?m<7XSza@O|R| zz&W7w4~_$>e%J5-05|;rhrVlEILKXAoZwV{N8WPaKaS-*Kh)@Q-hVi^j5eR>&VeH# zRs;$FAb5-$KtN%!6vvI=hj*qr+u5SLf&<}1?_f_7oE8|utpcEFC=LxIQHd~GU_cNB zMKe+%-@rK%W_6F2B8r@UYjw!<0Mnbxfqoirwn zu-=(_+2q*a)|og&$F$AtkfITjau3h z2jkZZh_8jg`N(KWyCCESi;O(+ImxRKAf6}c-X|6^WSK6~7q!jWP*useAZkTQT|X~b zjh=lKe~eEtP`}U}ds;oi`L)9qgdH(y6$~UTC69oEiorMB9hxVEHkuM|7n;yAV{04; zjmFF1Z9hX*yRk>2HlNX9j75;H%t^~T2CX4FQ~GIhe2pnWRrM>yDg(#hmza>h7^Y&p zvWhP!LQj&P8!ZkB^1itv*Df$L)L-HoU3A%TL($|!=fwSL&G2H<;prBM^-4nv(h6~{ z76vzG4gJDrtzr3JWENP?Gh z$I4^_S7)yFqlLn@wqRJ%2(>$X`Un9=^p7Gm;OOH{K)c2C=sq&7qx0iBGOYf(mB z)W&u{{H(L1DR7@E2Aug=?eBJf zJm!gtXB2>4c3KG)0h3ItZ(w?xkN)=7aa-6IOEmBQjR)H?0}-#JhcgVI19A-_hP;tZ zwxxf0ei6qvy?eVjEVF4Q@HT@Fgm)qvK5Tl#;1eOnDyiF?84LukckwUq^{YOJ{wT{o zvxHB=N%W`I%&LukymoUi7q)I9#aA*P2)FldcfBtr;2)fa$m=*e`p!ifJ|^*Fjuw${eVW)pVOGsH|BPwKdnJzOD|cvi2x#r7xrp z8Qf5J&PPgAvDk2wyu_CXNnJF)8J+`~vU@WPS2rb6V?t}Sl8p}S1tAj$y?w0$i{uCQ z-JBgoUxf0H%c`-CeW>q%L$%MIf{%IuDpHb$?R_6k3Rj0G2xe+${gH;UUl&zYyU7BB zvjcE}@9am(MEX$+X{p{GfKu8VRzQRI?xUS7jq#}TH=+|x2zNc$N>%c@SF~WCc;(Ba zFgE5cxHO{X<>boIU-hdx>-`au;Qn{uhvVI<-q`b5k}Rj3o=424;WeLh3)V)^g>Ef& zoS0k!)tlB@6FX!SH;7vpO05~W*QAV4c-4`#HnbG`Y5_F}V58yC3q5mfp}QUHO!;aSA6I@2em^^4qA@Z?k$ZA z4PVro>x`w6)}%8>bVgGwly$z~=RWYH2t}9-=ynXH-b!((Coof(BJss-cUGPqxhw5f zm7W^8tc1$H2V=#GIP!xM+P>C?zswb~pg!4UmG^i%zxJInn)Q`UIHtnuh(Snra)!Or;WwdcoU!FSazIF(Lm}iegO&nk8&qAsM=#ij-S$9xN^h zeYu&lZX|#}7t$18{@et3ABu<8c;B^pw-h|vESyWiLZ*6E zihkWu+SK{Q^uXWfN`1-Ob_1OKtm`LPsb;3_Bi2MqmMMiw-Q+p2+lngh^O&3K?M>7< zRRPV@E3is04Jbx0CZHRpk9^(l*6>+9BlJEQSvBMgS_BM T;5eg>`#)ukId5KT=5g&mr=WXb literal 0 HcmV?d00001 diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index beddd2e23..51169cc1b 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -178,6 +178,7 @@ "dist/credentials/UnleashedSoftwareApi.credentials.js", "dist/credentials/UpleadApi.credentials.js", "dist/credentials/VeroApi.credentials.js", + "dist/credentials/VonageApi.credentials.js", "dist/credentials/WebflowApi.credentials.js", "dist/credentials/WebflowOAuth2Api.credentials.js", "dist/credentials/WooCommerceApi.credentials.js", @@ -370,6 +371,7 @@ "dist/nodes/UnleashedSoftware/UnleashedSoftware.node.js", "dist/nodes/Uplead/Uplead.node.js", "dist/nodes/Vero/Vero.node.js", + "dist/nodes/Vonage/Vonage.node.js", "dist/nodes/Webflow/WebflowTrigger.node.js", "dist/nodes/Webhook.node.js", "dist/nodes/Wordpress/Wordpress.node.js",