mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
✨ Add Emelia nodes (#1455)
* Emelia node added * Minor improvements on Emelia nodes * Fix nodes and credentials listing * Fix multi-line imports * Apply cosmetic changes to node description * Apply cosmetic changes to node execute method * Fix linting details * Apply cosmetic changes to trigger node * Replace PNG with SVG icon * Bring generic functions in line with codebase * Refactor resources and add operations * Fix typo in GraphQL call function * Add campaign description * Add contact list description * ⚡ Improvements * ⚡ Minor improvements to Emelia Nodes Co-authored-by: Charles LECALIER <clecalie@student.42.fr> Co-authored-by: ricardo <ricardoespinoza105@gmail.com> Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
This commit is contained in:
parent
4dce9e2cd1
commit
4d4ab7943b
18
packages/nodes-base/credentials/EmeliaApi.credentials.ts
Normal file
18
packages/nodes-base/credentials/EmeliaApi.credentials.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import {
|
||||||
|
ICredentialType,
|
||||||
|
NodePropertyTypes,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export class EmeliaApi implements ICredentialType {
|
||||||
|
name = 'emeliaApi';
|
||||||
|
displayName = 'Emelia API';
|
||||||
|
documentationUrl = 'emelia';
|
||||||
|
properties = [
|
||||||
|
{
|
||||||
|
displayName: 'API Key',
|
||||||
|
name: 'apiKey',
|
||||||
|
type: 'string' as NodePropertyTypes,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
326
packages/nodes-base/nodes/Emelia/CampaignDescription.ts
Normal file
326
packages/nodes-base/nodes/Emelia/CampaignDescription.ts
Normal file
|
@ -0,0 +1,326 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const campaignOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
default: 'get',
|
||||||
|
description: 'Operation to perform',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Add Contact',
|
||||||
|
value: 'addContact',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Create',
|
||||||
|
value: 'create',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get',
|
||||||
|
value: 'get',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get All',
|
||||||
|
value: 'getAll',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Pause',
|
||||||
|
value: 'pause',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Start',
|
||||||
|
value: 'start',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'campaign',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const campaignFields = [
|
||||||
|
// ----------------------------------
|
||||||
|
// campaign: addContact
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Campaign ID',
|
||||||
|
name: 'campaignId',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getCampaigns',
|
||||||
|
},
|
||||||
|
default: [],
|
||||||
|
required: true,
|
||||||
|
description: 'The ID of the campaign to add the contact to.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'campaign',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'addContact',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Contact Email',
|
||||||
|
name: 'contactEmail',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'The email of the contact to add to the campaign.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'campaign',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'addContact',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Additional Fields',
|
||||||
|
name: 'additionalFields',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
default: {},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'campaign',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'addContact',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
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',
|
||||||
|
name: 'fieldName',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
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.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'First Name',
|
||||||
|
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.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Last Contacted',
|
||||||
|
name: 'lastContacted',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'Last contacted date of the contact to add.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Last Open',
|
||||||
|
name: 'lastOpen',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'Last opened date of the contact to add.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Last Replied',
|
||||||
|
name: 'lastReplied',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
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.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Phone Number',
|
||||||
|
name: 'phoneNumber',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'Phone number of the contact to add.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// campaign: create
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Campaign Name',
|
||||||
|
name: 'campaignName',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'The name of the campaign to create.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'campaign',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// campaign: get
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Campaign ID',
|
||||||
|
name: 'campaignId',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
description: 'The ID of the campaign to retrieve.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'campaign',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// 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: 100,
|
||||||
|
description: 'The number of results to return.',
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 100,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'campaign',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// campaign: pause
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Campaign ID',
|
||||||
|
name: 'campaignId',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
description: 'The ID of the campaign to pause.<br>The campaign must be in RUNNING mode.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'campaign',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'pause',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// campaign: start
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Campaign ID',
|
||||||
|
name: 'campaignId',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
description: 'The ID of the campaign to start.<br>Email provider and contacts must be set.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'campaign',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'start',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
] as INodeProperties[];
|
221
packages/nodes-base/nodes/Emelia/ContactListDescription.ts
Normal file
221
packages/nodes-base/nodes/Emelia/ContactListDescription.ts
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const contactListOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
default: 'get',
|
||||||
|
description: 'Operation to perform',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Add',
|
||||||
|
value: 'add',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get All',
|
||||||
|
value: 'getAll',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'contactList',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const contactListFields = [
|
||||||
|
// ----------------------------------
|
||||||
|
// contactList: add
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Contact List ID',
|
||||||
|
name: 'contactListId',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getContactLists',
|
||||||
|
},
|
||||||
|
default: [],
|
||||||
|
required: true,
|
||||||
|
description: 'The ID of the contact list to add the contact to.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'contactList',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'add',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Contact Email',
|
||||||
|
name: 'contactEmail',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'The email of the contact to add to the contact list.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'contactList',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'add',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Additional Fields',
|
||||||
|
name: 'additionalFields',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
default: {},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'contactList',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'add',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
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',
|
||||||
|
name: 'fieldName',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
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.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'First Name',
|
||||||
|
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.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Last Contacted',
|
||||||
|
name: 'lastContacted',
|
||||||
|
type: 'dateTime',
|
||||||
|
default: '',
|
||||||
|
description: 'Last contacted date of the contact to add.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Last Open',
|
||||||
|
name: 'lastOpen',
|
||||||
|
type: 'dateTime',
|
||||||
|
default: '',
|
||||||
|
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.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Mails Sent',
|
||||||
|
name: 'mailsSent',
|
||||||
|
type: 'number',
|
||||||
|
default: 0,
|
||||||
|
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.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// contactList: getAll
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Return all results.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'contactList',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Limit',
|
||||||
|
name: 'limit',
|
||||||
|
type: 'number',
|
||||||
|
default: 100,
|
||||||
|
description: 'The number of results to return.',
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 100,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'contactList',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
387
packages/nodes-base/nodes/Emelia/Emelia.node.ts
Normal file
387
packages/nodes-base/nodes/Emelia/Emelia.node.ts
Normal file
|
@ -0,0 +1,387 @@
|
||||||
|
import {
|
||||||
|
IExecuteFunctions
|
||||||
|
} from 'n8n-core';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IDataObject,
|
||||||
|
ILoadOptionsFunctions,
|
||||||
|
INodeExecutionData,
|
||||||
|
INodeType,
|
||||||
|
INodeTypeDescription
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
emeliaGraphqlRequest,
|
||||||
|
loadResource,
|
||||||
|
} from './GenericFunctions';
|
||||||
|
|
||||||
|
import {
|
||||||
|
campaignFields,
|
||||||
|
campaignOperations,
|
||||||
|
} from './CampaignDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
contactListFields,
|
||||||
|
contactListOperations,
|
||||||
|
} from './ContactListDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
isEmpty,
|
||||||
|
} from 'lodash';
|
||||||
|
|
||||||
|
export class Emelia implements INodeType {
|
||||||
|
description: INodeTypeDescription = {
|
||||||
|
displayName: 'Emelia',
|
||||||
|
name: 'emelia',
|
||||||
|
icon: 'file:emelia.svg',
|
||||||
|
group: ['input'],
|
||||||
|
version: 1,
|
||||||
|
subtitle: '={{$parameter["resource"] + ": " + $parameter["operation"]}}',
|
||||||
|
description: 'Consume the Emelia API',
|
||||||
|
defaults: {
|
||||||
|
name: 'Emelia',
|
||||||
|
color: '#e18063',
|
||||||
|
},
|
||||||
|
inputs: ['main'],
|
||||||
|
outputs: ['main'],
|
||||||
|
credentials: [
|
||||||
|
{
|
||||||
|
name: 'emeliaApi',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
displayName: 'Resource',
|
||||||
|
name: 'resource',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Campaign',
|
||||||
|
value: 'campaign',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Contact List',
|
||||||
|
value: 'contactList',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'campaign',
|
||||||
|
required: true,
|
||||||
|
description: 'The resource to operate on.',
|
||||||
|
},
|
||||||
|
...campaignOperations,
|
||||||
|
...campaignFields,
|
||||||
|
...contactListOperations,
|
||||||
|
...contactListFields,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
methods = {
|
||||||
|
loadOptions: {
|
||||||
|
async getCampaigns(this: ILoadOptionsFunctions) {
|
||||||
|
return loadResource.call(this, 'campaign');
|
||||||
|
},
|
||||||
|
|
||||||
|
async getContactLists(this: ILoadOptionsFunctions) {
|
||||||
|
return loadResource.call(this, 'contactList');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||||
|
const items = this.getInputData();
|
||||||
|
const returnData: IDataObject[] = [];
|
||||||
|
|
||||||
|
const resource = this.getNodeParameter('resource', 0);
|
||||||
|
const operation = this.getNodeParameter('operation', 0);
|
||||||
|
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
if (resource === 'campaign') {
|
||||||
|
|
||||||
|
// **********************************
|
||||||
|
// campaign
|
||||||
|
// **********************************
|
||||||
|
|
||||||
|
if (operation === 'addContact') {
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// campaign: addContact
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
const contact = {
|
||||||
|
email: this.getNodeParameter('contactEmail', i) as string,
|
||||||
|
};
|
||||||
|
|
||||||
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
|
|
||||||
|
if (!isEmpty(additionalFields)) {
|
||||||
|
Object.assign(contact, additionalFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (additionalFields.customFieldsUi) {
|
||||||
|
const customFields = (additionalFields.customFieldsUi as IDataObject || {}).customFieldsValues as IDataObject[] || [];
|
||||||
|
const data = customFields.reduce((obj, value) => Object.assign(obj, { [`${value.fieldName}`]: value.value }), {});
|
||||||
|
Object.assign(contact, data);
|
||||||
|
//@ts-ignore
|
||||||
|
delete contact.customFieldsUi;
|
||||||
|
}
|
||||||
|
|
||||||
|
const responseData = await emeliaGraphqlRequest.call(this, {
|
||||||
|
query: `
|
||||||
|
mutation AddContactToCampaignHook($id: ID!, $contact: JSON!) {
|
||||||
|
addContactToCampaignHook(id: $id, contact: $contact)
|
||||||
|
}`,
|
||||||
|
operationName: 'AddContactToCampaignHook',
|
||||||
|
variables: {
|
||||||
|
id: this.getNodeParameter('campaignId', i),
|
||||||
|
contact,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
returnData.push({ contactId: responseData.data.addContactToCampaignHook });
|
||||||
|
|
||||||
|
} else if (operation === 'create') {
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// campaign: create
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
const responseData = await emeliaGraphqlRequest.call(this, {
|
||||||
|
operationName: 'createCampaign',
|
||||||
|
query: `
|
||||||
|
mutation createCampaign($name: String!) {
|
||||||
|
createCampaign(name: $name) {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
status
|
||||||
|
createdAt
|
||||||
|
provider
|
||||||
|
startAt
|
||||||
|
estimatedEnd
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
variables: {
|
||||||
|
name: this.getNodeParameter('campaignName', i),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
returnData.push(responseData.data.createCampaign);
|
||||||
|
|
||||||
|
} else if (operation === 'get') {
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// campaign: get
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
const responseData = await emeliaGraphqlRequest.call(this, {
|
||||||
|
query: `
|
||||||
|
query campaign($id: ID!){
|
||||||
|
campaign(id: $id){
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
status
|
||||||
|
createdAt
|
||||||
|
schedule{
|
||||||
|
dailyContact
|
||||||
|
dailyLimit
|
||||||
|
minInterval
|
||||||
|
maxInterval
|
||||||
|
trackLinks
|
||||||
|
trackOpens
|
||||||
|
timeZone
|
||||||
|
days
|
||||||
|
start
|
||||||
|
end
|
||||||
|
eventToStopMails
|
||||||
|
}
|
||||||
|
provider
|
||||||
|
startAt
|
||||||
|
recipients{
|
||||||
|
total_count
|
||||||
|
}
|
||||||
|
estimatedEnd
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
operationName: 'campaign',
|
||||||
|
variables: {
|
||||||
|
id: this.getNodeParameter('campaignId', i),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
returnData.push(responseData.data.campaign);
|
||||||
|
|
||||||
|
} else if (operation === 'getAll') {
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// campaign: getAll
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
const responseData = await emeliaGraphqlRequest.call(this, {
|
||||||
|
query: `
|
||||||
|
query all_campaigns {
|
||||||
|
all_campaigns {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
status
|
||||||
|
createdAt
|
||||||
|
stats {
|
||||||
|
mailsSent
|
||||||
|
uniqueOpensPercent
|
||||||
|
opens
|
||||||
|
linkClickedPercent
|
||||||
|
repliedPercent
|
||||||
|
bouncedPercent
|
||||||
|
unsubscribePercent
|
||||||
|
progressPercent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
operationName: 'all_campaigns',
|
||||||
|
});
|
||||||
|
|
||||||
|
let campaigns = responseData.data.all_campaigns;
|
||||||
|
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', i);
|
||||||
|
|
||||||
|
if (!returnAll) {
|
||||||
|
const limit = this.getNodeParameter('limit', i) as number;
|
||||||
|
campaigns = campaigns.slice(0, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
returnData.push(...campaigns);
|
||||||
|
|
||||||
|
} else if (operation === 'pause') {
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// campaign: pause
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
await emeliaGraphqlRequest.call(this, {
|
||||||
|
query: `
|
||||||
|
mutation pauseCampaign($id: ID!) {
|
||||||
|
pauseCampaign(id: $id)
|
||||||
|
}`,
|
||||||
|
operationName: 'pauseCampaign',
|
||||||
|
variables: {
|
||||||
|
id: this.getNodeParameter('campaignId', i),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
returnData.push({ success: true });
|
||||||
|
|
||||||
|
} else if (operation === 'start') {
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// campaign: start
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
await emeliaGraphqlRequest.call(this, {
|
||||||
|
query: `
|
||||||
|
mutation startCampaign($id: ID!) {
|
||||||
|
startCampaign(id: $id)
|
||||||
|
}`,
|
||||||
|
operationName: 'startCampaign',
|
||||||
|
variables: {
|
||||||
|
id: this.getNodeParameter('campaignId', i),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
returnData.push({ success: true });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (resource === 'contactList') {
|
||||||
|
|
||||||
|
// **********************************
|
||||||
|
// ContactList
|
||||||
|
// **********************************
|
||||||
|
|
||||||
|
if (operation === 'add') {
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// contactList: add
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
const contact = {
|
||||||
|
email: this.getNodeParameter('contactEmail', i) as string,
|
||||||
|
};
|
||||||
|
|
||||||
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
|
|
||||||
|
if (!isEmpty(additionalFields)) {
|
||||||
|
Object.assign(contact, additionalFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (additionalFields.customFieldsUi) {
|
||||||
|
const customFields = (additionalFields.customFieldsUi as IDataObject || {}).customFieldsValues as IDataObject[] || [];
|
||||||
|
const data = customFields.reduce((obj, value) => Object.assign(obj, { [`${value.fieldName}`]: value.value }), {});
|
||||||
|
Object.assign(contact, data);
|
||||||
|
//@ts-ignore
|
||||||
|
delete contact.customFieldsUi;
|
||||||
|
}
|
||||||
|
|
||||||
|
const responseData = await emeliaGraphqlRequest.call(this, {
|
||||||
|
query: `
|
||||||
|
mutation AddContactsToListHook($id: ID!, $contact: JSON!) {
|
||||||
|
addContactsToListHook(id: $id, contact: $contact)
|
||||||
|
}`,
|
||||||
|
operationName: 'AddContactsToListHook',
|
||||||
|
variables: {
|
||||||
|
id: this.getNodeParameter('contactListId', i),
|
||||||
|
contact,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
returnData.push({ contactId: responseData.data.addContactsToListHook });
|
||||||
|
|
||||||
|
} else if (operation === 'getAll') {
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// contactList: getAll
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
const responseData = await emeliaGraphqlRequest.call(this, {
|
||||||
|
query: `
|
||||||
|
query contact_lists{
|
||||||
|
contact_lists{
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
contactCount
|
||||||
|
fields
|
||||||
|
usedInCampaign
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
operationName: 'contact_lists',
|
||||||
|
});
|
||||||
|
|
||||||
|
let contactLists = responseData.data.contact_lists;
|
||||||
|
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', i);
|
||||||
|
|
||||||
|
if (!returnAll) {
|
||||||
|
const limit = this.getNodeParameter('limit', i) as number;
|
||||||
|
contactLists = contactLists.slice(0, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
returnData.push(...contactLists);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
|
||||||
|
if (this.continueOnFail()) {
|
||||||
|
returnData.push({ error: error.message });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [this.helpers.returnJsonArray(returnData)];
|
||||||
|
}
|
||||||
|
}
|
180
packages/nodes-base/nodes/Emelia/EmeliaTrigger.node.ts
Normal file
180
packages/nodes-base/nodes/Emelia/EmeliaTrigger.node.ts
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
import {
|
||||||
|
IHookFunctions,
|
||||||
|
ILoadOptionsFunctions,
|
||||||
|
INodePropertyOptions,
|
||||||
|
INodeType,
|
||||||
|
INodeTypeDescription,
|
||||||
|
IWebhookFunctions,
|
||||||
|
IWebhookResponseData,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
emeliaApiRequest,
|
||||||
|
emeliaGraphqlRequest,
|
||||||
|
} from './GenericFunctions';
|
||||||
|
|
||||||
|
interface Campaign {
|
||||||
|
_id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EmeliaTrigger implements INodeType {
|
||||||
|
description: INodeTypeDescription = {
|
||||||
|
displayName: 'Emelia Trigger',
|
||||||
|
name: 'emeliaTrigger',
|
||||||
|
icon: 'file:emelia.svg',
|
||||||
|
group: ['trigger'],
|
||||||
|
version: 1,
|
||||||
|
description: 'Handle Emelia campaign activity events via webhooks',
|
||||||
|
defaults: {
|
||||||
|
name: 'Emelia Trigger',
|
||||||
|
color: '#e18063',
|
||||||
|
},
|
||||||
|
inputs: [],
|
||||||
|
outputs: ['main'],
|
||||||
|
credentials: [
|
||||||
|
{
|
||||||
|
name: 'emeliaApi',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
webhooks: [
|
||||||
|
{
|
||||||
|
name: 'default',
|
||||||
|
httpMethod: 'POST',
|
||||||
|
responseMode: 'onReceived',
|
||||||
|
path: 'webhook',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
displayName: 'Campaign',
|
||||||
|
name: 'campaignId',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getCampaigns',
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Events',
|
||||||
|
name: 'events',
|
||||||
|
type: 'multiOptions',
|
||||||
|
required: true,
|
||||||
|
default: [],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Email Bounced',
|
||||||
|
value: 'bounced',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Email Opened',
|
||||||
|
value: 'opened',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Email Replied',
|
||||||
|
value: 'replied',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Email Sent',
|
||||||
|
value: 'sent',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Link Clicked',
|
||||||
|
value: 'clicked',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Unsubscribed Contact',
|
||||||
|
value: 'unsubscribed',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
methods = {
|
||||||
|
loadOptions: {
|
||||||
|
async getCampaigns(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
|
const responseData = await emeliaGraphqlRequest.call(this, {
|
||||||
|
query: `
|
||||||
|
query GetCampaigns {
|
||||||
|
campaigns {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
operationName: 'GetCampaigns',
|
||||||
|
variables: '{}',
|
||||||
|
});
|
||||||
|
|
||||||
|
return responseData.data.campaigns.map(
|
||||||
|
(campaign: Campaign) => ({
|
||||||
|
name: campaign.name,
|
||||||
|
value: campaign._id,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
webhookMethods = {
|
||||||
|
default: {
|
||||||
|
async checkExists(this: IHookFunctions): Promise<boolean> {
|
||||||
|
const webhookUrl = this.getNodeWebhookUrl('default') as string;
|
||||||
|
const campaignId = this.getNodeParameter('campaignId') as string;
|
||||||
|
const { webhooks } = await emeliaApiRequest.call(this, 'GET', '/webhook');
|
||||||
|
for (const webhook of webhooks) {
|
||||||
|
if (webhook.url === webhookUrl && webhook.campaignId === campaignId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
async create(this: IHookFunctions): Promise<boolean> {
|
||||||
|
const webhookUrl = this.getNodeWebhookUrl('default') as string;
|
||||||
|
const webhookData = this.getWorkflowStaticData('node');
|
||||||
|
const events = this.getNodeParameter('events') as string[];
|
||||||
|
|
||||||
|
const campaignId = this.getNodeParameter('campaignId') as string;
|
||||||
|
const body = {
|
||||||
|
hookUrl: webhookUrl,
|
||||||
|
events: events.map(e => e.toUpperCase()),
|
||||||
|
campaignId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const { webhookId } = await emeliaApiRequest.call(this, 'POST', '/webhook/webhook', body);
|
||||||
|
webhookData.webhookId = webhookId;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
async delete(this: IHookFunctions): Promise<boolean> {
|
||||||
|
const webhookData = this.getWorkflowStaticData('node');
|
||||||
|
const webhookUrl = this.getNodeWebhookUrl('default') as string;
|
||||||
|
const campaignId = this.getNodeParameter('campaignId') as string;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const body = {
|
||||||
|
hookUrl: webhookUrl,
|
||||||
|
campaignId,
|
||||||
|
};
|
||||||
|
await emeliaApiRequest.call(this, 'DELETE', '/webhook/webhook', body);
|
||||||
|
} 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),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
104
packages/nodes-base/nodes/Emelia/GenericFunctions.ts
Normal file
104
packages/nodes-base/nodes/Emelia/GenericFunctions.ts
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
import {
|
||||||
|
IExecuteFunctions,
|
||||||
|
ILoadOptionsFunctions,
|
||||||
|
} from 'n8n-core';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IHookFunctions,
|
||||||
|
INodePropertyOptions,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make an authenticated GraphQL request to Emelia.
|
||||||
|
*/
|
||||||
|
export async function emeliaGraphqlRequest(
|
||||||
|
this: IExecuteFunctions | ILoadOptionsFunctions,
|
||||||
|
body: object = {},
|
||||||
|
) {
|
||||||
|
const response = await emeliaApiRequest.call(this, 'POST', '/graphql', body);
|
||||||
|
|
||||||
|
if (response.errors) {
|
||||||
|
throw new Error(`Emelia error message: ${response.errors[0].message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make an authenticated REST API request to Emelia, used for trigger node.
|
||||||
|
*/
|
||||||
|
export async function emeliaApiRequest(
|
||||||
|
this: IExecuteFunctions | ILoadOptionsFunctions | IHookFunctions,
|
||||||
|
method: string,
|
||||||
|
endpoint: string,
|
||||||
|
body: object = {},
|
||||||
|
qs: object = {},
|
||||||
|
) {
|
||||||
|
const { apiKey } = this.getCredentials('emeliaApi') as { apiKey: string };
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
headers: {
|
||||||
|
Authorization: apiKey,
|
||||||
|
},
|
||||||
|
method,
|
||||||
|
body,
|
||||||
|
qs,
|
||||||
|
uri: `https://graphql.emelia.io${endpoint}`,
|
||||||
|
json: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
return await this.helpers.request!.call(this, options);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
|
||||||
|
if (error?.response?.body?.error) {
|
||||||
|
const { error: errorMessage } = error.response.body;
|
||||||
|
throw new Error(
|
||||||
|
`Emelia error response [${error.statusCode}]: ${errorMessage}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load resources so that the user can select them easily.
|
||||||
|
*/
|
||||||
|
export async function loadResource(
|
||||||
|
this: ILoadOptionsFunctions,
|
||||||
|
resource: 'campaign' | 'contactList',
|
||||||
|
): Promise<INodePropertyOptions[]> {
|
||||||
|
const mapping: { [key in 'campaign' | 'contactList']: { query: string, key: string } } = {
|
||||||
|
campaign: {
|
||||||
|
query: `
|
||||||
|
query GetCampaigns {
|
||||||
|
campaigns {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
key: 'campaigns',
|
||||||
|
},
|
||||||
|
contactList: {
|
||||||
|
query: `
|
||||||
|
query GetContactLists {
|
||||||
|
contact_lists {
|
||||||
|
_id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
key: 'contact_lists',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const responseData = await emeliaGraphqlRequest.call(this, { query: mapping[resource].query });
|
||||||
|
|
||||||
|
return responseData.data[mapping[resource].key].map((campaign: { name: string, _id: string }) => ({
|
||||||
|
name: campaign.name,
|
||||||
|
value: campaign._id,
|
||||||
|
}));
|
||||||
|
|
||||||
|
}
|
1
packages/nodes-base/nodes/Emelia/emelia.svg
Normal file
1
packages/nodes-base/nodes/Emelia/emelia.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg viewBox="-2 2 45 45" xmlns="http://www.w3.org/2000/svg"><path d="M29.714 6.539H9.078a2.109 2.109 0 00-2.007 2.762l5.484 16.863 4.219-1.817a2.69 2.69 0 01-.024-.34 2.641 2.641 0 111.023 2.081 2416.71 2416.71 0 01-4.415 2.545l5.156 15.852a.865.865 0 001.645 0l11.48-35.293a2.026 2.026 0 00-1.925-2.653z" opacity="0.9" fill="#f4b454"></path><path d="M37.154 21.261L1.071 12.577a.865.865 0 00-.861 1.4l10.909 12.8 5.656-2.436a2.694 2.694 0 01-.024-.34 2.641 2.641 0 111.023 2.081 2178.15 2178.15 0 01-4.86 2.8l11.291 13.255a2.11 2.11 0 003.4-.264l10.8-17.586a2.026 2.026 0 00-1.251-3.026z" fill="#ef6d4a" opacity="0.9"></path><path d="M37.636 12.577L1.553 21.261A2.025 2.025 0 00.301 24.29l3.472 5.656 13-5.6a2.689 2.689 0 01-.024-.34 2.641 2.641 0 111.023 2.081c-3.1 1.791-9.073 5.234-12.108 6.94l5.432 8.848a2.11 2.11 0 003.4.264l23.995-28.161a.865.865 0 00-.855-1.401z" fill="#20354c" opacity="0.9"></path></svg>
|
After Width: | Height: | Size: 917 B |
|
@ -70,6 +70,7 @@
|
||||||
"dist/credentials/DropboxApi.credentials.js",
|
"dist/credentials/DropboxApi.credentials.js",
|
||||||
"dist/credentials/DropboxOAuth2Api.credentials.js",
|
"dist/credentials/DropboxOAuth2Api.credentials.js",
|
||||||
"dist/credentials/EgoiApi.credentials.js",
|
"dist/credentials/EgoiApi.credentials.js",
|
||||||
|
"dist/credentials/EmeliaApi.credentials.js",
|
||||||
"dist/credentials/EventbriteApi.credentials.js",
|
"dist/credentials/EventbriteApi.credentials.js",
|
||||||
"dist/credentials/EventbriteOAuth2Api.credentials.js",
|
"dist/credentials/EventbriteOAuth2Api.credentials.js",
|
||||||
"dist/credentials/FacebookGraphApi.credentials.js",
|
"dist/credentials/FacebookGraphApi.credentials.js",
|
||||||
|
@ -317,6 +318,8 @@
|
||||||
"dist/nodes/Egoi/Egoi.node.js",
|
"dist/nodes/Egoi/Egoi.node.js",
|
||||||
"dist/nodes/EmailReadImap.node.js",
|
"dist/nodes/EmailReadImap.node.js",
|
||||||
"dist/nodes/EmailSend.node.js",
|
"dist/nodes/EmailSend.node.js",
|
||||||
|
"dist/nodes/Emelia/Emelia.node.js",
|
||||||
|
"dist/nodes/Emelia/EmeliaTrigger.node.js",
|
||||||
"dist/nodes/ErrorTrigger.node.js",
|
"dist/nodes/ErrorTrigger.node.js",
|
||||||
"dist/nodes/Eventbrite/EventbriteTrigger.node.js",
|
"dist/nodes/Eventbrite/EventbriteTrigger.node.js",
|
||||||
"dist/nodes/ExecuteCommand.node.js",
|
"dist/nodes/ExecuteCommand.node.js",
|
||||||
|
|
Loading…
Reference in a new issue