diff --git a/packages/nodes-base/credentials/MailjetEmailApi.credentials.ts b/packages/nodes-base/credentials/MailjetEmailApi.credentials.ts new file mode 100644 index 0000000000..a76d490c1e --- /dev/null +++ b/packages/nodes-base/credentials/MailjetEmailApi.credentials.ts @@ -0,0 +1,23 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class MailjetEmailApi implements ICredentialType { + name = 'mailjetEmailApi'; + displayName = 'Mailjet Email API'; + properties = [ + { + displayName: 'API Key', + name: 'apiKey', + type: 'string' as NodePropertyTypes, + default: '', + }, + { + displayName: 'Secret Key', + name: 'secretKey', + type: 'string' as NodePropertyTypes, + default: '', + }, + ]; +} diff --git a/packages/nodes-base/credentials/MailjetSmsApi.credentials.ts b/packages/nodes-base/credentials/MailjetSmsApi.credentials.ts new file mode 100644 index 0000000000..17d6db2709 --- /dev/null +++ b/packages/nodes-base/credentials/MailjetSmsApi.credentials.ts @@ -0,0 +1,17 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class MailjetSmsApi implements ICredentialType { + name = 'mailjetSmsApi'; + displayName = 'Mailjet SMS API'; + properties = [ + { + displayName: 'Token', + name: 'token', + type: 'string' as NodePropertyTypes, + default: '', + }, + ]; +} diff --git a/packages/nodes-base/nodes/Mailjet/EmailDescription.ts b/packages/nodes-base/nodes/Mailjet/EmailDescription.ts new file mode 100644 index 0000000000..8f37d5acef --- /dev/null +++ b/packages/nodes-base/nodes/Mailjet/EmailDescription.ts @@ -0,0 +1,386 @@ +import { INodeProperties } from 'n8n-workflow'; + +export const emailOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'email', + ], + }, + }, + options: [ + { + name: 'Send', + value: 'send', + description: 'Send a email', + }, + { + name: 'Send Template', + value: 'sendTemplate', + description: 'Send a email template', + }, + ], + default: 'send', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const emailFields = [ + +/* -------------------------------------------------------------------------- */ +/* email:send */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'From Email', + name: 'fromEmail', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'email', + ], + operation: [ + 'send', + ] + }, + }, + description: 'The title for the email', + }, + { + displayName: 'Is Body HTML?', + name: 'isBodyHtml', + type: 'boolean', + required: true, + default: true, + displayOptions: { + show: { + resource: [ + 'email', + ], + operation: [ + 'send', + ] + }, + }, + }, + { + displayName: 'Body', + name: 'body', + type: 'string', + required: true, + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + displayOptions: { + show: { + resource: [ + 'email', + ], + operation: [ + 'send', + ] + }, + }, + }, + { + displayName: 'To Email', + name: 'toEmails', + type: 'string', + required: true, + description: 'Email address of the recipient. Multiple ones can be separated by comma.', + displayOptions: { + show: { + resource: [ + 'email', + ], + operation: [ + 'send', + ] + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'email', + ], + operation: [ + 'send', + ], + }, + }, + options: [ + { + displayName: 'Bcc Addresses', + name: 'bccAddresses', + type: 'string', + description: 'Bcc Email address of the recipient. Multiple ones can be separated by comma.', + default: '', + }, + { + displayName: 'Cc Addresses', + name: 'ccAddresses', + type: 'string', + description: 'Cc Email address of the recipient. Multiple ones can be separated by comma.', + default: '', + }, + { + displayName: 'From Name', + name: 'fromName', + type: 'string', + default: '', + }, + { + displayName: 'Priority', + name: 'priority', + type: 'number', + default: 2, + }, + { + displayName: 'Subject', + name: 'subject', + type: 'string', + default: '', + }, + { + displayName: 'Track Opens', + name: 'trackOpens', + type: 'string', + description: 'Enable or disable open tracking on this message.', + default: '', + }, + { + displayName: 'Track Clicks', + name: 'trackClicks', + type: 'string', + description: 'Enable or disable open tracking on this message.', + default: '', + }, + { + displayName: 'Template Language', + name: 'templateLanguage', + type: 'boolean', + default: true, + }, + ] + }, + { + displayName: 'Variables', + name: 'variablesUi', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + displayOptions: { + show: { + resource: [ + 'email', + ], + operation: [ + 'send', + ] + }, + }, + placeholder: 'Add Variable', + default: {}, + options: [ + { + name: 'variablesValues', + displayName: 'Variable', + values: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + }, + ] + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* email:sendTemplate */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'From Email', + name: 'fromEmail', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'email', + ], + operation: [ + 'sendTemplate', + ] + }, + }, + description: 'The title for the email', + }, + { + displayName: 'Template', + name: 'templateId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'email', + ], + operation: [ + 'sendTemplate', + ] + }, + }, + }, + { + displayName: 'To Email', + name: 'toEmails', + type: 'string', + required: true, + description: 'Email address of the recipient. Multiple ones can be separated by comma.', + displayOptions: { + show: { + resource: [ + 'email', + ], + operation: [ + 'sendTemplate', + ] + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'email', + ], + operation: [ + 'sendTemplate', + ], + }, + }, + options: [ + { + displayName: 'Bcc Addresses', + name: 'bccAddresses', + type: 'string', + description: 'Bcc Recipients of the email separated by ,.', + default: '', + }, + { + displayName: 'Cc Addresses', + name: 'ccAddresses', + type: 'string', + description: 'Cc recipients of the email separated by ,.', + default: '', + }, + { + displayName: 'From Name', + name: 'fromName', + type: 'string', + default: '', + }, + { + displayName: 'Priority', + name: 'priority', + type: 'number', + default: 2, + }, + { + displayName: 'Subject', + name: 'subject', + type: 'string', + default: '', + }, + { + displayName: 'Track Opens', + name: 'trackOpens', + type: 'string', + description: 'Enable or disable open tracking on this message.', + default: '', + }, + { + displayName: 'Track Clicks', + name: 'trackClicks', + type: 'string', + description: 'Enable or disable open tracking on this message.', + default: '', + }, + { + displayName: 'Template Language', + name: 'templateLanguage', + type: 'boolean', + default: true, + }, + ] + }, + { + displayName: 'Variables', + name: 'variablesUi', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + displayOptions: { + show: { + resource: [ + 'email', + ], + operation: [ + 'sendTemplate', + ] + }, + }, + placeholder: 'Add Variable', + default: {}, + options: [ + { + name: 'variablesValues', + displayName: 'Variable', + values: [ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + }, + ] + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Mailjet/GenericFunctions.ts b/packages/nodes-base/nodes/Mailjet/GenericFunctions.ts new file mode 100644 index 0000000000..ead19c67fd --- /dev/null +++ b/packages/nodes-base/nodes/Mailjet/GenericFunctions.ts @@ -0,0 +1,63 @@ +import { OptionsWithUri } from 'request'; +import { + IExecuteFunctions, + IExecuteSingleFunctions, + ILoadOptionsFunctions, +} from 'n8n-core'; +import { IDataObject, IHookFunctions } from 'n8n-workflow'; + +export async function mailjetApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | IHookFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any + const emailApiCredentials = this.getCredentials('mailjetEmailApi'); + let options: OptionsWithUri = { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method, + qs, + body, + uri: uri ||`https://api.mailjet.com${resource}`, + json: true + }; + options = Object.assign({}, options, option); + if (Object.keys(options.body).length === 0) { + delete options.body; + } + if (emailApiCredentials !== undefined) { + const base64Credentials = Buffer.from(`${emailApiCredentials.apiKey}:${emailApiCredentials.secretKey}`).toString('base64'); + //@ts-ignore + options.headers['Authorization'] = `Basic ${base64Credentials}`; + } else { + const smsApiCredentials = this.getCredentials('mailjetSmsApi'); + //@ts-ignore + options.headers['Authorization'] = `Bearer ${smsApiCredentials.token}`; + } + try { + return await this.helpers.request!(options); + } catch (error) { + if (error.response.body || error.response.body.ErrorMessage) { + throw new Error(`Mailjet Error: response [${error.statusCode}]: ${error.response.body.ErrorMessage}`); + } + throw new Error(error); + } +} + +export async function mailjetApiRequestAllItems(this: IExecuteFunctions | IHookFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any + + const returnData: IDataObject[] = []; + + let responseData; + + query.Limit = 1000; + query.Offset = 0; + + do { + query.Offset; + responseData = await mailjetApiRequest.call(this, method, endpoint, body, query, undefined, { resolveWithFullResponse: true }); + returnData.push.apply(returnData, responseData.body); + query.Offset = query.Offset + query.Limit; + } while ( + responseData.length !== 0 + ); + return returnData; +} diff --git a/packages/nodes-base/nodes/Mailjet/Mailjet.node.ts b/packages/nodes-base/nodes/Mailjet/Mailjet.node.ts new file mode 100644 index 0000000000..741b3ce87a --- /dev/null +++ b/packages/nodes-base/nodes/Mailjet/Mailjet.node.ts @@ -0,0 +1,287 @@ +import { + IExecuteFunctions, +} from 'n8n-core'; +import { + IDataObject, + INodeTypeDescription, + INodeExecutionData, + INodeType, +} from 'n8n-workflow'; +import { + mailjetApiRequest, +} from './GenericFunctions'; +import { + emailFields, + emailOperations, +} from './EmailDescription'; +import { + smsFields, + smsOperations, +} from './SmsDescription'; + +export class Mailjet implements INodeType { + description: INodeTypeDescription = { + displayName: 'Mailjet', + name: 'mailjet', + icon: 'file:mailjet.png', + group: ['output'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Consume Mailjet API', + defaults: { + name: 'Mailjet', + color: '#ff9f48', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'mailjetEmailApi', + required: true, + displayOptions: { + show: { + resource: [ + 'email', + ], + }, + }, + }, + { + name: 'mailjetSmsApi', + required: true, + displayOptions: { + show: { + resource: [ + 'sms', + ], + }, + }, + }, + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Email', + value: 'email', + }, + { + name: 'SMS', + value: 'sms', + }, + ], + default: 'email', + description: 'Resource to consume.', + }, + ...emailOperations, + ...emailFields, + ...smsOperations, + ...smsFields, + ], + }; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const returnData: IDataObject[] = []; + const length = items.length as unknown as number; + 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 === 'email') { + //https://dev.mailjet.com/email/guides/send-api-v31/#send-a-basic-email + if (operation === 'send') { + const fromEmail = this.getNodeParameter('fromEmail', i) as string; + const isBodyHtml = this.getNodeParameter('isBodyHtml', i) as boolean; + const message = this.getNodeParameter('body', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const toEmails = (this.getNodeParameter('toEmails', i) as string).split(',') as string[]; + const variables = (this.getNodeParameter('variablesUi', i) as IDataObject).variablesValues as IDataObject[]; + + const body: IDataObject = { + Messages: [ + { + From: { + email: fromEmail, + }, + to: [], + Cc: [], + Bcc: [], + Variables: {}, + }, + ], + SandboxMode: true, + }; + for (const toEmail of toEmails) { + //@ts-ignore + body.Messages[0].to.push({ + email: toEmail, + }) + } + if (variables) { + for (const variable of variables) { + //@ts-ignore + body.Messages[0].Variables[variable.name] = variable.value; + } + } + if (isBodyHtml) { + //@ts-ignore + body.Messages[0].HTMLPart = message; + } else { + //@ts-ignore + body.Messages[0].TextPart = message; + } + if (additionalFields.bccAddresses) { + const bccAddresses = (additionalFields.bccAddresses as string).split(',') as string[]; + for (const bccAddress of bccAddresses) { + //@ts-ignore + body.Messages[0].Bcc.push({ + email: bccAddress, + }); + } + } + if (additionalFields.ccAddresses) { + const ccAddresses = (additionalFields.ccAddresses as string).split(',') as string[]; + for (const ccAddress of ccAddresses) { + //@ts-ignore + body.Messages[0].Cc.push({ + email: ccAddress, + }); + } + } + if (additionalFields.subject) { + //@ts-ignore + body.Messages[0].subject = additionalFields.subject as string; + } + if (additionalFields.trackOpens) { + //@ts-ignore + body.Messages[0].TrackOpens = additionalFields.trackOpens as string; + } + if (additionalFields.trackClicks) { + //@ts-ignore + body.Messages[0].TrackClicks = additionalFields.trackClicks as string; + } + if (additionalFields.fromName) { + //@ts-ignore + body.Messages[0].From.name = additionalFields.fromName as string; + } + if (additionalFields.templateLanguage) { + //@ts-ignore + body.Messages[0].From.TemplateLanguage = additionalFields.templateLanguage as boolean; + } + if (additionalFields.priority) { + //@ts-ignore + body.Messages[0].Priority = additionalFields.priority as number; + } + responseData = await mailjetApiRequest.call(this, 'POST', '/v3.1/send', body); + responseData = responseData.Messages; + + } + //https://dev.mailjet.com/email/guides/send-api-v31/#use-a-template + if (operation === 'sendTemplate') { + const fromEmail = this.getNodeParameter('fromEmail', i) as string; + const templateId = this.getNodeParameter('templateId', i) as string; + const variables = (this.getNodeParameter('variablesUi', i) as IDataObject).variablesValues as IDataObject[]; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const toEmails = (this.getNodeParameter('toEmails', i) as string).split(',') as string[]; + + const body: IDataObject = { + Messages: [ + { + From: { + email: fromEmail, + }, + to: [], + Cc: [], + Bcc: [], + Variables: {}, + TemplateID: templateId, + }, + ], + SandboxMode: true, + }; + for (const toEmail of toEmails) { + //@ts-ignore + body.Messages[0].to.push({ + email: toEmail, + }); + } + if (variables) { + for (const variable of variables) { + //@ts-ignore + body.Messages[0].Variables[variable.name] = variable.value; + } + } + if (additionalFields.bccAddresses) { + const bccAddresses = (additionalFields.bccAddresses as string).split(',') as string[]; + for (const bccAddress of bccAddresses) { + //@ts-ignore + body.Messages[0].Bcc.push({ + email: bccAddress, + }); + } + } + if (additionalFields.ccAddresses) { + const ccAddresses = (additionalFields.ccAddresses as string).split(',') as string[]; + for (const ccAddress of ccAddresses) { + //@ts-ignore + body.Messages[0].Cc.push({ + email: ccAddress, + }); + } + } + if (additionalFields.subject) { + //@ts-ignore + body.Messages[0].Subject = additionalFields.subject as string; + } + if (additionalFields.trackOpens) { + //@ts-ignore + body.Messages[0].TrackOpens = additionalFields.trackOpens as string; + } + if (additionalFields.trackClicks) { + //@ts-ignore + body.Messages[0].TrackClicks = additionalFields.trackClicks as string; + } + if (additionalFields.fromName) { + //@ts-ignore + body.Messages[0].From.name = additionalFields.fromName as string; + } + if (additionalFields.templateLanguage) { + //@ts-ignore + body.Messages[0].From.TemplateLanguage = additionalFields.templateLanguage as boolean; + } + if (additionalFields.priority) { + //@ts-ignore + body.Messages[0].Priority = additionalFields.priority as number; + } + responseData = await mailjetApiRequest.call(this, 'POST', '/v3.1/send', body); + responseData = responseData.Messages; + } + } + if (resource === 'sms') { + //https://dev.mailjet.com/sms/reference/send-message#v4_post_sms-send + if (operation === 'send') { + const from = this.getNodeParameter('from', i) as string; + const to = this.getNodeParameter('to', i) as boolean; + const text = this.getNodeParameter('text', i) as string; + const body: IDataObject = { + From: from, + To: to, + Text: text, + }; + responseData = await mailjetApiRequest.call(this, 'POST', '/v4/sms-send', body); + } + } + if (Array.isArray(responseData)) { + returnData.push.apply(returnData, responseData as IDataObject[]); + } else { + returnData.push(responseData as IDataObject); + } + } + return [this.helpers.returnJsonArray(returnData)]; + } +} diff --git a/packages/nodes-base/nodes/Mailjet/MailjetTrigger.node.ts b/packages/nodes-base/nodes/Mailjet/MailjetTrigger.node.ts new file mode 100644 index 0000000000..fa009f5194 --- /dev/null +++ b/packages/nodes-base/nodes/Mailjet/MailjetTrigger.node.ts @@ -0,0 +1,138 @@ +import { + IHookFunctions, + IWebhookFunctions, +} from 'n8n-core'; + +import { + IDataObject, + INodeTypeDescription, + INodeType, + IWebhookResponseData, +} from 'n8n-workflow'; + +import { + mailjetApiRequest, +} from './GenericFunctions'; + +export class MailjetTrigger implements INodeType { + description: INodeTypeDescription = { + displayName: 'Mailjet Trigger', + name: 'mailjetTrigger', + icon: 'file:mailjet.png', + group: ['trigger'], + version: 1, + description: 'Handle Mailjet events via webhooks', + defaults: { + name: 'Mailjet Trigger', + color: '#ff9f48', + }, + inputs: [], + outputs: ['main'], + credentials: [ + { + name: 'mailjetEmailApi', + required: true, + }, + ], + webhooks: [ + { + name: 'default', + httpMethod: 'POST', + responseMode: 'onReceived', + path: 'webhook', + }, + ], + properties: [ + { + displayName: 'Event', + name: 'event', + type: 'options', + required: true, + default: 'open', + options: [ + { + name: 'email.bounce', + value: 'bounce', + }, + { + name: 'email.blocked', + value: 'blocked', + }, + { + name: 'email.open', + value: 'open', + }, + { + name: 'email.spam', + value: 'spam', + }, + { + name: 'email.sent', + value: 'sent', + }, + { + name: 'email.unsub', + value: 'unsub', + }, + ], + description: 'Determines which resource events the webhook is triggered for.', + }, + ], + + }; + + // @ts-ignore + webhookMethods = { + default: { + async checkExists(this: IHookFunctions): Promise { + const webhookData = this.getWorkflowStaticData('node'); + if (webhookData.webhookId === undefined) { + return false; + } + const endpoint = `/v3/rest/eventcallbackurl/${webhookData.webhookId}`; + try { + await mailjetApiRequest.call(this, 'GET', endpoint); + } catch (e) { + return false; + } + return true; + }, + async create(this: IHookFunctions): Promise { + const webhookUrl = this.getNodeWebhookUrl('default'); + const webhookData = this.getWorkflowStaticData('node'); + const event = this.getNodeParameter('event') as string; + const endpoint = '/v3/rest/eventcallbackurl'; + const body: IDataObject = { + Url: webhookUrl, + EventType: event, + Status: 'alive', + isBackup: 'false', + }; + const { Data } = await mailjetApiRequest.call(this, 'POST', endpoint, body); + webhookData.webhookId = Data[0].ID; + return true; + }, + async delete(this: IHookFunctions): Promise { + const webhookData = this.getWorkflowStaticData('node'); + const endpoint = `/v3/rest/eventcallbackurl/${webhookData.webhookId}`; + try { + await mailjetApiRequest.call(this, 'DELETE', endpoint); + } catch(error) { + return false; + } + delete webhookData.webhookId; + return true; + }, + }, + }; + + //@ts-ignore + async webhook(this: IWebhookFunctions): Promise { + const req = this.getRequestObject(); + return { + workflowData: [ + this.helpers.returnJsonArray(req.body), + ], + }; + } +} diff --git a/packages/nodes-base/nodes/Mailjet/SmsDescription.ts b/packages/nodes-base/nodes/Mailjet/SmsDescription.ts new file mode 100644 index 0000000000..ef6c05bb80 --- /dev/null +++ b/packages/nodes-base/nodes/Mailjet/SmsDescription.ts @@ -0,0 +1,88 @@ +import { INodeProperties } from 'n8n-workflow'; + +export const smsOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'sms', + ], + }, + }, + options: [ + { + name: 'Send', + value: 'send', + description: 'Send a sms', + }, + ], + default: 'send', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const smsFields = [ + +/* -------------------------------------------------------------------------- */ +/* sms:send */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'From', + name: 'from', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'sms', + ], + operation: [ + 'send', + ] + }, + }, + description: 'Customizable sender name. Should be between 3 and 11 characters in length, only alphanumeric characters are allowed.', + }, + { + displayName: 'To', + name: 'to', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'sms', + ], + operation: [ + 'send', + ] + }, + }, + description: 'Message recipient. Should be between 3 and 15 characters in length. The number always starts with a plus sign followed by a country code, followed by the number. Phone numbers are expected to comply with the E.164 format.', + }, + { + displayName: 'Text', + name: 'text', + type: 'string', + required: true, + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + displayOptions: { + show: { + resource: [ + 'sms', + ], + operation: [ + 'send', + ] + }, + }, + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Mailjet/mailjet.png b/packages/nodes-base/nodes/Mailjet/mailjet.png new file mode 100644 index 0000000000..c5c04611c4 Binary files /dev/null and b/packages/nodes-base/nodes/Mailjet/mailjet.png differ diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 3b9d05fd73..1ccea2b65b 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -69,6 +69,8 @@ "dist/credentials/MongoDb.credentials.js", "dist/credentials/Msg91Api.credentials.js", "dist/credentials/MySql.credentials.js", + "dist/credentials/MailjetEmailApi.credentials.js", + "dist/credentials/MailjetSmsApi.credentials.js", "dist/credentials/NextCloudApi.credentials.js", "dist/credentials/OpenWeatherMapApi.credentials.js", "dist/credentials/PayPalApi.credentials.js", @@ -167,6 +169,8 @@ "dist/nodes/MoveBinaryData.node.js", "dist/nodes/Msg91/Msg91.node.js", "dist/nodes/MySql/MySql.node.js", + "dist/nodes/Mailjet/Mailjet.node.js", + "dist/nodes/Mailjet/MailjetTrigger.node.js", "dist/nodes/NextCloud/NextCloud.node.js", "dist/nodes/NoOp.node.js", "dist/nodes/OpenWeatherMap.node.js",