From e4eefedcbfa5d764e2bb0edb661de1ef84b66b98 Mon Sep 17 00:00:00 2001 From: Priyanka P Date: Fri, 13 Dec 2019 16:59:33 +0530 Subject: [PATCH 1/2] Msg91 Integration(node) --- .../credentials/Msg91Api.credentials.ts | 19 ++ .../nodes/Msg91/GenericFunctions.ts | 64 ++++++ packages/nodes-base/nodes/Msg91/Msg91.node.ts | 186 ++++++++++++++++++ packages/nodes-base/nodes/Msg91/msg91.png | Bin 0 -> 2982 bytes packages/nodes-base/package.json | 2 + 5 files changed, 271 insertions(+) create mode 100644 packages/nodes-base/credentials/Msg91Api.credentials.ts create mode 100644 packages/nodes-base/nodes/Msg91/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/Msg91/Msg91.node.ts create mode 100644 packages/nodes-base/nodes/Msg91/msg91.png diff --git a/packages/nodes-base/credentials/Msg91Api.credentials.ts b/packages/nodes-base/credentials/Msg91Api.credentials.ts new file mode 100644 index 0000000000..d4f96a1ca9 --- /dev/null +++ b/packages/nodes-base/credentials/Msg91Api.credentials.ts @@ -0,0 +1,19 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + + +export class Msg91Api implements ICredentialType { + name = 'msg91Api'; + displayName = 'Msg91 Api'; + properties = [ + // User authentication key + { + displayName: 'Authentication Key', + name: 'authkey', + type: 'string' as NodePropertyTypes, + default: '', + }, + ]; +} diff --git a/packages/nodes-base/nodes/Msg91/GenericFunctions.ts b/packages/nodes-base/nodes/Msg91/GenericFunctions.ts new file mode 100644 index 0000000000..59a5831bf4 --- /dev/null +++ b/packages/nodes-base/nodes/Msg91/GenericFunctions.ts @@ -0,0 +1,64 @@ +import { + IExecuteFunctions, + IHookFunctions, +} from 'n8n-core'; + +import { + IDataObject, +} from 'n8n-workflow'; + +/** + * Make an API request to MSG91 + * + * @param {IHookFunctions} this + * @param {string} method + * @param {string} url + * @param {object} body + * @returns {Promise} + */ +export async function msg91ApiRequest(this: IHookFunctions | IExecuteFunctions, method: string, endpoint: string, body: IDataObject, query?: IDataObject): Promise { // tslint:disable-line:no-any + const credentials = this.getCredentials('msg91Api'); + if (credentials === undefined) { + throw new Error('No credentials got returned!'); + } + + if (query === undefined) { + query = {}; + } + + query.authkey = credentials.authkey as string; + + const options = { + method, + form: body, + qs: query, + uri: `https://api.msg91.com/api/sendhttp.php`, + auth: { + user: '', + pass: '', + }, + json: true + }; + + try { + return await this.helpers.request(options); + } catch (error) { + if (error.statusCode === 401) { + // Return a clear error + throw new Error('The MSG91 credentials are not valid!'); + } + + if (error.response && error.response.body && error.response.body.message) { + // Try to return the error prettier + let errorMessage = `MSG91 error response [${error.statusCode}]: ${error.response.body.message}`; + if (error.response.body.more_info) { + errorMessage = `errorMessage (${error.response.body.more_info})`; + } + + throw new Error(errorMessage); + } + + // If that data does not exist for some reason return the actual error + throw error; + } +} \ No newline at end of file diff --git a/packages/nodes-base/nodes/Msg91/Msg91.node.ts b/packages/nodes-base/nodes/Msg91/Msg91.node.ts new file mode 100644 index 0000000000..f59dd49ece --- /dev/null +++ b/packages/nodes-base/nodes/Msg91/Msg91.node.ts @@ -0,0 +1,186 @@ +import { IExecuteFunctions } from 'n8n-core'; +import { + IDataObject, + INodeExecutionData, + INodeType, + INodeTypeDescription, +} from 'n8n-workflow'; + +import { + msg91ApiRequest, +} from './GenericFunctions'; + + +export class Msg91 implements INodeType { + description: INodeTypeDescription = { + displayName: 'Msg91', + name: 'msg91', + icon: 'file:msg91.png', + group: ['transform'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Send Transactional SMS', + defaults: { + name: 'Msg91', + color: '#0000ff', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'msg91Api', + 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', + displayOptions: { + show: { + resource: [ + 'sms', + ], + }, + }, + options: [ + { + name: 'Send', + value: 'send', + description: 'Send SMS', + }, + ], + default: 'send', + description: 'The operation to perform.', + }, + { + displayName: 'Sender', + name: 'sender', + type: 'string', + default: '', + placeholder: '+14155238886', + required: true, + displayOptions: { + show: { + operation: [ + 'send', + ], + resource: [ + 'sms', + ], + }, + }, + description: 'The number from which to send the message', + }, + { + displayName: 'To', + name: 'mobiles', + type: 'string', + default: '', + placeholder: 'Mobile Number With Country Code', + required: true, + displayOptions: { + show: { + operation: [ + 'send', + ], + resource: [ + 'sms', + ], + }, + }, + description: 'The number to which to send the message', + }, + { + displayName: 'Message', + name: 'message', + type: 'string', + default: '', + required: true, + displayOptions: { + show: { + operation: [ + 'send', + ], + resource: [ + 'sms', + ], + }, + }, + description: 'The message to send', + }, + ] + }; + + + async execute(this: IExecuteFunctions): Promise { + + const items = this.getInputData(); + const returnData: IDataObject[] = []; + + let operation: string; + let resource: string; + + // For Post + let body: IDataObject; + // For Query string + let qs: IDataObject; + + let requestMethod: string; + let endpoint: string; + + for (let i = 0; i < items.length; i++) { + requestMethod = 'GET'; + endpoint = ''; + body = {}; + qs = {}; + + resource = this.getNodeParameter('resource', i) as string; + operation = this.getNodeParameter('operation', i) as string; + + if (resource === 'sms') { + if (operation === 'send') { + // ---------------------------------- + // sms:send + // ---------------------------------- + + requestMethod = 'GET'; + endpoint = 'https://api.msg91.com/api/sendhttp.php'; + + qs.route = 4; + qs.country = 0; + qs.sender = this.getNodeParameter('sender', i) as string; + qs.mobiles = this.getNodeParameter('mobiles', i) as string; + qs.message = this.getNodeParameter('message', i) as string; + + } else { + throw new Error(`The operation "${operation}" is not known!`); + } + } else { + throw new Error(`The resource "${resource}" is not known!`); + } + + const responseData = await msg91ApiRequest.call(this, requestMethod, endpoint, body, qs); + + returnData.push(responseData as IDataObject); + } + + return [this.helpers.returnJsonArray(returnData)]; + + } +} diff --git a/packages/nodes-base/nodes/Msg91/msg91.png b/packages/nodes-base/nodes/Msg91/msg91.png new file mode 100644 index 0000000000000000000000000000000000000000..dc10c1f269c2c7d9c03c0976959d4ccc25b9ac67 GIT binary patch literal 2982 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 D8CIAm literal 0 HcmV?d00001 diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index dad780ab23..6d44a32faf 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -68,6 +68,7 @@ "dist/credentials/TodoistApi.credentials.js", "dist/credentials/TrelloApi.credentials.js", "dist/credentials/TwilioApi.credentials.js", + "dist/credentials/Msg91Api.credentials.js", "dist/credentials/TypeformApi.credentials.js", "dist/credentials/MandrillApi.credentials.js", "dist/credentials/TodoistApi.credentials.js", @@ -149,6 +150,7 @@ "dist/nodes/Trello/Trello.node.js", "dist/nodes/Trello/TrelloTrigger.node.js", "dist/nodes/Twilio/Twilio.node.js", + "dist/nodes/Msg91/Msg91.node.js", "dist/nodes/Typeform/TypeformTrigger.node.js", "dist/nodes/WriteBinaryFile.node.js", "dist/nodes/Webhook.node.js", From d169a5617fcef2b5ea5834c04d0193578201b8bf Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 20 Dec 2019 16:35:00 -0600 Subject: [PATCH 2/2] :zap: Small improvements to Msg91-Node --- .../nodes/Msg91/GenericFunctions.ts | 14 ++--- packages/nodes-base/nodes/Msg91/Msg91.node.ts | 54 +++++++++---------- 2 files changed, 31 insertions(+), 37 deletions(-) diff --git a/packages/nodes-base/nodes/Msg91/GenericFunctions.ts b/packages/nodes-base/nodes/Msg91/GenericFunctions.ts index 59a5831bf4..893996fc8a 100644 --- a/packages/nodes-base/nodes/Msg91/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Msg91/GenericFunctions.ts @@ -24,19 +24,15 @@ export async function msg91ApiRequest(this: IHookFunctions | IExecuteFunctions, if (query === undefined) { query = {}; - } - - query.authkey = credentials.authkey as string; + } + + query.authkey = credentials.authkey as string; const options = { method, form: body, qs: query, - uri: `https://api.msg91.com/api/sendhttp.php`, - auth: { - user: '', - pass: '', - }, + uri: `https://api.msg91.com/api${endpoint}`, json: true }; @@ -61,4 +57,4 @@ export async function msg91ApiRequest(this: IHookFunctions | IExecuteFunctions, // If that data does not exist for some reason return the actual error throw error; } -} \ No newline at end of file +} diff --git a/packages/nodes-base/nodes/Msg91/Msg91.node.ts b/packages/nodes-base/nodes/Msg91/Msg91.node.ts index f59dd49ece..240a3623e0 100644 --- a/packages/nodes-base/nodes/Msg91/Msg91.node.ts +++ b/packages/nodes-base/nodes/Msg91/Msg91.node.ts @@ -46,7 +46,6 @@ export class Msg91 implements INodeType { default: 'sms', description: 'The resource to operate on.', }, - { displayName: 'Operation', name: 'operation', @@ -69,8 +68,27 @@ export class Msg91 implements INodeType { description: 'The operation to perform.', }, { - displayName: 'Sender', - name: 'sender', + displayName: 'From', + name: 'from', + type: 'string', + default: '', + placeholder: '4155238886', + required: true, + displayOptions: { + show: { + operation: [ + 'send', + ], + resource: [ + 'sms', + ], + }, + }, + description: 'The number from which to send the message.', + }, + { + displayName: 'To', + name: 'to', type: 'string', default: '', placeholder: '+14155238886', @@ -85,26 +103,7 @@ export class Msg91 implements INodeType { ], }, }, - description: 'The number from which to send the message', - }, - { - displayName: 'To', - name: 'mobiles', - type: 'string', - default: '', - placeholder: 'Mobile Number With Country Code', - required: true, - displayOptions: { - show: { - operation: [ - 'send', - ], - resource: [ - 'sms', - ], - }, - }, - description: 'The number to which to send the message', + description: 'The number, with coutry code, to which to send the message.', }, { displayName: 'Message', @@ -145,7 +144,6 @@ export class Msg91 implements INodeType { let endpoint: string; for (let i = 0; i < items.length; i++) { - requestMethod = 'GET'; endpoint = ''; body = {}; qs = {}; @@ -160,12 +158,12 @@ export class Msg91 implements INodeType { // ---------------------------------- requestMethod = 'GET'; - endpoint = 'https://api.msg91.com/api/sendhttp.php'; + endpoint = '/sendhttp.php'; qs.route = 4; qs.country = 0; - qs.sender = this.getNodeParameter('sender', i) as string; - qs.mobiles = this.getNodeParameter('mobiles', i) as string; + qs.sender = this.getNodeParameter('from', i) as string; + qs.mobiles = this.getNodeParameter('to', i) as string; qs.message = this.getNodeParameter('message', i) as string; } else { @@ -177,7 +175,7 @@ export class Msg91 implements INodeType { const responseData = await msg91ApiRequest.call(this, requestMethod, endpoint, body, qs); - returnData.push(responseData as IDataObject); + returnData.push({ requestId: responseData }); } return [this.helpers.returnJsonArray(returnData)];