mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-31 15:37:26 -08:00
feat(MailerLite Node): Update node to support new api (#11933)
This commit is contained in:
parent
271401d882
commit
d6b8e65abe
|
@ -1,4 +1,10 @@
|
|||
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
|
||||
import type {
|
||||
ICredentialDataDecryptedObject,
|
||||
ICredentialTestRequest,
|
||||
ICredentialType,
|
||||
IHttpRequestOptions,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export class MailerLiteApi implements ICredentialType {
|
||||
name = 'mailerLiteApi';
|
||||
|
@ -15,5 +21,37 @@ export class MailerLiteApi implements ICredentialType {
|
|||
typeOptions: { password: true },
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Classic API',
|
||||
name: 'classicApi',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description:
|
||||
'If the Classic API should be used, If this is your first time using this node this should be false.',
|
||||
},
|
||||
];
|
||||
|
||||
async authenticate(
|
||||
credentials: ICredentialDataDecryptedObject,
|
||||
requestOptions: IHttpRequestOptions,
|
||||
): Promise<IHttpRequestOptions> {
|
||||
if (credentials.classicApi === true) {
|
||||
requestOptions.headers = {
|
||||
'X-MailerLite-ApiKey': credentials.apiKey as string,
|
||||
};
|
||||
} else {
|
||||
requestOptions.headers = {
|
||||
Authorization: `Bearer ${credentials.apiKey as string}`,
|
||||
};
|
||||
}
|
||||
return requestOptions;
|
||||
}
|
||||
|
||||
test: ICredentialTestRequest = {
|
||||
request: {
|
||||
baseURL:
|
||||
'={{$credentials.classicApi ? "https://api.mailerlite.com/api/v2" : "https://connect.mailerlite.com/api"}}',
|
||||
url: '/groups',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,39 +3,38 @@ import type {
|
|||
IExecuteFunctions,
|
||||
IHookFunctions,
|
||||
ILoadOptionsFunctions,
|
||||
INodePropertyOptions,
|
||||
JsonObject,
|
||||
IRequestOptions,
|
||||
IHttpRequestOptions,
|
||||
IHttpRequestMethods,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeApiError } from 'n8n-workflow';
|
||||
|
||||
import type { CustomField } from './v2/MailerLite.Interface';
|
||||
|
||||
export async function mailerliteApiRequest(
|
||||
this: IExecuteFunctions | ILoadOptionsFunctions | IHookFunctions,
|
||||
method: IHttpRequestMethods,
|
||||
path: string,
|
||||
|
||||
body: any = {},
|
||||
qs: IDataObject = {},
|
||||
_option = {},
|
||||
): Promise<any> {
|
||||
const credentials = await this.getCredentials('mailerLiteApi');
|
||||
|
||||
const options: IRequestOptions = {
|
||||
headers: {
|
||||
'X-MailerLite-ApiKey': credentials.apiKey,
|
||||
},
|
||||
const options: IHttpRequestOptions = {
|
||||
method,
|
||||
body,
|
||||
qs,
|
||||
uri: `https://api.mailerlite.com/api/v2${path}`,
|
||||
url:
|
||||
this.getNode().typeVersion === 1
|
||||
? `https://api.mailerlite.com/api/v2${path}`
|
||||
: `https://connect.mailerlite.com/api${path}`,
|
||||
json: true,
|
||||
};
|
||||
try {
|
||||
if (Object.keys(body as IDataObject).length === 0) {
|
||||
delete options.body;
|
||||
}
|
||||
//@ts-ignore
|
||||
return await this.helpers.request.call(this, options);
|
||||
return await this.helpers.httpRequestWithAuthentication.call(this, 'mailerLiteApi', options);
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||
}
|
||||
|
@ -45,21 +44,56 @@ export async function mailerliteApiRequestAllItems(
|
|||
this: IExecuteFunctions | ILoadOptionsFunctions | IHookFunctions,
|
||||
method: IHttpRequestMethods,
|
||||
endpoint: string,
|
||||
|
||||
body: any = {},
|
||||
query: IDataObject = {},
|
||||
): Promise<any> {
|
||||
const returnData: IDataObject[] = [];
|
||||
|
||||
let responseData;
|
||||
|
||||
query.limit = 1000;
|
||||
query.offset = 0;
|
||||
|
||||
do {
|
||||
responseData = await mailerliteApiRequest.call(this, method, endpoint, body, query);
|
||||
returnData.push.apply(returnData, responseData as IDataObject[]);
|
||||
query.offset = query.offset + query.limit;
|
||||
} while (responseData.length !== 0);
|
||||
if (this.getNode().typeVersion === 1) {
|
||||
do {
|
||||
responseData = await mailerliteApiRequest.call(this, method, endpoint, body, query);
|
||||
returnData.push(...(responseData as IDataObject[]));
|
||||
query.offset += query.limit;
|
||||
} while (responseData.length !== 0);
|
||||
} else {
|
||||
do {
|
||||
responseData = await mailerliteApiRequest.call(this, method, endpoint, body, query);
|
||||
returnData.push(...(responseData.data as IDataObject[]));
|
||||
query.cursor = responseData.meta.next_cursor;
|
||||
} while (responseData.links.next !== null);
|
||||
}
|
||||
|
||||
return returnData;
|
||||
}
|
||||
|
||||
export async function getCustomFields(
|
||||
this: ILoadOptionsFunctions,
|
||||
): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
const endpoint = '/fields';
|
||||
const fieldsResponse = await mailerliteApiRequest.call(this, 'GET', endpoint);
|
||||
|
||||
if (this.getNode().typeVersion === 1) {
|
||||
const fields = fieldsResponse as CustomField[];
|
||||
fields.forEach((field) => {
|
||||
returnData.push({
|
||||
name: field.key,
|
||||
value: field.key,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
const fields = (fieldsResponse as IDataObject).data as CustomField[];
|
||||
fields.forEach((field) => {
|
||||
returnData.push({
|
||||
name: field.name,
|
||||
value: field.key,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return returnData;
|
||||
}
|
||||
|
|
|
@ -1,206 +1,26 @@
|
|||
import type {
|
||||
IExecuteFunctions,
|
||||
IDataObject,
|
||||
ILoadOptionsFunctions,
|
||||
INodeExecutionData,
|
||||
INodePropertyOptions,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeConnectionType } from 'n8n-workflow';
|
||||
import type { INodeTypeBaseDescription, IVersionedNodeType } from 'n8n-workflow';
|
||||
import { VersionedNodeType } from 'n8n-workflow';
|
||||
|
||||
import { mailerliteApiRequest, mailerliteApiRequestAllItems } from './GenericFunctions';
|
||||
import { MailerLiteV1 } from './v1/MailerLiteV1.node';
|
||||
import { MailerLiteV2 } from './v2/MailerLiteV2.node';
|
||||
|
||||
import { subscriberFields, subscriberOperations } from './SubscriberDescription';
|
||||
export class MailerLite extends VersionedNodeType {
|
||||
constructor() {
|
||||
const baseDescription: INodeTypeBaseDescription = {
|
||||
displayName: 'MailerLite',
|
||||
name: 'mailerLite',
|
||||
icon: 'file:MailerLite.svg',
|
||||
group: ['input'],
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Consume MailerLite API',
|
||||
defaultVersion: 2,
|
||||
};
|
||||
|
||||
export class MailerLite implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'MailerLite',
|
||||
name: 'mailerLite',
|
||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-icon-not-svg
|
||||
icon: 'file:mailerLite.png',
|
||||
group: ['input'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Consume Mailer Lite API',
|
||||
defaults: {
|
||||
name: 'MailerLite',
|
||||
},
|
||||
inputs: [NodeConnectionType.Main],
|
||||
outputs: [NodeConnectionType.Main],
|
||||
credentials: [
|
||||
{
|
||||
name: 'mailerLiteApi',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'Subscriber',
|
||||
value: 'subscriber',
|
||||
},
|
||||
],
|
||||
default: 'subscriber',
|
||||
},
|
||||
...subscriberOperations,
|
||||
...subscriberFields,
|
||||
],
|
||||
};
|
||||
const nodeVersions: IVersionedNodeType['nodeVersions'] = {
|
||||
1: new MailerLiteV1(baseDescription),
|
||||
2: new MailerLiteV2(baseDescription),
|
||||
};
|
||||
|
||||
methods = {
|
||||
loadOptions: {
|
||||
// Get all the available custom fields to display them to user so that they can
|
||||
// select them easily
|
||||
async getCustomFields(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
const fields = await mailerliteApiRequest.call(this, 'GET', '/fields');
|
||||
for (const field of fields) {
|
||||
returnData.push({
|
||||
name: field.key,
|
||||
value: field.key,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const items = this.getInputData();
|
||||
const returnData: INodeExecutionData[] = [];
|
||||
const length = items.length;
|
||||
const qs: IDataObject = {};
|
||||
let responseData;
|
||||
const resource = this.getNodeParameter('resource', 0);
|
||||
const operation = this.getNodeParameter('operation', 0);
|
||||
for (let i = 0; i < length; i++) {
|
||||
try {
|
||||
if (resource === 'subscriber') {
|
||||
//https://developers.mailerlite.com/reference#create-a-subscriber
|
||||
if (operation === 'create') {
|
||||
const email = this.getNodeParameter('email', i) as string;
|
||||
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i);
|
||||
|
||||
const body: IDataObject = {
|
||||
email,
|
||||
fields: [],
|
||||
};
|
||||
|
||||
Object.assign(body, additionalFields);
|
||||
|
||||
if (additionalFields.customFieldsUi) {
|
||||
const customFieldsValues = (additionalFields.customFieldsUi as IDataObject)
|
||||
.customFieldsValues as IDataObject[];
|
||||
|
||||
if (customFieldsValues) {
|
||||
const fields = {};
|
||||
|
||||
for (const customFieldValue of customFieldsValues) {
|
||||
//@ts-ignore
|
||||
fields[customFieldValue.fieldId] = customFieldValue.value;
|
||||
}
|
||||
|
||||
body.fields = fields;
|
||||
delete body.customFieldsUi;
|
||||
}
|
||||
}
|
||||
|
||||
responseData = await mailerliteApiRequest.call(this, 'POST', '/subscribers', body);
|
||||
}
|
||||
//https://developers.mailerlite.com/reference#single-subscriber
|
||||
if (operation === 'get') {
|
||||
const subscriberId = this.getNodeParameter('subscriberId', i) as string;
|
||||
|
||||
responseData = await mailerliteApiRequest.call(
|
||||
this,
|
||||
'GET',
|
||||
`/subscribers/${subscriberId}`,
|
||||
);
|
||||
}
|
||||
//https://developers.mailerlite.com/reference#subscribers
|
||||
if (operation === 'getAll') {
|
||||
const returnAll = this.getNodeParameter('returnAll', i);
|
||||
|
||||
const filters = this.getNodeParameter('filters', i);
|
||||
|
||||
Object.assign(qs, filters);
|
||||
|
||||
if (returnAll) {
|
||||
responseData = await mailerliteApiRequestAllItems.call(
|
||||
this,
|
||||
'GET',
|
||||
'/subscribers',
|
||||
{},
|
||||
qs,
|
||||
);
|
||||
} else {
|
||||
qs.limit = this.getNodeParameter('limit', i);
|
||||
|
||||
responseData = await mailerliteApiRequest.call(this, 'GET', '/subscribers', {}, qs);
|
||||
}
|
||||
}
|
||||
//https://developers.mailerlite.com/reference#update-subscriber
|
||||
if (operation === 'update') {
|
||||
const subscriberId = this.getNodeParameter('subscriberId', i) as string;
|
||||
|
||||
const updateFields = this.getNodeParameter('updateFields', i);
|
||||
|
||||
const body: IDataObject = {};
|
||||
|
||||
Object.assign(body, updateFields);
|
||||
|
||||
if (updateFields.customFieldsUi) {
|
||||
const customFieldsValues = (updateFields.customFieldsUi as IDataObject)
|
||||
.customFieldsValues as IDataObject[];
|
||||
|
||||
if (customFieldsValues) {
|
||||
const fields = {};
|
||||
|
||||
for (const customFieldValue of customFieldsValues) {
|
||||
//@ts-ignore
|
||||
fields[customFieldValue.fieldId] = customFieldValue.value;
|
||||
}
|
||||
|
||||
body.fields = fields;
|
||||
delete body.customFieldsUi;
|
||||
}
|
||||
}
|
||||
|
||||
responseData = await mailerliteApiRequest.call(
|
||||
this,
|
||||
'PUT',
|
||||
`/subscribers/${subscriberId}`,
|
||||
body,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
const executionErrorData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray({ error: error.message }),
|
||||
{ itemData: { item: i } },
|
||||
);
|
||||
returnData.push(...executionErrorData);
|
||||
continue;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject[]),
|
||||
{ itemData: { item: i } },
|
||||
);
|
||||
|
||||
returnData.push(...executionData);
|
||||
}
|
||||
|
||||
return [returnData];
|
||||
super(nodeVersions, baseDescription);
|
||||
}
|
||||
}
|
||||
|
|
33
packages/nodes-base/nodes/MailerLite/MailerLite.svg
Normal file
33
packages/nodes-base/nodes/MailerLite/MailerLite.svg
Normal file
|
@ -0,0 +1,33 @@
|
|||
<svg version="1.1" id="Layer_1" xmlns:x="ns_extend;" xmlns:i="ns_ai;" xmlns:graph="ns_graphs;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 62.8 50.2" style="enable-background:new 0 0 62.8 50.2;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#09C269;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<metadata>
|
||||
<sfw xmlns="ns_sfw;">
|
||||
<slices>
|
||||
</slices>
|
||||
<sliceSourceBounds bottomLeftOrigin="true" height="50.2" width="62.8" x="236.9" y="-225.3">
|
||||
</sliceSourceBounds>
|
||||
</sfw>
|
||||
</metadata>
|
||||
<g id="mailerlite-light">
|
||||
<g>
|
||||
<g id="lite" transform="translate(137.000000, 0.000000)">
|
||||
<path id="Shape-path" class="st0" d="M-81.2,0h-48.9c-3.8,0-6.9,3.1-6.9,6.8v22.8v4.5v16.2l9.5-9.3h46.4c3.8,0,6.9-3.1,6.9-6.8
|
||||
V6.8C-74.3,3.1-77.4,0-81.2,0z">
|
||||
</path>
|
||||
<path id="Shape-path-3" class="st1" d="M-90.2,15.8c5.2,0,7.6,4.1,7.6,8c0,1-0.8,1.8-1.8,1.8H-94c0.5,2.3,2.1,3.6,4.7,3.6
|
||||
c1.9,0,2.9-0.4,3.9-0.9c0.2-0.1,0.5-0.2,0.7-0.2c0.9,0,1.7,0.7,1.7,1.6c0,0.6-0.4,1.1-1,1.5c-1.3,0.7-2.7,1.4-5.5,1.4
|
||||
c-5.2,0-8.3-3.1-8.3-8.4C-97.9,18.1-93.7,15.8-90.2,15.8z M-105.5,13.2c0.6,0,1,0.5,1,1v1.9h2.9c0.9,0,1.7,0.7,1.7,1.6
|
||||
c0,0.9-0.7,1.6-1.7,1.6h-2.9V28c0,1.2,0.6,1.3,1.5,1.3c0.5,0,0.8-0.1,1.1-0.1c0.2,0,0.5-0.1,0.7-0.1c0.7,0,1.6,0.6,1.6,1.5
|
||||
c0,0.6-0.4,1.1-1,1.4c-0.9,0.4-1.7,0.6-2.7,0.6c-3.2,0-4.9-1.5-4.9-4.4v-8.8h-1.7c-0.6,0-1-0.5-1-1c0-0.3,0.1-0.6,0.4-0.9l4-4
|
||||
C-106.3,13.5-106,13.2-105.5,13.2z M-124.2,9.4c1,0,1.8,0.8,1.8,1.8v19.4c0,1-0.8,1.8-1.8,1.8s-1.8-0.8-1.8-1.8V11.2
|
||||
C-126,10.2-125.2,9.4-124.2,9.4z M-115.6,16c1,0,1.8,0.8,1.8,1.8v12.8c0,1-0.8,1.8-1.8,1.8c-1,0-1.8-0.8-1.8-1.8V17.8
|
||||
C-117.4,16.8-116.6,16-115.6,16z M-90.1,19.1c-1.7,0-3.6,1-3.9,3.5h7.9C-86.6,20.1-88.4,19.1-90.1,19.1z M-115.5,9.9
|
||||
c1.1,0,2,0.9,2,2V12c0,1.1-0.9,2-2,2h-0.2c-1.1,0-2-0.9-2-2v-0.1c0-1.1,0.9-2,2-2H-115.5z">
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -1,180 +1,25 @@
|
|||
import type {
|
||||
IHookFunctions,
|
||||
IWebhookFunctions,
|
||||
IDataObject,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
IWebhookResponseData,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeConnectionType } from 'n8n-workflow';
|
||||
import type { INodeTypeBaseDescription, IVersionedNodeType } from 'n8n-workflow';
|
||||
import { VersionedNodeType } from 'n8n-workflow';
|
||||
|
||||
import { mailerliteApiRequest } from './GenericFunctions';
|
||||
import { MailerLiteTriggerV1 } from './v1/MailerLiteTriggerV1.node';
|
||||
import { MailerLiteTriggerV2 } from './v2/MailerLiteTriggerV2.node';
|
||||
|
||||
export class MailerLiteTrigger implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'MailerLite Trigger',
|
||||
name: 'mailerLiteTrigger',
|
||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-icon-not-svg
|
||||
icon: 'file:mailerLite.png',
|
||||
group: ['trigger'],
|
||||
version: 1,
|
||||
description: 'Starts the workflow when MailerLite events occur',
|
||||
defaults: {
|
||||
name: 'MailerLite Trigger',
|
||||
},
|
||||
inputs: [],
|
||||
outputs: [NodeConnectionType.Main],
|
||||
credentials: [
|
||||
{
|
||||
name: 'mailerLiteApi',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
webhooks: [
|
||||
{
|
||||
name: 'default',
|
||||
httpMethod: 'POST',
|
||||
responseMode: 'onReceived',
|
||||
path: 'webhook',
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Event',
|
||||
name: 'event',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Campaign Sent',
|
||||
value: 'campaign.sent',
|
||||
description: 'Fired when campaign is sent',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Added Throught Webform',
|
||||
value: 'subscriber.added_through_webform',
|
||||
description: 'Fired when a subscriber is added though a form',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Added to Group',
|
||||
value: 'subscriber.add_to_group',
|
||||
description: 'Fired when a subscriber is added to a group',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Autonomation Completed',
|
||||
value: 'subscriber.automation_complete',
|
||||
description: 'Fired when subscriber finishes automation',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Autonomation Triggered',
|
||||
value: 'subscriber.automation_triggered',
|
||||
description: 'Fired when subscriber starts automation',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Bounced',
|
||||
value: 'subscriber.bounced',
|
||||
description: 'Fired when an email address bounces',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Complained',
|
||||
value: 'subscriber.complaint',
|
||||
description: 'Fired when subscriber marks a campaign as a spam',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Created',
|
||||
value: 'subscriber.create',
|
||||
description: 'Fired when a new subscriber is added to an account',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Removed From Group',
|
||||
value: 'subscriber.remove_from_group',
|
||||
description: 'Fired when a subscriber is removed from a group',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Unsubscribe',
|
||||
value: 'subscriber.unsubscribe',
|
||||
description: 'Fired when a subscriber becomes unsubscribed',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Updated',
|
||||
value: 'subscriber.update',
|
||||
description: "Fired when any of the subscriber's custom fields are updated",
|
||||
},
|
||||
],
|
||||
required: true,
|
||||
default: [],
|
||||
description: 'The events to listen to',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
webhookMethods = {
|
||||
default: {
|
||||
async checkExists(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookUrl = this.getNodeWebhookUrl('default');
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
const event = this.getNodeParameter('event') as string;
|
||||
// Check all the webhooks which exist already if it is identical to the
|
||||
// one that is supposed to get created.
|
||||
const endpoint = '/webhooks';
|
||||
const { webhooks } = await mailerliteApiRequest.call(this, 'GET', endpoint, {});
|
||||
for (const webhook of webhooks) {
|
||||
if (webhook.url === webhookUrl && webhook.event === event) {
|
||||
// Set webhook-id to be sure that it can be deleted
|
||||
webhookData.webhookId = webhook.id as string;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
async create(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
const webhookUrl = this.getNodeWebhookUrl('default');
|
||||
const event = this.getNodeParameter('event') as string;
|
||||
|
||||
const endpoint = '/webhooks';
|
||||
|
||||
const body = {
|
||||
url: webhookUrl,
|
||||
event,
|
||||
};
|
||||
|
||||
const responseData = await mailerliteApiRequest.call(this, 'POST', endpoint, body);
|
||||
|
||||
if (responseData.id === undefined) {
|
||||
// Required data is missing so was not successful
|
||||
return false;
|
||||
}
|
||||
|
||||
webhookData.webhookId = responseData.id as string;
|
||||
return true;
|
||||
},
|
||||
async delete(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
if (webhookData.webhookId !== undefined) {
|
||||
const endpoint = `/webhooks/${webhookData.webhookId}`;
|
||||
|
||||
try {
|
||||
await mailerliteApiRequest.call(this, 'DELETE', endpoint);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove from the static workflow data so that it is clear
|
||||
// that no webhooks are registered anymore
|
||||
delete webhookData.webhookId;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
|
||||
const body = this.getBodyData();
|
||||
|
||||
const events = body.events as IDataObject[];
|
||||
|
||||
return {
|
||||
workflowData: [this.helpers.returnJsonArray(events)],
|
||||
export class MailerLiteTrigger extends VersionedNodeType {
|
||||
constructor() {
|
||||
const baseDescription: INodeTypeBaseDescription = {
|
||||
displayName: 'MailerLite Trigger',
|
||||
name: 'mailerLiteTrigger',
|
||||
icon: 'file:MailerLite.svg',
|
||||
group: ['trigger'],
|
||||
description: 'Starts the workflow when MailerLite events occur',
|
||||
defaultVersion: 2,
|
||||
};
|
||||
|
||||
const nodeVersions: IVersionedNodeType['nodeVersions'] = {
|
||||
1: new MailerLiteTriggerV1(baseDescription),
|
||||
2: new MailerLiteTriggerV2(baseDescription),
|
||||
};
|
||||
|
||||
super(nodeVersions, baseDescription);
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 893 B |
|
@ -0,0 +1,253 @@
|
|||
/* eslint-disable n8n-nodes-base/node-param-display-name-miscased */
|
||||
import {
|
||||
type IExecuteFunctions,
|
||||
type ILoadOptionsFunctions,
|
||||
type IHookFunctions,
|
||||
NodeApiError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
getCustomFields,
|
||||
mailerliteApiRequest,
|
||||
mailerliteApiRequestAllItems,
|
||||
} from '../GenericFunctions';
|
||||
|
||||
describe('MailerLite -> mailerliteApiRequest', () => {
|
||||
let mockExecuteFunctions: IExecuteFunctions | ILoadOptionsFunctions | IHookFunctions;
|
||||
|
||||
const setupMockFunctions = (typeVersion: number) => {
|
||||
mockExecuteFunctions = {
|
||||
getNode: jest.fn().mockReturnValue({ typeVersion }),
|
||||
helpers: {
|
||||
httpRequestWithAuthentication: jest.fn(),
|
||||
},
|
||||
} as unknown as IExecuteFunctions | ILoadOptionsFunctions | IHookFunctions;
|
||||
jest.clearAllMocks();
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
setupMockFunctions(1);
|
||||
});
|
||||
|
||||
it('should make a successful API request for type version 1', async () => {
|
||||
const method = 'GET';
|
||||
const path = '/test';
|
||||
const body = {};
|
||||
const qs = {};
|
||||
|
||||
const responseData = { success: true };
|
||||
|
||||
(mockExecuteFunctions.helpers.httpRequestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
responseData,
|
||||
);
|
||||
|
||||
const result = await mailerliteApiRequest.call(mockExecuteFunctions, method, path, body, qs);
|
||||
|
||||
expect(result).toEqual(responseData);
|
||||
expect(mockExecuteFunctions.helpers.httpRequestWithAuthentication).toHaveBeenCalledWith(
|
||||
'mailerLiteApi',
|
||||
{
|
||||
method,
|
||||
qs,
|
||||
url: 'https://api.mailerlite.com/api/v2/test',
|
||||
json: true,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('should make a successful API request for type version 2', async () => {
|
||||
setupMockFunctions(2);
|
||||
|
||||
const method = 'GET';
|
||||
const path = '/test';
|
||||
const body = {};
|
||||
const qs = {};
|
||||
|
||||
const responseData = { success: true };
|
||||
|
||||
(mockExecuteFunctions.helpers.httpRequestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
responseData,
|
||||
);
|
||||
|
||||
const result = await mailerliteApiRequest.call(mockExecuteFunctions, method, path, body, qs);
|
||||
|
||||
expect(result).toEqual(responseData);
|
||||
expect(mockExecuteFunctions.helpers.httpRequestWithAuthentication).toHaveBeenCalledWith(
|
||||
'mailerLiteApi',
|
||||
{
|
||||
method,
|
||||
qs,
|
||||
url: 'https://connect.mailerlite.com/api/test',
|
||||
json: true,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('should make an API request with an empty body', async () => {
|
||||
const method = 'GET';
|
||||
const path = '/test';
|
||||
const body = {};
|
||||
const qs = {};
|
||||
|
||||
const responseData = { success: true };
|
||||
|
||||
(mockExecuteFunctions.helpers.httpRequestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
responseData,
|
||||
);
|
||||
|
||||
const result = await mailerliteApiRequest.call(mockExecuteFunctions, method, path, body, qs);
|
||||
|
||||
expect(result).toEqual(responseData);
|
||||
expect(mockExecuteFunctions.helpers.httpRequestWithAuthentication).toHaveBeenCalledWith(
|
||||
'mailerLiteApi',
|
||||
{
|
||||
method,
|
||||
qs,
|
||||
url: 'https://api.mailerlite.com/api/v2/test',
|
||||
json: true,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if the API request fails', async () => {
|
||||
const method = 'GET';
|
||||
const path = '/test';
|
||||
const body = {};
|
||||
const qs = {};
|
||||
|
||||
const errorResponse = { message: 'Error' };
|
||||
|
||||
(mockExecuteFunctions.helpers.httpRequestWithAuthentication as jest.Mock).mockRejectedValue(
|
||||
errorResponse,
|
||||
);
|
||||
|
||||
await expect(
|
||||
mailerliteApiRequest.call(mockExecuteFunctions, method, path, body, qs),
|
||||
).rejects.toThrow(NodeApiError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('MailerLite -> mailerliteApiRequestAllItems', () => {
|
||||
let mockExecuteFunctions: IExecuteFunctions | ILoadOptionsFunctions | IHookFunctions;
|
||||
|
||||
const setupMockFunctions = (typeVersion: number) => {
|
||||
mockExecuteFunctions = {
|
||||
getNode: jest.fn().mockReturnValue({ typeVersion }),
|
||||
helpers: {
|
||||
httpRequestWithAuthentication: jest.fn(),
|
||||
},
|
||||
} as unknown as IExecuteFunctions | ILoadOptionsFunctions | IHookFunctions;
|
||||
jest.clearAllMocks();
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
setupMockFunctions(1);
|
||||
});
|
||||
|
||||
it('should handle pagination for type version 1', async () => {
|
||||
const method = 'GET';
|
||||
const endpoint = '/test';
|
||||
const body = {};
|
||||
const query = {};
|
||||
|
||||
const responseDataPage1 = [{ id: 1 }, { id: 2 }];
|
||||
const responseDataPage2 = [{ id: 3 }, { id: 4 }];
|
||||
|
||||
(mockExecuteFunctions.helpers.httpRequestWithAuthentication as jest.Mock)
|
||||
.mockResolvedValueOnce(responseDataPage1)
|
||||
.mockResolvedValueOnce(responseDataPage2)
|
||||
.mockResolvedValueOnce([]);
|
||||
|
||||
const result = await mailerliteApiRequestAllItems.call(
|
||||
mockExecuteFunctions,
|
||||
method,
|
||||
endpoint,
|
||||
body,
|
||||
query,
|
||||
);
|
||||
|
||||
expect(result).toEqual([...responseDataPage1, ...responseDataPage2]);
|
||||
});
|
||||
|
||||
it('should handle pagination for type version 2', async () => {
|
||||
setupMockFunctions(2);
|
||||
|
||||
const method = 'GET';
|
||||
const endpoint = '/test';
|
||||
const body = {};
|
||||
const query = {};
|
||||
|
||||
const responseDataPage1 = {
|
||||
data: [{ id: 1 }, { id: 2 }],
|
||||
meta: { next_cursor: 'cursor1' },
|
||||
links: { next: 'nextLink1' },
|
||||
};
|
||||
const responseDataPage2 = {
|
||||
data: [{ id: 3 }, { id: 4 }],
|
||||
meta: { next_cursor: null },
|
||||
links: { next: null },
|
||||
};
|
||||
|
||||
(mockExecuteFunctions.helpers.httpRequestWithAuthentication as jest.Mock)
|
||||
.mockResolvedValueOnce(responseDataPage1)
|
||||
.mockResolvedValueOnce(responseDataPage2);
|
||||
|
||||
const result = await mailerliteApiRequestAllItems.call(
|
||||
mockExecuteFunctions,
|
||||
method,
|
||||
endpoint,
|
||||
body,
|
||||
query,
|
||||
);
|
||||
|
||||
expect(result).toEqual([...responseDataPage1.data, ...responseDataPage2.data]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('MailerLite -> getCustomFields', () => {
|
||||
let mockExecuteFunctions: ILoadOptionsFunctions;
|
||||
|
||||
const v1FieldResponse = [
|
||||
{ name: 'Field1', key: 'field1' },
|
||||
{ name: 'Field2', key: 'field2' },
|
||||
];
|
||||
|
||||
const v2FieldResponse = {
|
||||
data: v1FieldResponse,
|
||||
};
|
||||
|
||||
const setupMockFunctions = (typeVersion: number) => {
|
||||
mockExecuteFunctions = {
|
||||
getNode: jest.fn().mockReturnValue({ typeVersion }),
|
||||
helpers: {
|
||||
httpRequestWithAuthentication: jest
|
||||
.fn()
|
||||
.mockResolvedValue(typeVersion === 1 ? v1FieldResponse : v2FieldResponse),
|
||||
},
|
||||
} as unknown as ILoadOptionsFunctions;
|
||||
jest.clearAllMocks();
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
setupMockFunctions(1);
|
||||
});
|
||||
|
||||
it('should return custom fields for type version 1', async () => {
|
||||
const result = await getCustomFields.call(mockExecuteFunctions);
|
||||
|
||||
expect(result).toEqual([
|
||||
{ name: 'field1', value: 'field1' },
|
||||
{ name: 'field2', value: 'field2' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return custom fields for type version 2', async () => {
|
||||
setupMockFunctions(2);
|
||||
const result = await getCustomFields.call(mockExecuteFunctions);
|
||||
|
||||
expect(result).toEqual([
|
||||
{ name: 'Field1', value: 'field1' },
|
||||
{ name: 'Field2', value: 'field2' },
|
||||
]);
|
||||
});
|
||||
});
|
411
packages/nodes-base/nodes/MailerLite/tests/apiResponses.ts
Normal file
411
packages/nodes-base/nodes/MailerLite/tests/apiResponses.ts
Normal file
|
@ -0,0 +1,411 @@
|
|||
export const getUpdateSubscriberResponseClassic = {
|
||||
id: 1343965485,
|
||||
email: 'demo@mailerlite.com',
|
||||
sent: 0,
|
||||
opened: 0,
|
||||
clicked: 0,
|
||||
type: 'unsubscribed',
|
||||
fields: [
|
||||
{
|
||||
key: 'email',
|
||||
value: 'demo@mailerlite.com',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
value: 'Demo',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'last_name',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'company',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'country',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'city',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'phone',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'state',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'zip',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
],
|
||||
date_subscribe: null,
|
||||
date_unsubscribe: '2016-04-04 12:07:26',
|
||||
date_created: '2016-04-04',
|
||||
date_updated: null,
|
||||
};
|
||||
export const getSubscriberResponseClassic = {
|
||||
id: 1343965485,
|
||||
name: 'John',
|
||||
email: 'demo@mailerlite.com',
|
||||
sent: 0,
|
||||
opened: 0,
|
||||
clicked: 0,
|
||||
type: 'active',
|
||||
signup_ip: '127.0.0.1',
|
||||
signup_timestamp: '2018-01-01 01:01:01',
|
||||
confirmation_ip: '127.0.0.2',
|
||||
confirmation_timestamp: '2018-01-01 01:01:02',
|
||||
fields: [
|
||||
{
|
||||
key: 'email',
|
||||
value: 'demo@mailerlite.com',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
value: 'John',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'last_name',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'company',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'country',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'city',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'phone',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'state',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'zip',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
],
|
||||
date_subscribe: null,
|
||||
date_unsubscribe: null,
|
||||
date_created: '2016-04-04',
|
||||
date_updated: null,
|
||||
};
|
||||
export const getCreateResponseClassic = {
|
||||
id: 1343965485,
|
||||
name: 'John',
|
||||
email: 'demo@mailerlite.com',
|
||||
sent: 0,
|
||||
opened: 0,
|
||||
clicked: 0,
|
||||
type: 'active',
|
||||
fields: [
|
||||
{
|
||||
key: 'email',
|
||||
value: 'demo@mailerlite.com',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
value: 'John',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'last_name',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'company',
|
||||
value: 'MailerLite',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'country',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'city',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'phone',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'state',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'zip',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
],
|
||||
date_subscribe: null,
|
||||
date_unsubscribe: null,
|
||||
date_created: '2016-04-04 12:00:00',
|
||||
date_updated: '2016-04-04 12:00:00',
|
||||
};
|
||||
export const getAllSubscribersResponseClassic = [
|
||||
{
|
||||
id: 1343965485,
|
||||
name: 'John',
|
||||
email: 'demo@mailerlite.com',
|
||||
sent: 0,
|
||||
opened: 0,
|
||||
clicked: 0,
|
||||
type: 'active',
|
||||
fields: [
|
||||
{
|
||||
key: 'email',
|
||||
value: 'demo@mailerlite.com',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
value: 'John',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'last_name',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'company',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'country',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'city',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'phone',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'state',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
key: 'zip',
|
||||
value: '',
|
||||
type: 'TEXT',
|
||||
},
|
||||
],
|
||||
date_subscribe: null,
|
||||
date_unsubscribe: null,
|
||||
date_created: '2016-04-04',
|
||||
date_updated: null,
|
||||
},
|
||||
];
|
||||
|
||||
export const getUpdateSubscriberResponseV2 = {
|
||||
data: {
|
||||
id: '139872142007207563',
|
||||
email: 'user@n8n.io',
|
||||
status: 'junk',
|
||||
source: 'api',
|
||||
sent: 0,
|
||||
opens_count: 0,
|
||||
clicks_count: 0,
|
||||
open_rate: 0,
|
||||
click_rate: 0,
|
||||
ip_address: null,
|
||||
subscribed_at: '2024-12-05 09:54:29',
|
||||
unsubscribed_at: null,
|
||||
created_at: '2024-12-05 09:54:29',
|
||||
updated_at: '2024-12-05 10:20:32',
|
||||
fields: {
|
||||
name: null,
|
||||
last_name: null,
|
||||
company: null,
|
||||
country: null,
|
||||
city: null,
|
||||
phone: null,
|
||||
state: null,
|
||||
z_i_p: null,
|
||||
},
|
||||
groups: [],
|
||||
opted_in_at: null,
|
||||
optin_ip: '8.8.8.8',
|
||||
},
|
||||
};
|
||||
export const getCreateResponseV2 = {
|
||||
data: {
|
||||
id: '139872142007207563',
|
||||
email: 'user@n8n.io',
|
||||
status: 'junk',
|
||||
source: 'api',
|
||||
sent: 0,
|
||||
opens_count: 0,
|
||||
clicks_count: 0,
|
||||
open_rate: 0,
|
||||
click_rate: 0,
|
||||
ip_address: null,
|
||||
subscribed_at: '2024-12-05 09:54:29',
|
||||
unsubscribed_at: null,
|
||||
created_at: '2024-12-05 09:54:29',
|
||||
updated_at: '2024-12-05 10:20:32',
|
||||
fields: {
|
||||
name: null,
|
||||
last_name: null,
|
||||
company: null,
|
||||
country: null,
|
||||
city: null,
|
||||
phone: null,
|
||||
state: null,
|
||||
z_i_p: null,
|
||||
},
|
||||
groups: [],
|
||||
opted_in_at: null,
|
||||
optin_ip: '8.8.8.8',
|
||||
},
|
||||
};
|
||||
export const getSubscriberResponseV2 = {
|
||||
data: {
|
||||
id: '139872142007207563',
|
||||
email: 'user@n8n.io',
|
||||
status: 'junk',
|
||||
source: 'api',
|
||||
sent: 0,
|
||||
opens_count: 0,
|
||||
clicks_count: 0,
|
||||
open_rate: 0,
|
||||
click_rate: 0,
|
||||
ip_address: null,
|
||||
subscribed_at: '2024-12-05 09:54:29',
|
||||
unsubscribed_at: null,
|
||||
created_at: '2024-12-05 09:54:29',
|
||||
updated_at: '2024-12-05 10:20:32',
|
||||
fields: {
|
||||
name: null,
|
||||
last_name: null,
|
||||
company: null,
|
||||
country: null,
|
||||
city: null,
|
||||
phone: null,
|
||||
state: null,
|
||||
z_i_p: null,
|
||||
},
|
||||
groups: [],
|
||||
opted_in_at: null,
|
||||
optin_ip: '8.8.8.8',
|
||||
},
|
||||
};
|
||||
export const getAllSubscribersResponseV2 = {
|
||||
data: [
|
||||
{
|
||||
id: '139872142007207563',
|
||||
email: 'user@n8n.io',
|
||||
status: 'junk',
|
||||
source: 'api',
|
||||
sent: 0,
|
||||
opens_count: 0,
|
||||
clicks_count: 0,
|
||||
open_rate: 0,
|
||||
click_rate: 0,
|
||||
ip_address: null,
|
||||
subscribed_at: '2024-12-05 09:54:29',
|
||||
unsubscribed_at: null,
|
||||
created_at: '2024-12-05 09:54:29',
|
||||
updated_at: '2024-12-05 10:20:32',
|
||||
fields: {
|
||||
name: null,
|
||||
last_name: null,
|
||||
company: null,
|
||||
country: null,
|
||||
city: null,
|
||||
phone: null,
|
||||
state: null,
|
||||
z_i_p: null,
|
||||
},
|
||||
opted_in_at: null,
|
||||
optin_ip: '8.8.8.8',
|
||||
},
|
||||
{
|
||||
id: '139059851540038710',
|
||||
email: 'nathan@n8n.io',
|
||||
status: 'junk',
|
||||
source: 'api',
|
||||
sent: 0,
|
||||
opens_count: 0,
|
||||
clicks_count: 0,
|
||||
open_rate: 0,
|
||||
click_rate: 0,
|
||||
ip_address: null,
|
||||
subscribed_at: null,
|
||||
unsubscribed_at: null,
|
||||
created_at: '2024-11-26 10:43:28',
|
||||
updated_at: '2024-11-27 10:09:34',
|
||||
fields: {
|
||||
name: 'Nathan',
|
||||
last_name: 'Workflow',
|
||||
company: null,
|
||||
country: null,
|
||||
city: null,
|
||||
phone: null,
|
||||
state: null,
|
||||
z_i_p: null,
|
||||
},
|
||||
opted_in_at: null,
|
||||
optin_ip: null,
|
||||
},
|
||||
],
|
||||
links: {
|
||||
first: null,
|
||||
last: null,
|
||||
prev: null,
|
||||
next: null,
|
||||
},
|
||||
meta: {
|
||||
path: 'https://connect.mailerlite.com/api/subscribers',
|
||||
per_page: 2,
|
||||
next_cursor: null,
|
||||
prev_cursor: null,
|
||||
},
|
||||
};
|
|
@ -0,0 +1,460 @@
|
|||
{
|
||||
"name": "[TEST] MailerLite v1 Node",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "be5a39ea-04bf-49a3-969f-47d4a9496f08",
|
||||
"name": "When clicking ‘Test workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [-340, 280]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"email": "demo@mailerlite.com",
|
||||
"additionalFields": {}
|
||||
},
|
||||
"id": "98d30bbe-cbdd-4313-933e-804cdf322860",
|
||||
"name": "Create Subscriber",
|
||||
"type": "n8n-nodes-base.mailerLite",
|
||||
"typeVersion": 1,
|
||||
"position": [-140, 40],
|
||||
"credentials": {
|
||||
"mailerLiteApi": {
|
||||
"id": "bm7VHS2C7lRgVOhb",
|
||||
"name": "Mailer Lite account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [80, 40],
|
||||
"id": "93aa764f-5101-4961-9b51-9fa92f746337",
|
||||
"name": "No Operation, do nothing"
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [80, 220],
|
||||
"id": "4dccb059-c4f6-4eae-b68a-5c5c36d0b8d4",
|
||||
"name": "No Operation, do nothing1"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "get",
|
||||
"subscriberId": "demo@mailerlite.com"
|
||||
},
|
||||
"type": "n8n-nodes-base.mailerLite",
|
||||
"typeVersion": 1,
|
||||
"position": [-140, 220],
|
||||
"id": "82115adf-edf4-4ce4-9109-3ade129294d1",
|
||||
"name": "Get Subscriber",
|
||||
"credentials": {
|
||||
"mailerLiteApi": {
|
||||
"id": "bm7VHS2C7lRgVOhb",
|
||||
"name": "Mailer Lite account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "update",
|
||||
"subscriberId": "demo@mailerlite.com",
|
||||
"updateFields": {
|
||||
"type": "active"
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.mailerLite",
|
||||
"typeVersion": 1,
|
||||
"position": [-140, 420],
|
||||
"id": "fae9c6bd-1bd1-4ee8-865d-283b7edb6004",
|
||||
"name": "Update Subscriber",
|
||||
"credentials": {
|
||||
"mailerLiteApi": {
|
||||
"id": "bm7VHS2C7lRgVOhb",
|
||||
"name": "Mailer Lite account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [80, 420],
|
||||
"id": "45937d69-3956-434d-b955-a67d77d43d57",
|
||||
"name": "No Operation, do nothing2"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "getAll",
|
||||
"limit": 1,
|
||||
"filters": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.mailerLite",
|
||||
"typeVersion": 1,
|
||||
"position": [-180, 680],
|
||||
"id": "6491d933-0929-44bd-89cf-977823dde650",
|
||||
"name": "Get Many Subscrbers",
|
||||
"credentials": {
|
||||
"mailerLiteApi": {
|
||||
"id": "bm7VHS2C7lRgVOhb",
|
||||
"name": "Mailer Lite account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [40, 680],
|
||||
"id": "6e35d6e1-1ce3-4410-8558-a5d573676d8a",
|
||||
"name": "No Operation, do nothing3"
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1343965485,
|
||||
"name": "John",
|
||||
"email": "demo@mailerlite.com",
|
||||
"sent": 0,
|
||||
"opened": 0,
|
||||
"clicked": 0,
|
||||
"type": "active",
|
||||
"fields": [
|
||||
{
|
||||
"key": "email",
|
||||
"value": "demo@mailerlite.com",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "name",
|
||||
"value": "John",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "last_name",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "company",
|
||||
"value": "MailerLite",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "country",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "city",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "phone",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "state",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "zip",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
}
|
||||
],
|
||||
"date_subscribe": null,
|
||||
"date_unsubscribe": null,
|
||||
"date_created": "2016-04-04 12:00:00",
|
||||
"date_updated": "2016-04-04 12:00:00"
|
||||
}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing1": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1343965485,
|
||||
"name": "John",
|
||||
"email": "demo@mailerlite.com",
|
||||
"sent": 0,
|
||||
"opened": 0,
|
||||
"clicked": 0,
|
||||
"type": "active",
|
||||
"signup_ip": "127.0.0.1",
|
||||
"signup_timestamp": "2018-01-01 01:01:01",
|
||||
"confirmation_ip": "127.0.0.2",
|
||||
"confirmation_timestamp": "2018-01-01 01:01:02",
|
||||
"fields": [
|
||||
{
|
||||
"key": "email",
|
||||
"value": "demo@mailerlite.com",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "name",
|
||||
"value": "John",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "last_name",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "company",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "country",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "city",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "phone",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "state",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "zip",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
}
|
||||
],
|
||||
"date_subscribe": null,
|
||||
"date_unsubscribe": null,
|
||||
"date_created": "2016-04-04",
|
||||
"date_updated": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing2": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1343965485,
|
||||
"email": "demo@mailerlite.com",
|
||||
"sent": 0,
|
||||
"opened": 0,
|
||||
"clicked": 0,
|
||||
"type": "unsubscribed",
|
||||
"fields": [
|
||||
{
|
||||
"key": "email",
|
||||
"value": "demo@mailerlite.com",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "name",
|
||||
"value": "Demo",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "last_name",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "company",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "country",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "city",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "phone",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "state",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "zip",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
}
|
||||
],
|
||||
"date_subscribe": null,
|
||||
"date_unsubscribe": "2016-04-04 12:07:26",
|
||||
"date_created": "2016-04-04",
|
||||
"date_updated": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing3": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1343965485,
|
||||
"name": "John",
|
||||
"email": "demo@mailerlite.com",
|
||||
"sent": 0,
|
||||
"opened": 0,
|
||||
"clicked": 0,
|
||||
"type": "active",
|
||||
"fields": [
|
||||
{
|
||||
"key": "email",
|
||||
"value": "demo@mailerlite.com",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "name",
|
||||
"value": "John",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "last_name",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "company",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "country",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "city",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "phone",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "state",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
},
|
||||
{
|
||||
"key": "zip",
|
||||
"value": "",
|
||||
"type": "TEXT"
|
||||
}
|
||||
],
|
||||
"date_subscribe": null,
|
||||
"date_unsubscribe": null,
|
||||
"date_created": "2016-04-04",
|
||||
"date_updated": null
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking ‘Test workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Create Subscriber",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Get Subscriber",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Update Subscriber",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Get Many Subscrbers",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Create Subscriber": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get Subscriber": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Update Subscriber": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get Many Subscrbers": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing3",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "826c1711-fcea-4564-809b-0258dbdd72f4",
|
||||
"meta": {
|
||||
"instanceId": "8c8c5237b8e37b006a7adce87f4369350c58e41f3ca9de16196d3197f69eabcd"
|
||||
},
|
||||
"id": "I0absgO5t7xV2f2V",
|
||||
"tags": []
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
import nock from 'nock';
|
||||
|
||||
import { getWorkflowFilenames, testWorkflows } from '../../../../test/nodes/Helpers';
|
||||
import {
|
||||
getCreateResponseClassic,
|
||||
getSubscriberResponseClassic,
|
||||
getAllSubscribersResponseClassic,
|
||||
getUpdateSubscriberResponseClassic,
|
||||
} from '../apiResponses';
|
||||
|
||||
describe('MailerLite', () => {
|
||||
describe('Run v1 workflow', () => {
|
||||
beforeAll(() => {
|
||||
nock.disableNetConnect();
|
||||
|
||||
const mock = nock('https://api.mailerlite.com/api/v2');
|
||||
|
||||
mock.post('/subscribers').reply(200, getCreateResponseClassic);
|
||||
mock.get('/subscribers/demo@mailerlite.com').reply(200, getSubscriberResponseClassic);
|
||||
mock.get('/subscribers').query({ limit: 1 }).reply(200, getAllSubscribersResponseClassic);
|
||||
mock.put('/subscribers/demo@mailerlite.com').reply(200, getUpdateSubscriberResponseClassic);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
nock.restore();
|
||||
});
|
||||
|
||||
const workflows = getWorkflowFilenames(__dirname);
|
||||
testWorkflows(workflows);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,370 @@
|
|||
{
|
||||
"name": "[TEST] MailerLite v2 Node",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "3c72284b-2b88-4d5f-81bc-b1970b14f2af",
|
||||
"name": "When clicking ‘Test workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [800, 240]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"email": "user@n8n.io",
|
||||
"additionalFields": {
|
||||
"status": "active"
|
||||
}
|
||||
},
|
||||
"id": "702c6598-cbe8-403e-962e-56621ec727a4",
|
||||
"name": "Create Subscriber",
|
||||
"type": "n8n-nodes-base.mailerLite",
|
||||
"typeVersion": 2,
|
||||
"position": [1000, 0],
|
||||
"credentials": {
|
||||
"mailerLiteApi": {
|
||||
"id": "bm7VHS2C7lRgVOhb",
|
||||
"name": "Mailer Lite account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1220, 0],
|
||||
"id": "540b98b5-b3bf-49a1-a406-acc6872f4b50",
|
||||
"name": "No Operation, do nothing"
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1220, 180],
|
||||
"id": "17c0b8e7-a9d7-4a4f-882f-c3fb3f6bc289",
|
||||
"name": "No Operation, do nothing1"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "get",
|
||||
"subscriberId": "user@n8n.io"
|
||||
},
|
||||
"type": "n8n-nodes-base.mailerLite",
|
||||
"typeVersion": 2,
|
||||
"position": [1000, 180],
|
||||
"id": "5598f2b9-4d67-4ad7-a8e4-7b7bf723cd5a",
|
||||
"name": "Get Subscriber",
|
||||
"credentials": {
|
||||
"mailerLiteApi": {
|
||||
"id": "bm7VHS2C7lRgVOhb",
|
||||
"name": "Mailer Lite account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "update",
|
||||
"subscriberId": "user@n8n.io",
|
||||
"additionalFields": {
|
||||
"status": "junk",
|
||||
"optin_ip": "8.8.8.8"
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.mailerLite",
|
||||
"typeVersion": 2,
|
||||
"position": [1000, 380],
|
||||
"id": "223e4507-c88e-4066-a122-ccaf9cea7b49",
|
||||
"name": "Update Subscriber",
|
||||
"credentials": {
|
||||
"mailerLiteApi": {
|
||||
"id": "bm7VHS2C7lRgVOhb",
|
||||
"name": "Mailer Lite account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1220, 380],
|
||||
"id": "94d04b52-8809-4670-a8ca-135921139fc9",
|
||||
"name": "No Operation, do nothing2"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "getAll",
|
||||
"limit": 2,
|
||||
"filters": {
|
||||
"status": "junk"
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.mailerLite",
|
||||
"typeVersion": 2,
|
||||
"position": [960, 640],
|
||||
"id": "30c6e797-ceda-4c84-8f34-b61200ffd9e9",
|
||||
"name": "Get Many Subscrbers",
|
||||
"credentials": {
|
||||
"mailerLiteApi": {
|
||||
"id": "bm7VHS2C7lRgVOhb",
|
||||
"name": "Mailer Lite account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1180, 640],
|
||||
"id": "c8529a30-889b-4ac9-a509-73f5dd8eef4a",
|
||||
"name": "No Operation, do nothing3"
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {
|
||||
"id": "139872142007207563",
|
||||
"email": "user@n8n.io",
|
||||
"status": "junk",
|
||||
"source": "api",
|
||||
"sent": 0,
|
||||
"opens_count": 0,
|
||||
"clicks_count": 0,
|
||||
"open_rate": 0,
|
||||
"click_rate": 0,
|
||||
"ip_address": null,
|
||||
"subscribed_at": "2024-12-05 09:54:29",
|
||||
"unsubscribed_at": null,
|
||||
"created_at": "2024-12-05 09:54:29",
|
||||
"updated_at": "2024-12-05 10:20:32",
|
||||
"fields": {
|
||||
"name": null,
|
||||
"last_name": null,
|
||||
"company": null,
|
||||
"country": null,
|
||||
"city": null,
|
||||
"phone": null,
|
||||
"state": null,
|
||||
"z_i_p": null
|
||||
},
|
||||
"groups": [],
|
||||
"opted_in_at": null,
|
||||
"optin_ip": "8.8.8.8"
|
||||
}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing1": [
|
||||
{
|
||||
"json": {
|
||||
"id": "139872142007207563",
|
||||
"email": "user@n8n.io",
|
||||
"status": "junk",
|
||||
"source": "api",
|
||||
"sent": 0,
|
||||
"opens_count": 0,
|
||||
"clicks_count": 0,
|
||||
"open_rate": 0,
|
||||
"click_rate": 0,
|
||||
"ip_address": null,
|
||||
"subscribed_at": "2024-12-05 09:54:29",
|
||||
"unsubscribed_at": null,
|
||||
"created_at": "2024-12-05 09:54:29",
|
||||
"updated_at": "2024-12-05 10:20:32",
|
||||
"fields": {
|
||||
"name": null,
|
||||
"last_name": null,
|
||||
"company": null,
|
||||
"country": null,
|
||||
"city": null,
|
||||
"phone": null,
|
||||
"state": null,
|
||||
"z_i_p": null
|
||||
},
|
||||
"groups": [],
|
||||
"opted_in_at": null,
|
||||
"optin_ip": "8.8.8.8"
|
||||
}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing2": [
|
||||
{
|
||||
"json": {
|
||||
"data": {
|
||||
"id": "139872142007207563",
|
||||
"email": "user@n8n.io",
|
||||
"status": "junk",
|
||||
"source": "api",
|
||||
"sent": 0,
|
||||
"opens_count": 0,
|
||||
"clicks_count": 0,
|
||||
"open_rate": 0,
|
||||
"click_rate": 0,
|
||||
"ip_address": null,
|
||||
"subscribed_at": "2024-12-05 09:54:29",
|
||||
"unsubscribed_at": null,
|
||||
"created_at": "2024-12-05 09:54:29",
|
||||
"updated_at": "2024-12-05 10:20:32",
|
||||
"fields": {
|
||||
"name": null,
|
||||
"last_name": null,
|
||||
"company": null,
|
||||
"country": null,
|
||||
"city": null,
|
||||
"phone": null,
|
||||
"state": null,
|
||||
"z_i_p": null
|
||||
},
|
||||
"groups": [],
|
||||
"opted_in_at": null,
|
||||
"optin_ip": "8.8.8.8"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing3": [
|
||||
{
|
||||
"json": {
|
||||
"id": "139872142007207563",
|
||||
"email": "user@n8n.io",
|
||||
"status": "junk",
|
||||
"source": "api",
|
||||
"sent": 0,
|
||||
"opens_count": 0,
|
||||
"clicks_count": 0,
|
||||
"open_rate": 0,
|
||||
"click_rate": 0,
|
||||
"ip_address": null,
|
||||
"subscribed_at": "2024-12-05 09:54:29",
|
||||
"unsubscribed_at": null,
|
||||
"created_at": "2024-12-05 09:54:29",
|
||||
"updated_at": "2024-12-05 10:20:32",
|
||||
"fields": {
|
||||
"name": null,
|
||||
"last_name": null,
|
||||
"company": null,
|
||||
"country": null,
|
||||
"city": null,
|
||||
"phone": null,
|
||||
"state": null,
|
||||
"z_i_p": null
|
||||
},
|
||||
"opted_in_at": null,
|
||||
"optin_ip": "8.8.8.8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "139059851540038710",
|
||||
"email": "nathan@n8n.io",
|
||||
"status": "junk",
|
||||
"source": "api",
|
||||
"sent": 0,
|
||||
"opens_count": 0,
|
||||
"clicks_count": 0,
|
||||
"open_rate": 0,
|
||||
"click_rate": 0,
|
||||
"ip_address": null,
|
||||
"subscribed_at": null,
|
||||
"unsubscribed_at": null,
|
||||
"created_at": "2024-11-26 10:43:28",
|
||||
"updated_at": "2024-11-27 10:09:34",
|
||||
"fields": {
|
||||
"name": "Nathan",
|
||||
"last_name": "Workflow",
|
||||
"company": null,
|
||||
"country": null,
|
||||
"city": null,
|
||||
"phone": null,
|
||||
"state": null,
|
||||
"z_i_p": null
|
||||
},
|
||||
"opted_in_at": null,
|
||||
"optin_ip": null
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking ‘Test workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Create Subscriber",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Get Subscriber",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Update Subscriber",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Get Many Subscrbers",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Create Subscriber": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get Subscriber": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Update Subscriber": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get Many Subscrbers": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing3",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "338331ef-1b38-47dc-9e2b-45340ea3fe3b",
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "8c8c5237b8e37b006a7adce87f4369350c58e41f3ca9de16196d3197f69eabcd"
|
||||
},
|
||||
"id": "0Ov6Vd62DUXrWWQH",
|
||||
"tags": []
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
import nock from 'nock';
|
||||
|
||||
import { getWorkflowFilenames, testWorkflows } from '../../../../test/nodes/Helpers';
|
||||
import {
|
||||
getCreateResponseV2,
|
||||
getSubscriberResponseV2,
|
||||
getAllSubscribersResponseV2,
|
||||
getUpdateSubscriberResponseV2,
|
||||
} from '../apiResponses';
|
||||
|
||||
describe('MailerLite', () => {
|
||||
describe('Run v2 workflow', () => {
|
||||
beforeAll(() => {
|
||||
nock.disableNetConnect();
|
||||
|
||||
const mock = nock('https://connect.mailerlite.com/api');
|
||||
|
||||
mock.post('/subscribers').reply(200, getCreateResponseV2);
|
||||
mock.get('/subscribers/user@n8n.io').reply(200, getSubscriberResponseV2);
|
||||
mock
|
||||
.get('/subscribers')
|
||||
.query({ 'filter[status]': 'junk', limit: 2 })
|
||||
.reply(200, getAllSubscribersResponseV2);
|
||||
mock.put('/subscribers/user@n8n.io').reply(200, getUpdateSubscriberResponseV2);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
nock.restore();
|
||||
});
|
||||
|
||||
const workflows = getWorkflowFilenames(__dirname);
|
||||
testWorkflows(workflows);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,184 @@
|
|||
import {
|
||||
type IHookFunctions,
|
||||
type IWebhookFunctions,
|
||||
type IDataObject,
|
||||
type INodeType,
|
||||
type INodeTypeDescription,
|
||||
type IWebhookResponseData,
|
||||
type INodeTypeBaseDescription,
|
||||
NodeConnectionType,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { mailerliteApiRequest } from '../GenericFunctions';
|
||||
|
||||
export class MailerLiteTriggerV1 implements INodeType {
|
||||
description: INodeTypeDescription;
|
||||
|
||||
constructor(baseDescription: INodeTypeBaseDescription) {
|
||||
this.description = {
|
||||
...baseDescription,
|
||||
displayName: 'MailerLite Trigger',
|
||||
name: 'mailerLiteTrigger',
|
||||
group: ['trigger'],
|
||||
version: 1,
|
||||
description: 'Starts the workflow when MailerLite events occur',
|
||||
defaults: {
|
||||
name: 'MailerLite Trigger',
|
||||
},
|
||||
inputs: [],
|
||||
outputs: [NodeConnectionType.Main],
|
||||
credentials: [
|
||||
{
|
||||
name: 'mailerLiteApi',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
webhooks: [
|
||||
{
|
||||
name: 'default',
|
||||
httpMethod: 'POST',
|
||||
responseMode: 'onReceived',
|
||||
path: 'webhook',
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Event',
|
||||
name: 'event',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Campaign Sent',
|
||||
value: 'campaign.sent',
|
||||
description: 'Fired when campaign is sent',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Added Through Webform',
|
||||
value: 'subscriber.added_through_webform',
|
||||
description: 'Fired when a subscriber is added though a form',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Added to Group',
|
||||
value: 'subscriber.add_to_group',
|
||||
description: 'Fired when a subscriber is added to a group',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Automation Completed',
|
||||
value: 'subscriber.automation_complete',
|
||||
description: 'Fired when subscriber finishes automation',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Automation Triggered',
|
||||
value: 'subscriber.automation_triggered',
|
||||
description: 'Fired when subscriber starts automation',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Bounced',
|
||||
value: 'subscriber.bounced',
|
||||
description: 'Fired when an email address bounces',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Complained',
|
||||
value: 'subscriber.complaint',
|
||||
description: 'Fired when subscriber marks a campaign as a spam',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Created',
|
||||
value: 'subscriber.create',
|
||||
description: 'Fired when a new subscriber is added to an account',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Removed From Group',
|
||||
value: 'subscriber.remove_from_group',
|
||||
description: 'Fired when a subscriber is removed from a group',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Unsubscribe',
|
||||
value: 'subscriber.unsubscribe',
|
||||
description: 'Fired when a subscriber becomes unsubscribed',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Updated',
|
||||
value: 'subscriber.update',
|
||||
description: "Fired when any of the subscriber's custom fields are updated",
|
||||
},
|
||||
],
|
||||
required: true,
|
||||
default: [],
|
||||
description: 'The events to listen to',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
webhookMethods = {
|
||||
default: {
|
||||
async checkExists(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookUrl = this.getNodeWebhookUrl('default');
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
const event = this.getNodeParameter('event') as string;
|
||||
// Check all the webhooks which exist already if it is identical to the
|
||||
// one that is supposed to get created.
|
||||
const endpoint = '/webhooks';
|
||||
const { webhooks } = await mailerliteApiRequest.call(this, 'GET', endpoint, {});
|
||||
for (const webhook of webhooks) {
|
||||
if (webhook.url === webhookUrl && webhook.event === event) {
|
||||
// Set webhook-id to be sure that it can be deleted
|
||||
webhookData.webhookId = webhook.id as string;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
async create(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
const webhookUrl = this.getNodeWebhookUrl('default');
|
||||
const event = this.getNodeParameter('event') as string;
|
||||
|
||||
const endpoint = '/webhooks';
|
||||
|
||||
const body = {
|
||||
url: webhookUrl,
|
||||
event,
|
||||
};
|
||||
|
||||
const responseData = await mailerliteApiRequest.call(this, 'POST', endpoint, body);
|
||||
|
||||
if (responseData.id === undefined) {
|
||||
// Required data is missing so was not successful
|
||||
return false;
|
||||
}
|
||||
|
||||
webhookData.webhookId = responseData.id as string;
|
||||
return true;
|
||||
},
|
||||
async delete(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
if (webhookData.webhookId !== undefined) {
|
||||
const endpoint = `/webhooks/${webhookData.webhookId}`;
|
||||
|
||||
try {
|
||||
await mailerliteApiRequest.call(this, 'DELETE', endpoint);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove from the static workflow data so that it is clear
|
||||
// that no webhooks are registered anymore
|
||||
delete webhookData.webhookId;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
|
||||
const body = this.getBodyData();
|
||||
|
||||
const events = body.events as IDataObject[];
|
||||
|
||||
return {
|
||||
workflowData: [this.helpers.returnJsonArray(events)],
|
||||
};
|
||||
}
|
||||
}
|
199
packages/nodes-base/nodes/MailerLite/v1/MailerLiteV1.node.ts
Normal file
199
packages/nodes-base/nodes/MailerLite/v1/MailerLiteV1.node.ts
Normal file
|
@ -0,0 +1,199 @@
|
|||
import type {
|
||||
IExecuteFunctions,
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
INodeTypeBaseDescription,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeConnectionType } from 'n8n-workflow';
|
||||
|
||||
import { subscriberFields, subscriberOperations } from './SubscriberDescription';
|
||||
import {
|
||||
getCustomFields,
|
||||
mailerliteApiRequest,
|
||||
mailerliteApiRequestAllItems,
|
||||
} from '../GenericFunctions';
|
||||
|
||||
export class MailerLiteV1 implements INodeType {
|
||||
description: INodeTypeDescription;
|
||||
|
||||
constructor(baseDescription: INodeTypeBaseDescription) {
|
||||
this.description = {
|
||||
...baseDescription,
|
||||
displayName: 'MailerLite',
|
||||
name: 'mailerLite',
|
||||
group: ['input'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Consume Mailer Lite API',
|
||||
defaults: {
|
||||
name: 'MailerLite',
|
||||
},
|
||||
inputs: [NodeConnectionType.Main],
|
||||
outputs: [NodeConnectionType.Main],
|
||||
credentials: [
|
||||
{
|
||||
name: 'mailerLiteApi',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'Subscriber',
|
||||
value: 'subscriber',
|
||||
},
|
||||
],
|
||||
default: 'subscriber',
|
||||
},
|
||||
...subscriberOperations,
|
||||
...subscriberFields,
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
methods = {
|
||||
loadOptions: {
|
||||
getCustomFields,
|
||||
},
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const items = this.getInputData();
|
||||
const returnData: INodeExecutionData[] = [];
|
||||
const length = items.length;
|
||||
const qs: IDataObject = {};
|
||||
let responseData;
|
||||
const resource = this.getNodeParameter('resource', 0);
|
||||
const operation = this.getNodeParameter('operation', 0);
|
||||
for (let i = 0; i < length; i++) {
|
||||
try {
|
||||
if (resource === 'subscriber') {
|
||||
//https://developers.mailerlite.com/reference#create-a-subscriber
|
||||
if (operation === 'create') {
|
||||
const email = this.getNodeParameter('email', i) as string;
|
||||
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i);
|
||||
|
||||
const body: IDataObject = {
|
||||
email,
|
||||
fields: [],
|
||||
};
|
||||
|
||||
Object.assign(body, additionalFields);
|
||||
|
||||
if (additionalFields.customFieldsUi) {
|
||||
const customFieldsValues = (additionalFields.customFieldsUi as IDataObject)
|
||||
.customFieldsValues as IDataObject[];
|
||||
|
||||
if (customFieldsValues) {
|
||||
const fields = {};
|
||||
|
||||
for (const customFieldValue of customFieldsValues) {
|
||||
//@ts-ignore
|
||||
fields[customFieldValue.fieldId] = customFieldValue.value;
|
||||
}
|
||||
|
||||
body.fields = fields;
|
||||
delete body.customFieldsUi;
|
||||
}
|
||||
}
|
||||
|
||||
responseData = await mailerliteApiRequest.call(this, 'POST', '/subscribers', body);
|
||||
}
|
||||
//https://developers.mailerlite.com/reference#single-subscriber
|
||||
if (operation === 'get') {
|
||||
const subscriberId = this.getNodeParameter('subscriberId', i) as string;
|
||||
|
||||
responseData = await mailerliteApiRequest.call(
|
||||
this,
|
||||
'GET',
|
||||
`/subscribers/${subscriberId}`,
|
||||
);
|
||||
}
|
||||
//https://developers.mailerlite.com/reference#subscribers
|
||||
if (operation === 'getAll') {
|
||||
const returnAll = this.getNodeParameter('returnAll', i);
|
||||
|
||||
const filters = this.getNodeParameter('filters', i);
|
||||
|
||||
Object.assign(qs, filters);
|
||||
|
||||
if (returnAll) {
|
||||
responseData = await mailerliteApiRequestAllItems.call(
|
||||
this,
|
||||
'GET',
|
||||
'/subscribers',
|
||||
{},
|
||||
qs,
|
||||
);
|
||||
} else {
|
||||
qs.limit = this.getNodeParameter('limit', i);
|
||||
|
||||
responseData = await mailerliteApiRequest.call(this, 'GET', '/subscribers', {}, qs);
|
||||
}
|
||||
}
|
||||
//https://developers.mailerlite.com/reference#update-subscriber
|
||||
if (operation === 'update') {
|
||||
const subscriberId = this.getNodeParameter('subscriberId', i) as string;
|
||||
|
||||
const updateFields = this.getNodeParameter('updateFields', i);
|
||||
|
||||
const body: IDataObject = {};
|
||||
|
||||
Object.assign(body, updateFields);
|
||||
|
||||
if (updateFields.customFieldsUi) {
|
||||
const customFieldsValues = (updateFields.customFieldsUi as IDataObject)
|
||||
.customFieldsValues as IDataObject[];
|
||||
|
||||
if (customFieldsValues) {
|
||||
const fields = {};
|
||||
|
||||
for (const customFieldValue of customFieldsValues) {
|
||||
//@ts-ignore
|
||||
fields[customFieldValue.fieldId] = customFieldValue.value;
|
||||
}
|
||||
|
||||
body.fields = fields;
|
||||
delete body.customFieldsUi;
|
||||
}
|
||||
}
|
||||
|
||||
responseData = await mailerliteApiRequest.call(
|
||||
this,
|
||||
'PUT',
|
||||
`/subscribers/${subscriberId}`,
|
||||
body,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
const executionErrorData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray({ error: error.message }),
|
||||
{ itemData: { item: i } },
|
||||
);
|
||||
returnData.push(...executionErrorData);
|
||||
continue;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject[]),
|
||||
{ itemData: { item: i } },
|
||||
);
|
||||
|
||||
returnData.push(...executionData);
|
||||
}
|
||||
|
||||
return [returnData];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
export interface CustomField {
|
||||
name: string;
|
||||
key: string;
|
||||
}
|
||||
|
||||
export interface SubscriberFields {
|
||||
city: string | null;
|
||||
company: string | null;
|
||||
country: string | null;
|
||||
last_name: string | null;
|
||||
name: string | null;
|
||||
phone: string | null;
|
||||
state: string | null;
|
||||
z_i_p: string | null;
|
||||
}
|
||||
|
||||
export interface Subscriber {
|
||||
id: string;
|
||||
email: string;
|
||||
status: string;
|
||||
source: string;
|
||||
sent: number;
|
||||
opens_count: number;
|
||||
clicks_count: number;
|
||||
open_rate: number;
|
||||
click_rate: number;
|
||||
ip_address: string | null;
|
||||
subscribed_at: string;
|
||||
unsubscribed_at: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
fields: SubscriberFields;
|
||||
opted_in_at: string | null;
|
||||
optin_ip: string | null;
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
import {
|
||||
type IHookFunctions,
|
||||
type IWebhookFunctions,
|
||||
type IDataObject,
|
||||
type INodeType,
|
||||
type INodeTypeDescription,
|
||||
type IWebhookResponseData,
|
||||
type INodeTypeBaseDescription,
|
||||
NodeConnectionType,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { mailerliteApiRequest } from '../GenericFunctions';
|
||||
|
||||
export class MailerLiteTriggerV2 implements INodeType {
|
||||
description: INodeTypeDescription;
|
||||
|
||||
constructor(baseDescription: INodeTypeBaseDescription) {
|
||||
this.description = {
|
||||
...baseDescription,
|
||||
displayName: 'MailerLite Trigger',
|
||||
name: 'mailerLiteTrigger',
|
||||
group: ['trigger'],
|
||||
version: [2],
|
||||
description: 'Starts the workflow when MailerLite events occur',
|
||||
defaults: {
|
||||
name: 'MailerLite Trigger',
|
||||
},
|
||||
inputs: [],
|
||||
outputs: [NodeConnectionType.Main],
|
||||
credentials: [
|
||||
{
|
||||
name: 'mailerLiteApi',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
webhooks: [
|
||||
{
|
||||
name: 'default',
|
||||
httpMethod: 'POST',
|
||||
responseMode: 'onReceived',
|
||||
path: 'webhook',
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Events',
|
||||
name: 'events',
|
||||
type: 'multiOptions',
|
||||
options: [
|
||||
{
|
||||
name: 'Campaign Sent',
|
||||
value: 'campaign.sent',
|
||||
description: 'Fired when campaign is sent',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Added to Group',
|
||||
value: 'subscriber.added_to_group',
|
||||
description: 'Fired when a subscriber is added to a group',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Automation Completed',
|
||||
value: 'subscriber.automation_completed',
|
||||
description: 'Fired when subscriber finishes automation',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Automation Triggered',
|
||||
value: 'subscriber.automation_triggered',
|
||||
description: 'Fired when subscriber starts automation',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Bounced',
|
||||
value: 'subscriber.bounced',
|
||||
description: 'Fired when an email address bounces',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Created',
|
||||
value: 'subscriber.created',
|
||||
description: 'Fired when a new subscriber is added to an account',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Removed From Group',
|
||||
value: 'subscriber.removed_from_group',
|
||||
description: 'Fired when a subscriber is removed from a group',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Spam Reported',
|
||||
value: 'subscriber.spam_reported',
|
||||
description: 'Fired when subscriber marks a campaign as a spam',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Unsubscribe',
|
||||
value: 'subscriber.unsubscribed',
|
||||
description: 'Fired when a subscriber becomes unsubscribed',
|
||||
},
|
||||
{
|
||||
name: 'Subscriber Updated',
|
||||
value: 'subscriber.updated',
|
||||
description: "Fired when any of the subscriber's custom fields are updated",
|
||||
},
|
||||
],
|
||||
required: true,
|
||||
default: [],
|
||||
description: 'The events to listen to',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
webhookMethods = {
|
||||
default: {
|
||||
async checkExists(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookUrl = this.getNodeWebhookUrl('default');
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
const events = this.getNodeParameter('events') as string[];
|
||||
// Check all the webhooks which exist already if it is identical to the
|
||||
// one that is supposed to get created.
|
||||
const endpoint = '/webhooks';
|
||||
const { data } = await mailerliteApiRequest.call(this, 'GET', endpoint, {});
|
||||
for (const webhook of data) {
|
||||
if (webhook.url === webhookUrl && webhook.events === events) {
|
||||
// Set webhook-id to be sure that it can be deleted
|
||||
webhookData.webhookId = webhook.id as string;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
async create(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
const webhookUrl = this.getNodeWebhookUrl('default');
|
||||
const events = this.getNodeParameter('events') as string[];
|
||||
|
||||
const endpoint = '/webhooks';
|
||||
|
||||
const body = {
|
||||
url: webhookUrl,
|
||||
events,
|
||||
};
|
||||
|
||||
const { data } = await mailerliteApiRequest.call(this, 'POST', endpoint, body);
|
||||
|
||||
if (data.id === undefined) {
|
||||
// Required data is missing so was not successful
|
||||
return false;
|
||||
}
|
||||
|
||||
webhookData.webhookId = data.id as string;
|
||||
return true;
|
||||
},
|
||||
async delete(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
if (webhookData.webhookId !== undefined) {
|
||||
const endpoint = `/webhooks/${webhookData.webhookId}`;
|
||||
|
||||
try {
|
||||
await mailerliteApiRequest.call(this, 'DELETE', endpoint);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove from the static workflow data so that it is clear
|
||||
// that no webhooks are registered anymore
|
||||
delete webhookData.webhookId;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
|
||||
const body = this.getBodyData();
|
||||
|
||||
const data = body.fields as IDataObject[];
|
||||
|
||||
return {
|
||||
workflowData: [this.helpers.returnJsonArray(data)],
|
||||
};
|
||||
}
|
||||
}
|
206
packages/nodes-base/nodes/MailerLite/v2/MailerLiteV2.node.ts
Normal file
206
packages/nodes-base/nodes/MailerLite/v2/MailerLiteV2.node.ts
Normal file
|
@ -0,0 +1,206 @@
|
|||
import type {
|
||||
IExecuteFunctions,
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
INodeTypeBaseDescription,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeConnectionType } from 'n8n-workflow';
|
||||
|
||||
import type { Subscriber } from './MailerLite.Interface';
|
||||
import { subscriberFields, subscriberOperations } from './SubscriberDescription';
|
||||
import {
|
||||
getCustomFields,
|
||||
mailerliteApiRequest,
|
||||
mailerliteApiRequestAllItems,
|
||||
} from '../GenericFunctions';
|
||||
|
||||
export class MailerLiteV2 implements INodeType {
|
||||
description: INodeTypeDescription;
|
||||
|
||||
constructor(baseDescription: INodeTypeBaseDescription) {
|
||||
this.description = {
|
||||
...baseDescription,
|
||||
displayName: 'MailerLite',
|
||||
name: 'mailerLite',
|
||||
group: ['input'],
|
||||
version: [2],
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Consume Mailer Lite API',
|
||||
defaults: {
|
||||
name: 'MailerLite',
|
||||
},
|
||||
inputs: [NodeConnectionType.Main],
|
||||
outputs: [NodeConnectionType.Main],
|
||||
credentials: [
|
||||
{
|
||||
name: 'mailerLiteApi',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'Subscriber',
|
||||
value: 'subscriber',
|
||||
},
|
||||
],
|
||||
default: 'subscriber',
|
||||
},
|
||||
...subscriberOperations,
|
||||
...subscriberFields,
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
methods = {
|
||||
loadOptions: {
|
||||
getCustomFields,
|
||||
},
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const items = this.getInputData();
|
||||
const returnData: INodeExecutionData[] = [];
|
||||
const length = items.length;
|
||||
const qs: IDataObject = {};
|
||||
let responseData;
|
||||
const resource = this.getNodeParameter('resource', 0);
|
||||
const operation = this.getNodeParameter('operation', 0);
|
||||
for (let i = 0; i < length; i++) {
|
||||
try {
|
||||
if (resource === 'subscriber') {
|
||||
//https://developers.mailerlite.com/reference#create-a-subscriber
|
||||
if (operation === 'create') {
|
||||
const email = this.getNodeParameter('email', i) as string;
|
||||
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i);
|
||||
|
||||
const body: IDataObject = {
|
||||
email,
|
||||
fields: [],
|
||||
};
|
||||
|
||||
Object.assign(body, additionalFields);
|
||||
|
||||
if (additionalFields.customFieldsUi) {
|
||||
const customFieldsValues = (additionalFields.customFieldsUi as IDataObject)
|
||||
.customFieldsValues as IDataObject[];
|
||||
|
||||
if (customFieldsValues) {
|
||||
const fields = {};
|
||||
|
||||
for (const customFieldValue of customFieldsValues) {
|
||||
//@ts-ignore
|
||||
fields[customFieldValue.fieldId] = customFieldValue.value;
|
||||
}
|
||||
|
||||
body.fields = fields;
|
||||
delete body.customFieldsUi;
|
||||
}
|
||||
}
|
||||
|
||||
responseData = await mailerliteApiRequest.call(this, 'POST', '/subscribers', body);
|
||||
responseData = responseData.data;
|
||||
}
|
||||
//https://developers.mailerlite.com/reference#single-subscriber
|
||||
if (operation === 'get') {
|
||||
const subscriberId = this.getNodeParameter('subscriberId', i) as string;
|
||||
|
||||
responseData = await mailerliteApiRequest.call(
|
||||
this,
|
||||
'GET',
|
||||
`/subscribers/${subscriberId}`,
|
||||
);
|
||||
|
||||
responseData = responseData.data as Subscriber[];
|
||||
}
|
||||
//https://developers.mailerlite.com/reference#subscribers
|
||||
if (operation === 'getAll') {
|
||||
const returnAll = this.getNodeParameter('returnAll', i);
|
||||
|
||||
const filters = this.getNodeParameter('filters', i);
|
||||
|
||||
if (filters.status) {
|
||||
qs['filter[status]'] = filters.status as string;
|
||||
}
|
||||
|
||||
if (returnAll) {
|
||||
responseData = await mailerliteApiRequestAllItems.call(
|
||||
this,
|
||||
'GET',
|
||||
'/subscribers',
|
||||
{},
|
||||
qs,
|
||||
);
|
||||
} else {
|
||||
qs.limit = this.getNodeParameter('limit', i);
|
||||
|
||||
responseData = await mailerliteApiRequest.call(this, 'GET', '/subscribers', {}, qs);
|
||||
responseData = responseData.data;
|
||||
}
|
||||
}
|
||||
//https://developers.mailerlite.com/reference#update-subscriber
|
||||
if (operation === 'update') {
|
||||
const subscriberId = this.getNodeParameter('subscriberId', i) as string;
|
||||
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i);
|
||||
|
||||
const body: IDataObject = {};
|
||||
|
||||
Object.assign(body, additionalFields);
|
||||
|
||||
if (additionalFields.customFieldsUi) {
|
||||
const customFieldsValues = (additionalFields.customFieldsUi as IDataObject)
|
||||
.customFieldsValues as IDataObject[];
|
||||
|
||||
if (customFieldsValues) {
|
||||
const fields = {};
|
||||
|
||||
for (const customFieldValue of customFieldsValues) {
|
||||
//@ts-ignore
|
||||
fields[customFieldValue.fieldId] = customFieldValue.value;
|
||||
}
|
||||
|
||||
body.fields = fields;
|
||||
delete body.customFieldsUi;
|
||||
}
|
||||
}
|
||||
|
||||
responseData = await mailerliteApiRequest.call(
|
||||
this,
|
||||
'PUT',
|
||||
`/subscribers/${subscriberId}`,
|
||||
body,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
const executionErrorData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray({ error: error.message }),
|
||||
{ itemData: { item: i } },
|
||||
);
|
||||
returnData.push(...executionErrorData);
|
||||
continue;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject[]),
|
||||
{ itemData: { item: i } },
|
||||
);
|
||||
|
||||
returnData.push(...executionData);
|
||||
}
|
||||
|
||||
return [returnData];
|
||||
}
|
||||
}
|
304
packages/nodes-base/nodes/MailerLite/v2/SubscriberDescription.ts
Normal file
304
packages/nodes-base/nodes/MailerLite/v2/SubscriberDescription.ts
Normal file
|
@ -0,0 +1,304 @@
|
|||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const subscriberOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['subscriber'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Create a new subscriber',
|
||||
action: 'Create a subscriber',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get an subscriber',
|
||||
action: 'Get a subscriber',
|
||||
},
|
||||
{
|
||||
name: 'Get Many',
|
||||
value: 'getAll',
|
||||
description: 'Get many subscribers',
|
||||
action: 'Get many subscribers',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: 'Update an subscriber',
|
||||
action: 'Update a subscriber',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
},
|
||||
];
|
||||
|
||||
export const subscriberFields: INodeProperties[] = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* subscriber:create */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Email',
|
||||
name: 'email',
|
||||
type: 'string',
|
||||
placeholder: 'name@email.com',
|
||||
required: true,
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['subscriber'],
|
||||
operation: ['create'],
|
||||
},
|
||||
},
|
||||
description: 'Email of new subscriber',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* subscriber:update */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Subscriber Email',
|
||||
name: 'subscriberId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['subscriber'],
|
||||
operation: ['update'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'Email of subscriber',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['subscriber'],
|
||||
operation: ['update', 'create'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Custom Fields',
|
||||
name: 'customFieldsUi',
|
||||
placeholder: 'Add Custom Field',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
description: 'Filter by custom fields',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
name: 'customFieldsValues',
|
||||
displayName: 'Custom Field',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Field Name or ID',
|
||||
name: 'fieldId',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getCustomFields',
|
||||
},
|
||||
default: '',
|
||||
description:
|
||||
'The ID of the field to add custom field to. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The value to set on custom field',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Status',
|
||||
name: 'status',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Active',
|
||||
value: 'active',
|
||||
},
|
||||
{
|
||||
name: 'Bounced',
|
||||
value: 'bounced',
|
||||
},
|
||||
{
|
||||
name: 'Junk',
|
||||
value: 'junk',
|
||||
},
|
||||
{
|
||||
name: 'Unconfirmed',
|
||||
value: 'unconfirmed',
|
||||
},
|
||||
{
|
||||
name: 'Unsubscribed',
|
||||
value: 'unsubscribed',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Subscribed At',
|
||||
name: 'subscribed_at',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'IP Address',
|
||||
name: 'ip_address',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Opted In At',
|
||||
name: 'opted_in_at',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Opt In IP',
|
||||
name: 'optin_ip',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Unsubscribed At',
|
||||
name: 'unsubscribed_at',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* subscriber:delete */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Subscriber Email',
|
||||
name: 'subscriberId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['subscriber'],
|
||||
operation: ['delete'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'Email of subscriber to delete',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* subscriber:get */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Subscriber Email',
|
||||
name: 'subscriberId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['subscriber'],
|
||||
operation: ['get'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'Email of subscriber to get',
|
||||
},
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* subscriber:getAll */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['subscriber'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['subscriber'],
|
||||
operation: ['getAll'],
|
||||
returnAll: [false],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 100,
|
||||
},
|
||||
default: 50,
|
||||
description: 'Max number of results to return',
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filters',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Filter',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['getAll'],
|
||||
resource: ['subscriber'],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Status',
|
||||
name: 'status',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Active',
|
||||
value: 'active',
|
||||
},
|
||||
{
|
||||
name: 'Bounced',
|
||||
value: 'bounced',
|
||||
},
|
||||
{
|
||||
name: 'Junk',
|
||||
value: 'junk',
|
||||
},
|
||||
{
|
||||
name: 'Unconfirmed',
|
||||
value: 'unconfirmed',
|
||||
},
|
||||
{
|
||||
name: 'Unsubscribed',
|
||||
value: 'unsubscribed',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
Loading…
Reference in a new issue