mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-23 11:44:06 -08:00
feat(Emelia Node): Add Campaign > Duplicate functionality (#3000)
* feat(Emelia Node): Add campaign duplication feature * ⚡ small ui fixes, added credential test, fixed nodelinter issues * ⚡ Improvements * ⚡ Updated wording for Number operations on IF-Node (#3065) * fix(Google Tasks Node): Fix "Show Completed" option and hide title field where not needed (#2741) * 🐛 Google Tasks: Fix showCompleted * ⚡ Improvements Co-authored-by: ricardo <ricardoespinoza105@gmail.com> * feat(Mocean Node): Add "Delivery Report URL" option and credential tests (#3075) * add dlr url column add dlr url(delivery report URl) column. Allow user set the endpoint to receive the report * update update delivery report url description * ⚡ fixed nodelinter issues, added credential test, replaced icon * ⚡ Improvements Co-authored-by: d3no <d3no520@gmail.com> Co-authored-by: Michael Kret <michael.k@radency.com> * ⚡ Normalize name Co-authored-by: Michael Kret <michael.k@radency.com> Co-authored-by: ricardo <ricardoespinoza105@gmail.com> Co-authored-by: Jonathan Bennetts <jonathan.bennetts@gmail.com> Co-authored-by: Tom <19203795+that-one-tom@users.noreply.github.com> Co-authored-by: Ricardo Espinoza <ricardo@n8n.io> Co-authored-by: d3no <d3no520@gmail.com> Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
This commit is contained in:
parent
c89d2b10f2
commit
0b08be1c0b
|
@ -9,6 +9,7 @@ export const campaignOperations: INodeProperties[] = [
|
|||
type: 'options',
|
||||
default: 'get',
|
||||
description: 'Operation to perform',
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'Add Contact',
|
||||
|
@ -18,6 +19,10 @@ export const campaignOperations: INodeProperties[] = [
|
|||
name: 'Create',
|
||||
value: 'create',
|
||||
},
|
||||
{
|
||||
name: 'Duplicate',
|
||||
value: 'duplicate',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
|
@ -58,7 +63,7 @@ export const campaignFields: INodeProperties[] = [
|
|||
},
|
||||
default: [],
|
||||
required: true,
|
||||
description: 'The ID of the campaign to add the contact to.',
|
||||
description: 'The ID of the campaign to add the contact to',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -76,7 +81,7 @@ export const campaignFields: INodeProperties[] = [
|
|||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'The email of the contact to add to the campaign.',
|
||||
description: 'The email of the contact to add to the campaign',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -113,7 +118,7 @@ export const campaignFields: INodeProperties[] = [
|
|||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
description: 'Filter by custom fields ',
|
||||
description: 'Filter by custom fields',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
|
@ -125,14 +130,14 @@ export const campaignFields: INodeProperties[] = [
|
|||
name: 'fieldName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The name of the field to add custom field to.',
|
||||
description: 'The name of the field to add custom field to',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The value to set on custom field.',
|
||||
description: 'The value to set on custom field',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -143,49 +148,49 @@ export const campaignFields: INodeProperties[] = [
|
|||
name: 'firstName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'First name of the contact to add.',
|
||||
description: 'First name of the contact to add',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Contacted',
|
||||
name: 'lastContacted',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: 'Last contacted date of the contact to add',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Name',
|
||||
name: 'lastName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Last name of the contact to add.',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Contacted',
|
||||
name: 'lastContacted',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Last contacted date of the contact to add.',
|
||||
description: 'Last name of the contact to add',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Open',
|
||||
name: 'lastOpen',
|
||||
type: 'string',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: 'Last opened date of the contact to add.',
|
||||
description: 'Last opened date of the contact to add',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Replied',
|
||||
name: 'lastReplied',
|
||||
type: 'string',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: 'Last replied date of the contact to add.',
|
||||
description: 'Last replied date of the contact to add',
|
||||
},
|
||||
{
|
||||
displayName: 'Mails Sent',
|
||||
name: 'mailsSent',
|
||||
type: 'number',
|
||||
default: 0,
|
||||
description: 'Number of emails sent to the contact to add.',
|
||||
description: 'Number of emails sent to the contact to add',
|
||||
},
|
||||
{
|
||||
displayName: 'Phone Number',
|
||||
name: 'phoneNumber',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Phone number of the contact to add.',
|
||||
description: 'Phone number of the contact to add',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -199,7 +204,7 @@ export const campaignFields: INodeProperties[] = [
|
|||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'The name of the campaign to create.',
|
||||
description: 'The name of the campaign to create',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -221,7 +226,7 @@ export const campaignFields: INodeProperties[] = [
|
|||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
description: 'The ID of the campaign to retrieve.',
|
||||
description: 'The ID of the campaign to retrieve',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -242,7 +247,7 @@ export const campaignFields: INodeProperties[] = [
|
|||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Return all results.',
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -259,7 +264,7 @@ export const campaignFields: INodeProperties[] = [
|
|||
name: 'limit',
|
||||
type: 'number',
|
||||
default: 100,
|
||||
description: 'The number of results to return.',
|
||||
description: 'Max number of results to return',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 100,
|
||||
|
@ -323,4 +328,93 @@ export const campaignFields: INodeProperties[] = [
|
|||
},
|
||||
},
|
||||
|
||||
// ----------------------------------
|
||||
// campaign: duplicate
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'Campaign ID',
|
||||
name: 'campaignId',
|
||||
type: 'options',
|
||||
default: '',
|
||||
required: true,
|
||||
description: 'The ID of the campaign to duplicate',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getCampaigns',
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'campaign',
|
||||
],
|
||||
operation: [
|
||||
'duplicate',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'New Campaign Name',
|
||||
name: 'campaignName',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'The name of the new campaign to create',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'campaign',
|
||||
],
|
||||
operation: [
|
||||
'duplicate',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
default: {},
|
||||
placeholder: 'Add Field',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'duplicate',
|
||||
],
|
||||
resource: [
|
||||
'campaign',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Copy Contacts',
|
||||
name: 'copyContacts',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Whether to copy all the contacts from the original campaign',
|
||||
},
|
||||
{
|
||||
displayName: 'Copy Email Provider',
|
||||
name: 'copyProvider',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to set the same email provider than the original campaign',
|
||||
},
|
||||
{
|
||||
displayName: 'Copy Email Sequence',
|
||||
name: 'copyMails',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to copy all the steps of the email sequence from the original campaign',
|
||||
},
|
||||
{
|
||||
displayName: 'Copy Global Settings',
|
||||
name: 'copySettings',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to copy all the general settings from the original campaign',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
|
|
@ -7,8 +7,9 @@ export const contactListOperations: INodeProperties[] = [
|
|||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
default: 'get',
|
||||
default: 'getAll',
|
||||
description: 'Operation to perform',
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'Add',
|
||||
|
@ -42,7 +43,7 @@ export const contactListFields: INodeProperties[] = [
|
|||
},
|
||||
default: [],
|
||||
required: true,
|
||||
description: 'The ID of the contact list to add the contact to.',
|
||||
description: 'The ID of the contact list to add the contact to',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -60,7 +61,7 @@ export const contactListFields: INodeProperties[] = [
|
|||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'The email of the contact to add to the contact list.',
|
||||
description: 'The email of the contact to add to the contact list',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -97,7 +98,7 @@ export const contactListFields: INodeProperties[] = [
|
|||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
description: 'Filter by custom fields ',
|
||||
description: 'Filter by custom fields',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
|
@ -109,14 +110,14 @@ export const contactListFields: INodeProperties[] = [
|
|||
name: 'fieldName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The name of the field to add custom field to.',
|
||||
description: 'The name of the field to add custom field to',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The value to set on custom field.',
|
||||
description: 'The value to set on custom field',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -127,49 +128,49 @@ export const contactListFields: INodeProperties[] = [
|
|||
name: 'firstName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'First name of the contact to add.',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Name',
|
||||
name: 'lastName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Last name of the contact to add.',
|
||||
description: 'First name of the contact to add',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Contacted',
|
||||
name: 'lastContacted',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: 'Last contacted date of the contact to add.',
|
||||
description: 'Last contacted date of the contact to add',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Name',
|
||||
name: 'lastName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Last name of the contact to add',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Open',
|
||||
name: 'lastOpen',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: 'Last opened date of the contact to add.',
|
||||
description: 'Last opened date of the contact to add',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Replied',
|
||||
name: 'lastReplied',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: 'Last replied date of the contact to add.',
|
||||
description: 'Last replied date of the contact to add',
|
||||
},
|
||||
{
|
||||
displayName: 'Mails Sent',
|
||||
name: 'mailsSent',
|
||||
type: 'number',
|
||||
default: 0,
|
||||
description: 'Number of emails sent to the contact to add.',
|
||||
description: 'Number of emails sent to the contact to add',
|
||||
},
|
||||
{
|
||||
displayName: 'Phone Number',
|
||||
name: 'phoneNumber',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Phone number of the contact to add.',
|
||||
description: 'Phone number of the contact to add',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -182,7 +183,7 @@ export const contactListFields: INodeProperties[] = [
|
|||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Return all results.',
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -199,7 +200,7 @@ export const contactListFields: INodeProperties[] = [
|
|||
name: 'limit',
|
||||
type: 'number',
|
||||
default: 100,
|
||||
description: 'The number of results to return.',
|
||||
description: 'Max number of results to return',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 100,
|
||||
|
|
|
@ -7,10 +7,12 @@ import {
|
|||
ILoadOptionsFunctions,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeDescription
|
||||
INodeTypeDescription,
|
||||
JsonObject
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
emeliaApiTest,
|
||||
emeliaGraphqlRequest,
|
||||
loadResource,
|
||||
} from './GenericFunctions';
|
||||
|
@ -36,7 +38,7 @@ export class Emelia implements INodeType {
|
|||
icon: 'file:emelia.svg',
|
||||
group: ['input'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["resource"] + ": " + $parameter["operation"]}}',
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Consume the Emelia API',
|
||||
defaults: {
|
||||
name: 'Emelia',
|
||||
|
@ -47,6 +49,7 @@ export class Emelia implements INodeType {
|
|||
{
|
||||
name: 'emeliaApi',
|
||||
required: true,
|
||||
testedBy: 'emeliaApiTest',
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
|
@ -54,6 +57,7 @@ export class Emelia implements INodeType {
|
|||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'Campaign',
|
||||
|
@ -66,7 +70,7 @@ export class Emelia implements INodeType {
|
|||
],
|
||||
default: 'campaign',
|
||||
required: true,
|
||||
description: 'The resource to operate on.',
|
||||
description: 'The resource to operate on',
|
||||
},
|
||||
...campaignOperations,
|
||||
...campaignFields,
|
||||
|
@ -76,6 +80,10 @@ export class Emelia implements INodeType {
|
|||
};
|
||||
|
||||
methods = {
|
||||
credentialTest: {
|
||||
emeliaApiTest,
|
||||
},
|
||||
|
||||
loadOptions: {
|
||||
async getCampaigns(this: ILoadOptionsFunctions) {
|
||||
return loadResource.call(this, 'campaign');
|
||||
|
@ -290,6 +298,46 @@ export class Emelia implements INodeType {
|
|||
|
||||
returnData.push({ success: true });
|
||||
|
||||
} else if (operation === 'duplicate') {
|
||||
|
||||
// ----------------------------------
|
||||
// campaign: duplicate
|
||||
// ----------------------------------
|
||||
|
||||
const options = this.getNodeParameter('options', i) as IDataObject;
|
||||
const variables = {
|
||||
fromId: this.getNodeParameter('campaignId', i),
|
||||
name: this.getNodeParameter('campaignName', i),
|
||||
copySettings: true,
|
||||
copyMails: true,
|
||||
copyContacts: false,
|
||||
copyProvider: true,
|
||||
...options,
|
||||
};
|
||||
const { data: { duplicateCampaign } } = await emeliaGraphqlRequest.call(this, {
|
||||
query: `
|
||||
mutation duplicateCampaign(
|
||||
$fromId: ID!
|
||||
$name: String!
|
||||
$copySettings: Boolean!
|
||||
$copyMails: Boolean!
|
||||
$copyContacts: Boolean!
|
||||
$copyProvider: Boolean!
|
||||
) {
|
||||
duplicateCampaign(
|
||||
fromId: $fromId
|
||||
name: $name
|
||||
copySettings: $copySettings
|
||||
copyMails: $copyMails
|
||||
copyContacts: $copyContacts
|
||||
copyProvider: $copyProvider
|
||||
)
|
||||
}`,
|
||||
operationName: 'duplicateCampaign',
|
||||
variables,
|
||||
});
|
||||
|
||||
returnData.push({ _id: duplicateCampaign });
|
||||
}
|
||||
|
||||
} else if (resource === 'contactList') {
|
||||
|
@ -373,7 +421,7 @@ export class Emelia implements INodeType {
|
|||
} catch (error) {
|
||||
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({ error: error.message });
|
||||
returnData.push({ error: (error as JsonObject).message });
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
|
||||
import {
|
||||
emeliaApiRequest,
|
||||
emeliaApiTest,
|
||||
emeliaGraphqlRequest,
|
||||
} from './GenericFunctions';
|
||||
|
||||
|
@ -23,6 +24,7 @@ export class EmeliaTrigger implements INodeType {
|
|||
displayName: 'Emelia Trigger',
|
||||
name: 'emeliaTrigger',
|
||||
icon: 'file:emelia.svg',
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
group: ['trigger'],
|
||||
version: 1,
|
||||
description: 'Handle Emelia campaign activity events via webhooks',
|
||||
|
@ -35,6 +37,7 @@ export class EmeliaTrigger implements INodeType {
|
|||
{
|
||||
name: 'emeliaApi',
|
||||
required: true,
|
||||
testedBy: 'emeliaApiTest',
|
||||
},
|
||||
],
|
||||
webhooks: [
|
||||
|
@ -93,6 +96,10 @@ export class EmeliaTrigger implements INodeType {
|
|||
};
|
||||
|
||||
methods = {
|
||||
credentialTest: {
|
||||
emeliaApiTest,
|
||||
},
|
||||
|
||||
loadOptions: {
|
||||
async getCampaigns(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const responseData = await emeliaGraphqlRequest.call(this, {
|
||||
|
|
|
@ -4,8 +4,12 @@ import {
|
|||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
ICredentialsDecrypted,
|
||||
ICredentialTestFunctions,
|
||||
IHookFunctions,
|
||||
INodeCredentialTestResult,
|
||||
INodePropertyOptions,
|
||||
JsonObject,
|
||||
NodeApiError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
|
@ -51,7 +55,7 @@ export async function emeliaApiRequest(
|
|||
try {
|
||||
return await this.helpers.request!.call(this, options);
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error);
|
||||
throw new NodeApiError(this.getNode(), (error as JsonObject));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,5 +95,47 @@ export async function loadResource(
|
|||
name: campaign.name,
|
||||
value: campaign._id,
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
export async function emeliaApiTest(this: ICredentialTestFunctions, credential: ICredentialsDecrypted): Promise<INodeCredentialTestResult> {
|
||||
const credentials = credential.data;
|
||||
|
||||
const body = {
|
||||
query: `
|
||||
query all_campaigns {
|
||||
all_campaigns {
|
||||
_id
|
||||
name
|
||||
status
|
||||
createdAt
|
||||
stats {
|
||||
mailsSent
|
||||
}
|
||||
}
|
||||
}`,
|
||||
operationName: 'all_campaigns',
|
||||
};
|
||||
|
||||
const options = {
|
||||
headers: {
|
||||
Authorization: credentials?.apiKey,
|
||||
},
|
||||
method: 'POST',
|
||||
body,
|
||||
uri: `https://graphql.emelia.io/graphql`,
|
||||
json: true,
|
||||
};
|
||||
|
||||
try {
|
||||
await this.helpers.request!(options);
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 'Error',
|
||||
message: `Connection details not valid: ${(error as JsonObject).message}`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: 'OK',
|
||||
message: 'Authentication successful!',
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue