mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
feat(Mailjet Node): Add credential tests and support for sandbox, JSON parameters & variables (#2987)
* Add Variables JSON to Mailjet Batch send * ⚡ Improvements * ⚡ Add credential verification * ⚡ Small improvement Co-authored-by: Marcin Koziej <marcin@cahoots.pl>
This commit is contained in:
parent
26a7c61175
commit
d2756de090
|
@ -20,5 +20,12 @@ export class MailjetEmailApi implements ICredentialType {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Sandbox Mode',
|
||||||
|
name: 'sandboxMode',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Allow to run the API call in a Sandbox mode, where all validations of the payload will be done without delivering the message',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import { INodeProperties } from 'n8n-workflow';
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
export const emailOperations: INodeProperties[] = [
|
export const emailOperations: INodeProperties[] = [
|
||||||
{
|
{
|
||||||
|
@ -25,7 +27,6 @@ export const emailOperations: INodeProperties[] = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
default: 'send',
|
default: 'send',
|
||||||
description: 'The operation to perform.',
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -120,6 +121,22 @@ export const emailFields: INodeProperties[] = [
|
||||||
default: '',
|
default: '',
|
||||||
description: 'HTML text message of email.',
|
description: 'HTML text message of email.',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'JSON Parameters',
|
||||||
|
name: 'jsonParameters',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'email',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'send',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Additional Fields',
|
displayName: 'Additional Fields',
|
||||||
name: 'additionalFields',
|
name: 'additionalFields',
|
||||||
|
@ -226,6 +243,29 @@ export const emailFields: INodeProperties[] = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Variables (JSON)',
|
||||||
|
name: 'variablesJson',
|
||||||
|
type: 'string',
|
||||||
|
typeOptions: {
|
||||||
|
alwaysOpenEditWindow: true,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'email',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'send',
|
||||||
|
],
|
||||||
|
jsonParameters: [
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'HTML text message of email.',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Variables',
|
displayName: 'Variables',
|
||||||
name: 'variablesUi',
|
name: 'variablesUi',
|
||||||
|
@ -241,6 +281,9 @@ export const emailFields: INodeProperties[] = [
|
||||||
operation: [
|
operation: [
|
||||||
'send',
|
'send',
|
||||||
],
|
],
|
||||||
|
jsonParameters: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
placeholder: 'Add Variable',
|
placeholder: 'Add Variable',
|
||||||
|
@ -327,6 +370,22 @@ export const emailFields: INodeProperties[] = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'JSON Parameters',
|
||||||
|
name: 'jsonParameters',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'email',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'sendTemplate',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Additional Fields',
|
displayName: 'Additional Fields',
|
||||||
name: 'additionalFields',
|
name: 'additionalFields',
|
||||||
|
@ -420,6 +479,9 @@ export const emailFields: INodeProperties[] = [
|
||||||
operation: [
|
operation: [
|
||||||
'sendTemplate',
|
'sendTemplate',
|
||||||
],
|
],
|
||||||
|
jsonParameters: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
placeholder: 'Add Variable',
|
placeholder: 'Add Variable',
|
||||||
|
@ -445,4 +507,27 @@ export const emailFields: INodeProperties[] = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Variables (JSON)',
|
||||||
|
name: 'variablesJson',
|
||||||
|
type: 'string',
|
||||||
|
typeOptions: {
|
||||||
|
alwaysOpenEditWindow: true,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'email',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'sendTemplate',
|
||||||
|
],
|
||||||
|
jsonParameters: [
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'HTML text message of email.',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
|
@ -9,6 +9,8 @@ import {
|
||||||
} from 'n8n-core';
|
} from 'n8n-core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
ICredentialDataDecryptedObject,
|
||||||
|
ICredentialTestFunctions,
|
||||||
IDataObject,
|
IDataObject,
|
||||||
IHookFunctions,
|
IHookFunctions,
|
||||||
JsonObject,
|
JsonObject,
|
||||||
|
@ -16,7 +18,7 @@ import {
|
||||||
} from 'n8n-workflow';
|
} 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<any> { // tslint:disable-line:no-any
|
export async function mailjetApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | IHookFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||||
const emailApiCredentials = await this.getCredentials('mailjetEmailApi');
|
const emailApiCredentials = await this.getCredentials('mailjetEmailApi') as { apiKey: string, secretKey: string, sandboxMode: boolean };
|
||||||
let options: OptionsWithUri = {
|
let options: OptionsWithUri = {
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
|
@ -33,8 +35,12 @@ export async function mailjetApiRequest(this: IExecuteFunctions | IExecuteSingle
|
||||||
delete options.body;
|
delete options.body;
|
||||||
}
|
}
|
||||||
if (emailApiCredentials !== undefined) {
|
if (emailApiCredentials !== undefined) {
|
||||||
const base64Credentials = Buffer.from(`${emailApiCredentials.apiKey}:${emailApiCredentials.secretKey}`).toString('base64');
|
const { sandboxMode } = emailApiCredentials;
|
||||||
options.headers!['Authorization'] = `Basic ${base64Credentials}`;
|
Object.assign(body, { SandboxMode: sandboxMode });
|
||||||
|
options.auth = {
|
||||||
|
username: emailApiCredentials.apiKey,
|
||||||
|
password: emailApiCredentials.secretKey,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
const smsApiCredentials = await this.getCredentials('mailjetSmsApi');
|
const smsApiCredentials = await this.getCredentials('mailjetSmsApi');
|
||||||
options.headers!['Authorization'] = `Bearer ${smsApiCredentials!.token}`;
|
options.headers!['Authorization'] = `Bearer ${smsApiCredentials!.token}`;
|
||||||
|
@ -65,6 +71,47 @@ export async function mailjetApiRequestAllItems(this: IExecuteFunctions | IHookF
|
||||||
return returnData;
|
return returnData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function validateCredentials(
|
||||||
|
this: ICredentialTestFunctions,
|
||||||
|
decryptedCredentials: ICredentialDataDecryptedObject,
|
||||||
|
): Promise<any> { // tslint:disable-line:no-any
|
||||||
|
const credentials = decryptedCredentials;
|
||||||
|
|
||||||
|
const {
|
||||||
|
apiKey,
|
||||||
|
secretKey,
|
||||||
|
} = credentials as {
|
||||||
|
apiKey: string,
|
||||||
|
secretKey: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
const options: OptionsWithUri = {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
auth: {
|
||||||
|
username: apiKey,
|
||||||
|
password: secretKey,
|
||||||
|
},
|
||||||
|
method: 'GET',
|
||||||
|
uri: `https://api.mailjet.com/v3/REST/template`,
|
||||||
|
json: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
return await this.helpers.request(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function validateJSON(json: string | undefined): IDataObject | undefined { // tslint:disable-line:no-any
|
||||||
|
let result;
|
||||||
|
try {
|
||||||
|
result = JSON.parse(json!);
|
||||||
|
} catch (exception) {
|
||||||
|
result = undefined;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IMessage {
|
export interface IMessage {
|
||||||
From?: { Email?: string, Name?: string };
|
From?: { Email?: string, Name?: string };
|
||||||
Subject?: string;
|
Subject?: string;
|
||||||
|
|
|
@ -3,17 +3,25 @@ import {
|
||||||
} from 'n8n-core';
|
} from 'n8n-core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
ICredentialDataDecryptedObject,
|
||||||
|
ICredentialsDecrypted,
|
||||||
|
ICredentialTestFunctions,
|
||||||
IDataObject,
|
IDataObject,
|
||||||
ILoadOptionsFunctions,
|
ILoadOptionsFunctions,
|
||||||
|
INodeCredentialTestResult,
|
||||||
INodeExecutionData,
|
INodeExecutionData,
|
||||||
INodePropertyOptions,
|
INodePropertyOptions,
|
||||||
INodeType,
|
INodeType,
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
|
JsonObject,
|
||||||
|
NodeOperationError,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
IMessage,
|
IMessage,
|
||||||
mailjetApiRequest,
|
mailjetApiRequest,
|
||||||
|
validateCredentials,
|
||||||
|
validateJSON,
|
||||||
} from './GenericFunctions';
|
} from './GenericFunctions';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -25,7 +33,6 @@ import {
|
||||||
smsFields,
|
smsFields,
|
||||||
smsOperations,
|
smsOperations,
|
||||||
} from './SmsDescription';
|
} from './SmsDescription';
|
||||||
|
|
||||||
export class Mailjet implements INodeType {
|
export class Mailjet implements INodeType {
|
||||||
description: INodeTypeDescription = {
|
description: INodeTypeDescription = {
|
||||||
displayName: 'Mailjet',
|
displayName: 'Mailjet',
|
||||||
|
@ -44,6 +51,7 @@ export class Mailjet implements INodeType {
|
||||||
{
|
{
|
||||||
name: 'mailjetEmailApi',
|
name: 'mailjetEmailApi',
|
||||||
required: true,
|
required: true,
|
||||||
|
testedBy: 'mailjetEmailApiTest',
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
resource: [
|
resource: [
|
||||||
|
@ -90,6 +98,25 @@ export class Mailjet implements INodeType {
|
||||||
};
|
};
|
||||||
|
|
||||||
methods = {
|
methods = {
|
||||||
|
credentialTest: {
|
||||||
|
async mailjetEmailApiTest(this: ICredentialTestFunctions, credential: ICredentialsDecrypted): Promise<INodeCredentialTestResult> {
|
||||||
|
try {
|
||||||
|
await validateCredentials.call(this, credential.data as ICredentialDataDecryptedObject);
|
||||||
|
} catch (error) {
|
||||||
|
const err = error as JsonObject;
|
||||||
|
if (err.statusCode === 401) {
|
||||||
|
return {
|
||||||
|
status: 'Error',
|
||||||
|
message: `Invalid credentials`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: 'OK',
|
||||||
|
message: 'Authentication successful',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
loadOptions: {
|
loadOptions: {
|
||||||
// Get all the available custom fields to display them to user so that he can
|
// Get all the available custom fields to display them to user so that he can
|
||||||
// select them easily
|
// select them easily
|
||||||
|
@ -126,7 +153,7 @@ export class Mailjet implements INodeType {
|
||||||
const subject = this.getNodeParameter('subject', i) as string;
|
const subject = this.getNodeParameter('subject', i) as string;
|
||||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
const toEmail = (this.getNodeParameter('toEmail', i) as string).split(',') as string[];
|
const toEmail = (this.getNodeParameter('toEmail', i) as string).split(',') as string[];
|
||||||
const variables = (this.getNodeParameter('variablesUi', i) as IDataObject).variablesValues as IDataObject[];
|
const jsonParameters = this.getNodeParameter('jsonParameters', i) as boolean;
|
||||||
|
|
||||||
const body: IMessage = {
|
const body: IMessage = {
|
||||||
From: {
|
From: {
|
||||||
|
@ -144,11 +171,21 @@ export class Mailjet implements INodeType {
|
||||||
Email: email,
|
Email: email,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (variables) {
|
|
||||||
|
if (jsonParameters) {
|
||||||
|
const variablesJson = this.getNodeParameter('variablesJson', i) as string;
|
||||||
|
const parsedJson = validateJSON(variablesJson);
|
||||||
|
if (parsedJson === undefined) {
|
||||||
|
throw new NodeOperationError(this.getNode(),`Parameter 'Variables (JSON)' has a invalid JSON`);
|
||||||
|
}
|
||||||
|
body.Variables = parsedJson;
|
||||||
|
} else {
|
||||||
|
const variables = (this.getNodeParameter('variablesUi', i) as IDataObject).variablesValues as IDataObject[] || [];
|
||||||
for (const variable of variables) {
|
for (const variable of variables) {
|
||||||
body.Variables![variable.name as string] = variable.value;
|
body.Variables![variable.name as string] = variable.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (htmlBody) {
|
if (htmlBody) {
|
||||||
body.HTMLPart = htmlBody;
|
body.HTMLPart = htmlBody;
|
||||||
}
|
}
|
||||||
|
@ -201,9 +238,9 @@ export class Mailjet implements INodeType {
|
||||||
const fromEmail = this.getNodeParameter('fromEmail', i) as string;
|
const fromEmail = this.getNodeParameter('fromEmail', i) as string;
|
||||||
const templateId = parseInt(this.getNodeParameter('templateId', i) as string, 10);
|
const templateId = parseInt(this.getNodeParameter('templateId', i) as string, 10);
|
||||||
const subject = this.getNodeParameter('subject', i) as string;
|
const subject = this.getNodeParameter('subject', i) as string;
|
||||||
const variables = (this.getNodeParameter('variablesUi', i) as IDataObject).variablesValues as IDataObject[];
|
|
||||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
const toEmail = (this.getNodeParameter('toEmail', i) as string).split(',') as string[];
|
const toEmail = (this.getNodeParameter('toEmail', i) as string).split(',') as string[];
|
||||||
|
const jsonParameters = this.getNodeParameter('jsonParameters', i) as boolean;
|
||||||
|
|
||||||
const body: IMessage = {
|
const body: IMessage = {
|
||||||
From: {
|
From: {
|
||||||
|
@ -222,11 +259,21 @@ export class Mailjet implements INodeType {
|
||||||
Email: email,
|
Email: email,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (variables) {
|
|
||||||
|
if (jsonParameters) {
|
||||||
|
const variablesJson = this.getNodeParameter('variablesJson', i) as string;
|
||||||
|
const parsedJson = validateJSON(variablesJson);
|
||||||
|
if (parsedJson === undefined) {
|
||||||
|
throw new NodeOperationError(this.getNode(), `Parameter 'Variables (JSON)' has a invalid JSON`);
|
||||||
|
}
|
||||||
|
body.Variables = parsedJson;
|
||||||
|
} else {
|
||||||
|
const variables = (this.getNodeParameter('variablesUi', i) as IDataObject).variablesValues as IDataObject[] || [];
|
||||||
for (const variable of variables) {
|
for (const variable of variables) {
|
||||||
body.Variables![variable.name as string] = variable.value;
|
body.Variables![variable.name as string] = variable.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (additionalFields.bccEmail) {
|
if (additionalFields.bccEmail) {
|
||||||
const bccEmail = (additionalFields.bccEmail as string).split(',') as string[];
|
const bccEmail = (additionalFields.bccEmail as string).split(',') as string[];
|
||||||
for (const email of bccEmail) {
|
for (const email of bccEmail) {
|
||||||
|
@ -289,7 +336,7 @@ export class Mailjet implements INodeType {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (this.continueOnFail()) {
|
if (this.continueOnFail()) {
|
||||||
returnData.push({ error: error.message });
|
returnData.push({ error: (error as JsonObject).message });
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
|
|
Loading…
Reference in a new issue