mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 04:04:06 -08:00
✨ Add Lemlist node (#1506)
* 🎉 Register node and credentials * ⚡ Add credentials file * 🎨 Add SVG icon * ⚡ Add generic functions file * ⚡ Add preliminary node stub * ⚡ Add resource description stubs * ⚡ Extract get CSV from getAll * ⚡ Implement lead:create * ⚡ Implement all lead operations * ⚡ Implement unsubscribe:create and delete * ⚡ Preload campaigns * 🔥 Remove logging * 🔥 Remove operation per feedback * 🎨 Prettify error message * ⚡ Implement unsubscribe:getAll * ⚡ Add trigger and small improvements * ⚡ Minor improvement and fixes Co-authored-by: ricardo <ricardoespinoza105@gmail.com> Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
This commit is contained in:
parent
c49fcdeed6
commit
7be61e2f23
18
packages/nodes-base/credentials/LemlistApi.credentials.ts
Normal file
18
packages/nodes-base/credentials/LemlistApi.credentials.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import {
|
||||
ICredentialType,
|
||||
NodePropertyTypes,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export class LemlistApi implements ICredentialType {
|
||||
name = 'lemlistApi';
|
||||
displayName = 'Lemlist API';
|
||||
documentationUrl = 'lemlist';
|
||||
properties = [
|
||||
{
|
||||
displayName: 'API Key',
|
||||
name: 'apiKey',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
},
|
||||
];
|
||||
}
|
93
packages/nodes-base/nodes/Lemlist/GenericFunctions.ts
Normal file
93
packages/nodes-base/nodes/Lemlist/GenericFunctions.ts
Normal file
|
@ -0,0 +1,93 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
IHookFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
ILoadOptionsFunctions,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
OptionsWithUri,
|
||||
} from 'request';
|
||||
|
||||
/**
|
||||
* Make an authenticated API request to Lemlist.
|
||||
*/
|
||||
export async function lemlistApiRequest(
|
||||
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions,
|
||||
method: string,
|
||||
endpoint: string,
|
||||
body: IDataObject = {},
|
||||
qs: IDataObject = {},
|
||||
option: IDataObject = {},
|
||||
) {
|
||||
|
||||
const { apiKey } = this.getCredentials('lemlistApi') as {
|
||||
apiKey: string,
|
||||
};
|
||||
|
||||
const encodedApiKey = Buffer.from(':' + apiKey).toString('base64');
|
||||
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'user-agent': 'n8n',
|
||||
'Authorization': `Basic ${encodedApiKey}`,
|
||||
},
|
||||
method,
|
||||
uri: `https://api.lemlist.com/api${endpoint}`,
|
||||
qs,
|
||||
body,
|
||||
json: true,
|
||||
};
|
||||
|
||||
if (!Object.keys(body).length) {
|
||||
delete options.body;
|
||||
}
|
||||
|
||||
if (!Object.keys(qs).length) {
|
||||
delete options.qs;
|
||||
}
|
||||
|
||||
if (Object.keys(option)) {
|
||||
Object.assign(options, option);
|
||||
}
|
||||
|
||||
try {
|
||||
return await this.helpers.request!(options);
|
||||
} catch (error) {
|
||||
|
||||
if (error?.response?.body) {
|
||||
throw new Error(`Lemlist error response [${error.statusCode}]: ${error?.response?.body}`);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an authenticated API request to Lemlist and return all results.
|
||||
*/
|
||||
export async function lemlistApiRequestAllItems(
|
||||
this: IExecuteFunctions | ILoadOptionsFunctions | IHookFunctions,
|
||||
method: string,
|
||||
endpoint: string,
|
||||
) {
|
||||
const returnData: IDataObject[] = [];
|
||||
|
||||
let responseData;
|
||||
const qs: IDataObject = {};
|
||||
|
||||
qs.limit = 100;
|
||||
qs.offset = 0;
|
||||
|
||||
do {
|
||||
responseData = await lemlistApiRequest.call(this, method, endpoint, {}, qs);
|
||||
returnData.push(...responseData);
|
||||
qs.offset += qs.limit;
|
||||
} while (
|
||||
responseData.length !== 0
|
||||
);
|
||||
return returnData;
|
||||
}
|
337
packages/nodes-base/nodes/Lemlist/Lemlist.node.ts
Normal file
337
packages/nodes-base/nodes/Lemlist/Lemlist.node.ts
Normal file
|
@ -0,0 +1,337 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
ILoadOptionsFunctions,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
activityFields,
|
||||
activityOperations,
|
||||
campaignFields,
|
||||
campaignOperations,
|
||||
leadFields,
|
||||
leadOperations,
|
||||
teamFields,
|
||||
teamOperations,
|
||||
unsubscribeFields,
|
||||
unsubscribeOperations,
|
||||
} from './descriptions';
|
||||
|
||||
import {
|
||||
lemlistApiRequest,
|
||||
lemlistApiRequestAllItems,
|
||||
} from './GenericFunctions';
|
||||
|
||||
import {
|
||||
isEmpty,
|
||||
omit,
|
||||
} from 'lodash';
|
||||
|
||||
export class Lemlist implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Lemlist',
|
||||
name: 'lemlist',
|
||||
icon: 'file:lemlist.svg',
|
||||
group: ['transform'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Consume the Lemlist API',
|
||||
defaults: {
|
||||
name: 'Lemlist',
|
||||
color: '#4d19ff',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
credentials: [
|
||||
{
|
||||
name: 'lemlistApi',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Activity',
|
||||
value: 'activity',
|
||||
},
|
||||
{
|
||||
name: 'Campaign',
|
||||
value: 'campaign',
|
||||
},
|
||||
{
|
||||
name: 'Lead',
|
||||
value: 'lead',
|
||||
},
|
||||
{
|
||||
name: 'Team',
|
||||
value: 'team',
|
||||
},
|
||||
{
|
||||
name: 'Unsubscribes',
|
||||
value: 'unsubscribe',
|
||||
},
|
||||
],
|
||||
default: 'activity',
|
||||
description: 'Resource to consume',
|
||||
},
|
||||
...activityOperations,
|
||||
...activityFields,
|
||||
...campaignOperations,
|
||||
...campaignFields,
|
||||
...leadOperations,
|
||||
...leadFields,
|
||||
...teamOperations,
|
||||
...teamFields,
|
||||
...unsubscribeOperations,
|
||||
...unsubscribeFields,
|
||||
],
|
||||
};
|
||||
|
||||
methods = {
|
||||
loadOptions: {
|
||||
async getCampaigns(this: ILoadOptionsFunctions) {
|
||||
const campaigns = await lemlistApiRequest.call(this, 'GET', '/campaigns');
|
||||
return campaigns.map(({ _id, name }: { _id: string, name: string }) => ({
|
||||
name,
|
||||
value: _id,
|
||||
}));
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions) {
|
||||
const items = this.getInputData();
|
||||
|
||||
const resource = this.getNodeParameter('resource', 0) as string;
|
||||
const operation = this.getNodeParameter('operation', 0) as string;
|
||||
|
||||
let responseData;
|
||||
const returnData: IDataObject[] = [];
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
|
||||
try {
|
||||
|
||||
if (resource === 'activity') {
|
||||
|
||||
// *********************************************************************
|
||||
// activity
|
||||
// *********************************************************************
|
||||
|
||||
if (operation === 'getAll') {
|
||||
|
||||
// ----------------------------------
|
||||
// activity: getAll
|
||||
// ----------------------------------
|
||||
|
||||
// https://developer.lemlist.com/#activities
|
||||
|
||||
const returnAll = this.getNodeParameter('returnAll', 0) as boolean;
|
||||
|
||||
const qs = {} as IDataObject;
|
||||
const filters = this.getNodeParameter('filters', i);
|
||||
|
||||
if (!isEmpty(filters)) {
|
||||
Object.assign(qs, filters);
|
||||
}
|
||||
|
||||
responseData = await lemlistApiRequest.call(this, 'GET', '/activities', {}, qs);
|
||||
|
||||
if (returnAll === false) {
|
||||
const limit = this.getNodeParameter('limit', 0) as number;
|
||||
responseData = responseData.slice(0, limit);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else if (resource === 'campaign') {
|
||||
|
||||
// *********************************************************************
|
||||
// campaign
|
||||
// *********************************************************************
|
||||
|
||||
if (operation === 'getAll') {
|
||||
|
||||
// ----------------------------------
|
||||
// campaign: getAll
|
||||
// ----------------------------------
|
||||
|
||||
// https://developer.lemlist.com/#list-all-campaigns
|
||||
|
||||
responseData = await lemlistApiRequest.call(this, 'GET', '/campaigns');
|
||||
|
||||
const returnAll = this.getNodeParameter('returnAll', i);
|
||||
|
||||
if (!returnAll) {
|
||||
const limit = this.getNodeParameter('limit', i);
|
||||
responseData = responseData.slice(0, limit);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (resource === 'lead') {
|
||||
|
||||
// *********************************************************************
|
||||
// lead
|
||||
// *********************************************************************
|
||||
|
||||
if (operation === 'create') {
|
||||
|
||||
// ----------------------------------
|
||||
// lead: create
|
||||
// ----------------------------------
|
||||
|
||||
// https://developer.lemlist.com/#add-a-lead-in-a-campaign
|
||||
|
||||
const qs = {} as IDataObject;
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||
|
||||
if (additionalFields.deduplicate !== undefined) {
|
||||
qs.deduplicate = additionalFields.deduplicate;
|
||||
}
|
||||
|
||||
const body = {} as IDataObject;
|
||||
|
||||
const remainingAdditionalFields = omit(additionalFields, 'deduplicate');
|
||||
|
||||
if (!isEmpty(remainingAdditionalFields)) {
|
||||
Object.assign(body, remainingAdditionalFields);
|
||||
}
|
||||
|
||||
const campaignId = this.getNodeParameter('campaignId', i);
|
||||
const email = this.getNodeParameter('email', i);
|
||||
const endpoint = `/campaigns/${campaignId}/leads/${email}`;
|
||||
|
||||
responseData = await lemlistApiRequest.call(this, 'POST', endpoint, body, qs);
|
||||
|
||||
} else if (operation === 'delete') {
|
||||
|
||||
// ----------------------------------
|
||||
// lead: delete
|
||||
// ----------------------------------
|
||||
|
||||
// https://developer.lemlist.com/#delete-a-lead-from-a-campaign
|
||||
|
||||
const campaignId = this.getNodeParameter('campaignId', i);
|
||||
const email = this.getNodeParameter('email', i);
|
||||
const endpoint = `/campaigns/${campaignId}/leads/${email}`;
|
||||
responseData = await lemlistApiRequest.call(this, 'DELETE', endpoint, {}, { action: 'remove' });
|
||||
|
||||
} else if (operation === 'get') {
|
||||
|
||||
// ----------------------------------
|
||||
// lead: get
|
||||
// ----------------------------------
|
||||
|
||||
// https://developer.lemlist.com/#get-a-specific-lead-by-email
|
||||
|
||||
const email = this.getNodeParameter('email', i);
|
||||
responseData = await lemlistApiRequest.call(this, 'GET', `/leads/${email}`);
|
||||
|
||||
} else if (operation === 'unsubscribe') {
|
||||
|
||||
// ----------------------------------
|
||||
// lead: unsubscribe
|
||||
// ----------------------------------
|
||||
|
||||
// https://developer.lemlist.com/#unsubscribe-a-lead-from-a-campaign
|
||||
|
||||
const campaignId = this.getNodeParameter('campaignId', i);
|
||||
const email = this.getNodeParameter('email', i);
|
||||
const endpoint = `/campaigns/${campaignId}/leads/${email}`;
|
||||
responseData = await lemlistApiRequest.call(this, 'DELETE', endpoint);
|
||||
|
||||
}
|
||||
|
||||
} else if (resource === 'team') {
|
||||
|
||||
// *********************************************************************
|
||||
// team
|
||||
// *********************************************************************
|
||||
|
||||
if (operation === 'get') {
|
||||
|
||||
// ----------------------------------
|
||||
// team: get
|
||||
// ----------------------------------
|
||||
|
||||
// https://developer.lemlist.com/#team
|
||||
|
||||
responseData = await lemlistApiRequest.call(this, 'GET', '/team');
|
||||
|
||||
}
|
||||
|
||||
} else if (resource === 'unsubscribe') {
|
||||
|
||||
// *********************************************************************
|
||||
// unsubscribe
|
||||
// *********************************************************************
|
||||
|
||||
if (operation === 'add') {
|
||||
|
||||
// ----------------------------------
|
||||
// unsubscribe: Add
|
||||
// ----------------------------------
|
||||
|
||||
// https://developer.lemlist.com/#add-an-email-address-in-the-unsubscribes
|
||||
|
||||
const email = this.getNodeParameter('email', i);
|
||||
responseData = await lemlistApiRequest.call(this, 'POST', `/unsubscribes/${email}`);
|
||||
|
||||
} else if (operation === 'delete') {
|
||||
|
||||
// ----------------------------------
|
||||
// unsubscribe: delete
|
||||
// ----------------------------------
|
||||
|
||||
// https://developer.lemlist.com/#delete-an-email-address-from-the-unsubscribes
|
||||
|
||||
const email = this.getNodeParameter('email', i);
|
||||
responseData = await lemlistApiRequest.call(this, 'DELETE', `/unsubscribes/${email}`);
|
||||
|
||||
} else if (operation === 'getAll') {
|
||||
|
||||
// ----------------------------------
|
||||
// unsubscribe: getAll
|
||||
// ----------------------------------
|
||||
|
||||
// https://developer.lemlist.com/#list-all-unsubscribes
|
||||
|
||||
const returnAll = this.getNodeParameter('returnAll', i);
|
||||
|
||||
if (returnAll) {
|
||||
responseData = await lemlistApiRequestAllItems.call(this, 'GET', '/unsubscribes');
|
||||
} else {
|
||||
const qs = {
|
||||
limit: this.getNodeParameter('limit', i) as number,
|
||||
};
|
||||
responseData = await lemlistApiRequest.call(this, 'GET', '/unsubscribes', {}, qs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({ error: error.toString() });
|
||||
continue;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
Array.isArray(responseData)
|
||||
? returnData.push(...responseData)
|
||||
: returnData.push(responseData);
|
||||
}
|
||||
|
||||
return [this.helpers.returnJsonArray(returnData)];
|
||||
}
|
||||
}
|
175
packages/nodes-base/nodes/Lemlist/LemlistTrigger.node.ts
Normal file
175
packages/nodes-base/nodes/Lemlist/LemlistTrigger.node.ts
Normal file
|
@ -0,0 +1,175 @@
|
|||
import {
|
||||
IHookFunctions,
|
||||
IWebhookFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
ILoadOptionsFunctions,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
IWebhookResponseData,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
lemlistApiRequest,
|
||||
} from './GenericFunctions';
|
||||
|
||||
export class LemlistTrigger implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Lemlist Trigger',
|
||||
name: 'lemlistTrigger',
|
||||
icon: 'file:lemlist.svg',
|
||||
group: ['trigger'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["event"]}}',
|
||||
description: 'Handle Lemlist events via webhooks',
|
||||
defaults: {
|
||||
name: 'Lemlist Trigger',
|
||||
color: '#4d19ff',
|
||||
},
|
||||
inputs: [],
|
||||
outputs: ['main'],
|
||||
credentials: [
|
||||
{
|
||||
name: 'lemlistApi',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
webhooks: [
|
||||
{
|
||||
name: 'default',
|
||||
httpMethod: 'POST',
|
||||
responseMode: 'onReceived',
|
||||
path: 'webhook',
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Event',
|
||||
name: 'event',
|
||||
type: 'options',
|
||||
required: true,
|
||||
default: '',
|
||||
options: [
|
||||
{
|
||||
name: 'Email Bounced',
|
||||
value: 'emailsBounced',
|
||||
},
|
||||
{
|
||||
name: 'Email Clicked',
|
||||
value: 'emailsClicked',
|
||||
},
|
||||
{
|
||||
name: 'Email Opened',
|
||||
value: 'emailsOpened',
|
||||
},
|
||||
{
|
||||
name: 'Email Replied',
|
||||
value: 'emailsReplied',
|
||||
},
|
||||
{
|
||||
name: 'Email Send Failed',
|
||||
value: 'emailsSendFailed',
|
||||
},
|
||||
{
|
||||
name: 'Email Sent',
|
||||
value: 'emailsSent',
|
||||
},
|
||||
{
|
||||
name: 'Email Unsubscribed',
|
||||
value: 'emailsUnsubscribed',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Campaing ID',
|
||||
name: 'campaignId',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getCampaigns',
|
||||
},
|
||||
default: '',
|
||||
description: ` We'll call this hook only for this campaignId.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Is First',
|
||||
name: 'isFirst',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: `We'll call this hook only the first time this activity happened.`,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
methods = {
|
||||
loadOptions: {
|
||||
async getCampaigns(this: ILoadOptionsFunctions) {
|
||||
const campaigns = await lemlistApiRequest.call(this, 'GET', '/campaigns');
|
||||
return campaigns.map(({ _id, name }: { _id: string, name: string }) => ({
|
||||
name,
|
||||
value: _id,
|
||||
}));
|
||||
},
|
||||
},
|
||||
};
|
||||
// @ts-ignore
|
||||
webhookMethods = {
|
||||
default: {
|
||||
async checkExists(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
const webhookUrl = this.getNodeWebhookUrl('default');
|
||||
const webhooks = await lemlistApiRequest.call(this, 'GET', '/hooks');
|
||||
for (const webhook of webhooks) {
|
||||
if (webhook.targetUrl === webhookUrl) {
|
||||
await lemlistApiRequest.call(this, 'DELETE', `/hooks/${webhookData.webhookId}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
async create(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookUrl = this.getNodeWebhookUrl('default');
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
const options = this.getNodeParameter('options') as IDataObject;
|
||||
const event = this.getNodeParameter('event') as string[];
|
||||
const body: IDataObject = {
|
||||
targetUrl: webhookUrl,
|
||||
event,
|
||||
};
|
||||
Object.assign(body, options);
|
||||
const webhook = await lemlistApiRequest.call(this, 'POST', '/hooks', body);
|
||||
webhookData.webhookId = webhook._id;
|
||||
return true;
|
||||
},
|
||||
async delete(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
try {
|
||||
await lemlistApiRequest.call(this, 'DELETE', `/hooks/${webhookData.webhookId}`);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
delete webhookData.webhookId;
|
||||
return true;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
|
||||
const req = this.getRequestObject();
|
||||
return {
|
||||
workflowData: [
|
||||
this.helpers.returnJsonArray(req.body),
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const activityOperations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
default: 'getAll',
|
||||
description: 'Operation to perform',
|
||||
options: [
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
},
|
||||
],
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'activity',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const activityFields = [
|
||||
// ----------------------------------
|
||||
// activity: getAll
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Return all results.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'activity',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
default: 5,
|
||||
description: 'The number of results to return.',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 1000,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'activity',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filters',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Filter',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'activity',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Campaign ID',
|
||||
name: 'campaignId',
|
||||
type: 'options',
|
||||
required: true,
|
||||
default: '',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getCampaigns',
|
||||
},
|
||||
description: 'ID of the campaign to retrieve activity for.',
|
||||
},
|
||||
{
|
||||
displayName: 'Type',
|
||||
name: 'type',
|
||||
type: 'options',
|
||||
default: 'emailsOpened',
|
||||
description: 'Type of activity to retrieve.',
|
||||
options: [
|
||||
{
|
||||
name: 'Emails Bounced',
|
||||
value: 'emailsBounced',
|
||||
},
|
||||
{
|
||||
name: 'Emails Clicked',
|
||||
value: 'emailsClicked',
|
||||
},
|
||||
{
|
||||
name: 'Emails Opened',
|
||||
value: 'emailsOpened',
|
||||
},
|
||||
{
|
||||
name: 'Emails Replied',
|
||||
value: 'emailsReplied',
|
||||
},
|
||||
{
|
||||
name: 'Emails Send Failed',
|
||||
value: 'emailsSendFailed',
|
||||
},
|
||||
{
|
||||
name: 'Emails Sent',
|
||||
value: 'emailsSent',
|
||||
},
|
||||
{
|
||||
name: 'Emails Unsubscribed',
|
||||
value: 'emailsUnsubscribed',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
] as INodeProperties[];
|
|
@ -0,0 +1,73 @@
|
|||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const campaignOperations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
default: 'getAll',
|
||||
description: 'Operation to perform',
|
||||
options: [
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
},
|
||||
],
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'campaign',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const campaignFields = [
|
||||
// ----------------------------------
|
||||
// campaign: getAll
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Return all results.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'campaign',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
default: 5,
|
||||
description: 'The number of results to return.',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 1000,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'campaign',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
] as INodeProperties[];
|
|
@ -0,0 +1,234 @@
|
|||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const leadOperations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
default: 'create',
|
||||
description: 'Operation to perform',
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
},
|
||||
{
|
||||
name: 'Unsubscribe',
|
||||
value: 'unsubscribe',
|
||||
},
|
||||
],
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'lead',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const leadFields = [
|
||||
// ----------------------------------
|
||||
// lead: create
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'Campaign ID',
|
||||
name: 'campaignId',
|
||||
type: 'options',
|
||||
required: true,
|
||||
default: [],
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getCampaigns',
|
||||
},
|
||||
description: 'ID of the campaign to create the lead under.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'lead',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Email',
|
||||
name: 'email',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Email of the lead to create.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'lead',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'lead',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Company Name',
|
||||
name: 'companyName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Company name of the lead to create.',
|
||||
},
|
||||
{
|
||||
displayName: 'Deduplicate',
|
||||
name: 'deduplicate',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Do not insert if this email is already present in another campaign.',
|
||||
},
|
||||
{
|
||||
displayName: 'First Name',
|
||||
name: 'firstName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'First name of the lead to create.',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Name',
|
||||
name: 'lastName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Last name of the lead to create.',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// ----------------------------------
|
||||
// lead: delete
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'Campaign ID',
|
||||
name: 'campaignId',
|
||||
type: 'options',
|
||||
required: true,
|
||||
default: [],
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getCampaigns',
|
||||
},
|
||||
description: 'ID of the campaign to remove the lead from.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'lead',
|
||||
],
|
||||
operation: [
|
||||
'delete',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Email',
|
||||
name: 'email',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Email of the lead to delete.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'lead',
|
||||
],
|
||||
operation: [
|
||||
'delete',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// ----------------------------------
|
||||
// lead: get
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'Email',
|
||||
name: 'email',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Email of the lead to retrieve.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'lead',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// ----------------------------------
|
||||
// lead: unsubscribe
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'Campaign ID',
|
||||
name: 'campaignId',
|
||||
type: 'options',
|
||||
required: true,
|
||||
default: [],
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getCampaigns',
|
||||
},
|
||||
description: 'ID of the campaign to unsubscribe the lead from.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'lead',
|
||||
],
|
||||
operation: [
|
||||
'unsubscribe',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Email',
|
||||
name: 'email',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Email of the lead to unsubscribe.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'lead',
|
||||
],
|
||||
operation: [
|
||||
'unsubscribe',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
] as INodeProperties[];
|
|
@ -0,0 +1,33 @@
|
|||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const teamOperations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
default: 'get',
|
||||
description: 'Operation to perform',
|
||||
options: [
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
},
|
||||
],
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'team',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const teamFields = [
|
||||
// ----------------------------------
|
||||
// team: get
|
||||
// ----------------------------------
|
||||
|
||||
] as INodeProperties[];
|
|
@ -0,0 +1,123 @@
|
|||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const unsubscribeOperations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
default: 'add',
|
||||
description: 'Operation to perform',
|
||||
options: [
|
||||
{
|
||||
name: 'Add',
|
||||
value: 'add',
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
},
|
||||
],
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'unsubscribe',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const unsubscribeFields = [
|
||||
// ----------------------------------
|
||||
// unsubscribe: add
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'Email',
|
||||
name: 'email',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Email to add to the unsubscribes.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'unsubscribe',
|
||||
],
|
||||
operation: [
|
||||
'add',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// ----------------------------------
|
||||
// unsubscribe: delete
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'Email',
|
||||
name: 'email',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Email to delete from the unsubscribes.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'unsubscribe',
|
||||
],
|
||||
operation: [
|
||||
'delete',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// ----------------------------------
|
||||
// unsubscribe: getAll
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Return all results.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'unsubscribe',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
default: 5,
|
||||
description: 'The number of results to return.',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 1000,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'unsubscribe',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
] as INodeProperties[];
|
5
packages/nodes-base/nodes/Lemlist/descriptions/index.ts
Normal file
5
packages/nodes-base/nodes/Lemlist/descriptions/index.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export * from './ActivityDescription';
|
||||
export * from './CampaignDescription';
|
||||
export * from './LeadDescription';
|
||||
export * from './TeamDescription';
|
||||
export * from './UnsubscribeDescription';
|
23
packages/nodes-base/nodes/Lemlist/lemlist.svg
Normal file
23
packages/nodes-base/nodes/Lemlist/lemlist.svg
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="200.000000pt" height="200.000000pt" viewBox="0 0 200.000000 200.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
|
||||
<g transform="translate(0.000000,200.000000) scale(0.100000,-0.100000)"
|
||||
fill="#4d19ff" stroke="none">
|
||||
<path d="M315 1986 c-127 -40 -218 -119 -274 -236 l-36 -75 0 -415 c0 -403 1
|
||||
-418 23 -500 27 -100 93 -242 152 -331 24 -35 82 -102 130 -149 135 -133 296
|
||||
-215 520 -264 64 -15 131 -16 465 -14 l390 3 70 34 c88 43 163 119 207 208
|
||||
l33 68 3 655 c3 697 3 696 -46 795 -52 105 -122 167 -236 210 l-66 25 -648 -1
|
||||
c-494 0 -656 -3 -687 -13z m402 -590 l28 -24 5 -249 c8 -377 -3 -366 371 -371
|
||||
229 -3 248 -4 268 -23 27 -25 35 -60 24 -96 -17 -51 -34 -54 -323 -51 l-265 3
|
||||
-53 30 c-73 40 -147 123 -169 188 -13 41 -17 101 -21 285 -4 252 0 276 46 312
|
||||
33 26 55 25 89 -4z m654 9 c57 -30 62 -112 9 -140 -23 -12 -71 -15 -218 -15
|
||||
-176 0 -191 1 -216 21 -48 38 -29 119 34 142 8 3 93 6 188 6 138 1 179 -2 203
|
||||
-14z m-61 -345 c13 -13 20 -33 20 -60 0 -27 -7 -47 -20 -60 -18 -18 -33 -20
|
||||
-182 -20 -152 0 -163 1 -185 22 -30 28 -31 83 -1 115 21 22 27 23 185 23 150
|
||||
0 165 -2 183 -20z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
|
@ -128,6 +128,7 @@
|
|||
"dist/credentials/JotFormApi.credentials.js",
|
||||
"dist/credentials/Kafka.credentials.js",
|
||||
"dist/credentials/KeapOAuth2Api.credentials.js",
|
||||
"dist/credentials/LemlistApi.credentials.js",
|
||||
"dist/credentials/LineNotifyOAuth2Api.credentials.js",
|
||||
"dist/credentials/LingvaNexApi.credentials.js",
|
||||
"dist/credentials/LinkedInOAuth2Api.credentials.js",
|
||||
|
@ -387,6 +388,8 @@
|
|||
"dist/nodes/Kafka/KafkaTrigger.node.js",
|
||||
"dist/nodes/Keap/Keap.node.js",
|
||||
"dist/nodes/Keap/KeapTrigger.node.js",
|
||||
"dist/nodes/Lemlist/Lemlist.node.js",
|
||||
"dist/nodes/Lemlist/LemlistTrigger.node.js",
|
||||
"dist/nodes/Line/Line.node.js",
|
||||
"dist/nodes/LingvaNex/LingvaNex.node.js",
|
||||
"dist/nodes/LinkedIn/LinkedIn.node.js",
|
||||
|
|
Loading…
Reference in a new issue