Add Automizy-Node (#1039)

*  Automizy-Node

*  Small improvement
This commit is contained in:
Ricardo Espinoza 2020-10-13 15:35:01 -04:00 committed by GitHub
parent 1e275b188d
commit 719a30350c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 1190 additions and 0 deletions

View file

@ -0,0 +1,17 @@
import {
ICredentialType,
NodePropertyTypes,
} from 'n8n-workflow';
export class AutomizyApi implements ICredentialType {
name = 'automizyApi';
displayName = 'Automizy API';
properties = [
{
displayName: 'API Token',
name: 'apiToken',
type: 'string' as NodePropertyTypes,
default: '',
},
];
}

View file

@ -0,0 +1,385 @@
import {
IExecuteFunctions,
} from 'n8n-core';
import {
IDataObject,
ILoadOptionsFunctions,
INodeExecutionData,
INodePropertyOptions,
INodeType,
INodeTypeDescription,
} from 'n8n-workflow';
import {
automizyApiRequest,
automizyApiRequestAllItems,
} from './GenericFunctions';
import {
contactListFields,
contactListOperations,
} from './ContactListDescription';
import {
contactFields,
contactOperations,
} from './ContactDescription';
import {
listFields,
listOperations,
} from './ListDescription';
export class Automizy implements INodeType {
description: INodeTypeDescription = {
displayName: 'Automizy',
name: 'automizy',
icon: 'file:automizy.png',
group: ['input'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Consume Automizy API',
defaults: {
name: 'Automizy',
color: '#e5863b',
},
inputs: ['main'],
outputs: ['main'],
credentials: [
{
name: 'automizyApi',
required: true,
},
],
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
options: [
{
name: 'Contact',
value: 'contact',
},
{
name: 'Contact List',
value: 'contactList',
},
{
name: 'List',
value: 'list',
},
],
default: 'contactList',
description: 'The resource to operate on.'
},
...contactListOperations,
...contactListFields,
...contactOperations,
...contactFields,
...listOperations,
...listFields,
],
};
methods = {
loadOptions: {
// Get all the tags to display them to user so that he can
// select them easily
async getTags(
this: ILoadOptionsFunctions
): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
const tags = await automizyApiRequestAllItems.call(
this,
'contactTags',
'GET',
'/contacts/tag-manager'
);
for (const tag of tags) {
const tagName = tag.name;
const tagId = tag.name;
returnData.push({
name: tagName,
value: tagId
});
}
return returnData;
},
// Get all the custom fields to display them to user so that he can
// select them easily
async getCustomFields(
this: ILoadOptionsFunctions
): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
const customFields = await automizyApiRequestAllItems.call(
this,
'customFields',
'GET',
'/custom-fields',
);
for (const customField of customFields) {
const customFieldName = customField.name;
const customFieldId = customField.id;
returnData.push({
name: customFieldName,
value: customFieldId
});
}
return returnData;
},
},
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: IDataObject[] = [];
const length = (items.length as unknown) as number;
const qs: IDataObject = {};
let responseData;
const resource = this.getNodeParameter('resource', 0) as string;
const operation = this.getNodeParameter('operation', 0) as string;
for (let i = 0; i < length; i++) {
if (resource === 'contactList') {
if (operation === 'add') {
const listId = this.getNodeParameter('listId', i) as string;
const email = this.getNodeParameter('email', i) as string;
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
const body: IDataObject = {
email,
};
Object.assign(body, additionalFields);
if (body.customFieldsUi) {
const customFieldsValues= (body.customFieldsUi as IDataObject).customFieldsValues as IDataObject[];
body.customFields = {};
for (const customField of customFieldsValues) {
//@ts-ignore
body.customFields[customField.key] = customField.value;
}
delete body.customFieldsUi;
}
responseData = await automizyApiRequest.call(
this,
'POST',
`/smart-lists/${listId}/contacts`,
body,
);
}
if (operation === 'getAll') {
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
const listId = this.getNodeParameter('listId', i) as string;
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
if (additionalFields.direction && additionalFields.sortBy) {
qs.order = `${additionalFields.sortBy}:${additionalFields.direction }`;
}
if (additionalFields.fields) {
qs.fields = additionalFields.fields;
}
if (returnAll) {
responseData = await automizyApiRequestAllItems.call(
this,
'contacts',
'GET',
`/smart-lists/${listId}/contacts`,
{},
qs,
);
} else {
qs.limit = this.getNodeParameter('limit', i) as number;
responseData = await automizyApiRequest.call(
this,
'GET',
`/smart-lists/${listId}/contacts`,
{},
qs,
);
responseData = responseData.contacts;
}
}
}
if (resource === 'contact') {
if (operation === 'delete') {
const contactId = this.getNodeParameter('contactId', i) as string;
responseData = await automizyApiRequest.call(
this,
'DELETE',
`/contacts/${contactId}`,
);
responseData = { success: true };
}
if (operation === 'get') {
const contactId = this.getNodeParameter('contactId', i) as string;
responseData = await automizyApiRequest.call(
this,
'GET',
`/contacts/${contactId}`,
);
}
if (operation === 'update') {
const email = this.getNodeParameter('email', i) as string;
const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
const body: IDataObject = {};
Object.assign(body, updateFields);
if (body.customFieldsUi) {
const customFieldsValues= (body.customFieldsUi as IDataObject).customFieldsValues as IDataObject[];
body.customFields = {};
for (const customField of customFieldsValues) {
//@ts-ignore
body.customFields[customField.key] = customField.value;
}
delete body.customFieldsUi;
}
responseData = await automizyApiRequest.call(
this,
'PATCH',
`/contacts/${email}`,
body,
);
}
}
if (resource === 'list') {
if (operation === 'create') {
const name = this.getNodeParameter('name', i) as string;
const body: IDataObject = {
name,
};
responseData = await automizyApiRequest.call(
this,
'POST',
`/smart-lists`,
body,
);
}
if (operation === 'delete') {
const listId = this.getNodeParameter('listId', i) as string;
responseData = await automizyApiRequest.call(
this,
'DELETE',
`/smart-lists/${listId}`,
);
responseData = { success: true };
}
if (operation === 'get') {
const listId = this.getNodeParameter('listId', i) as string;
responseData = await automizyApiRequest.call(
this,
'GET',
`/smart-lists/${listId}`,
);
}
if (operation === 'getAll') {
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
if (additionalFields.direction && additionalFields.sortBy) {
qs.order = `${additionalFields.sortBy}:${additionalFields.direction }`;
}
if (additionalFields.fields) {
qs.fields = additionalFields.fields;
}
if (returnAll) {
responseData = await automizyApiRequestAllItems.call(
this,
'smartLists',
'GET',
`/smart-lists`,
{},
qs,
);
} else {
qs.limit = this.getNodeParameter('limit', i) as number;
responseData = await automizyApiRequest.call(
this,
'GET',
`/smart-lists`,
{},
qs,
);
responseData = responseData.smartLists;
}
}
if (operation === 'update') {
const listId = this.getNodeParameter('listId', i) as string;
const name = this.getNodeParameter('name', i) as string;
const body: IDataObject = {
name,
};
responseData = await automizyApiRequest.call(
this,
'PATCH',
`/smart-lists/${listId}`,
body,
);
}
}
}
if (Array.isArray(responseData)) {
returnData.push.apply(returnData, responseData as IDataObject[]);
} else if (responseData !== undefined) {
returnData.push(responseData as IDataObject);
}
return [this.helpers.returnJsonArray(returnData)];
}
}

View file

@ -0,0 +1,197 @@
import {
INodeProperties,
} from 'n8n-workflow';
export const contactOperations = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'contact',
],
},
},
options: [
{
name: 'Delete',
value: 'delete',
description: 'Delete a contact',
},
{
name: 'Get',
value: 'get',
description: 'Get a contact',
},
{
name: 'Update',
value: 'update',
description: 'Update a contact',
},
],
default: 'get',
description: 'The operation to perform.',
},
] as INodeProperties[];
export const contactFields = [
/* -------------------------------------------------------------------------- */
/* contact:delete */
/* -------------------------------------------------------------------------- */
{
displayName: 'Contact ID',
name: 'contactId',
required: true,
type: 'string',
displayOptions: {
show: {
operation: [
'delete',
],
resource: [
'contact',
],
},
},
default: '',
description: 'Can be id or email'
},
/* -------------------------------------------------------------------------- */
/* contact:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'Contact ID',
name: 'contactId',
required: true,
type: 'string',
displayOptions: {
show: {
operation: [
'get',
],
resource: [
'contact',
],
},
},
default: '',
description: 'Can be id or email'
},
/* -------------------------------------------------------------------------- */
/* contact:update */
/* -------------------------------------------------------------------------- */
{
displayName: 'Email',
name: 'email',
required: true,
type: 'string',
displayOptions: {
show: {
operation: [
'update',
],
resource: [
'contact',
],
},
},
default: '',
},
{
displayName: 'Update Fields',
name: 'updateFields',
type: 'collection',
displayOptions: {
show: {
operation: [
'update',
],
resource: [
'contact',
],
},
},
default: {},
placeholder: 'Add Field',
options: [
{
displayName: 'Custom Fields',
name: 'customFieldsUi',
type: 'fixedCollection',
default: '',
placeholder: 'Add Custom Field',
typeOptions: {
multipleValues: true,
loadOptionsMethod: 'getCustomFields',
},
options: [
{
name: 'customFieldsValues',
displayName: 'Custom Field',
values: [
{
displayName: 'Key',
name: 'key',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCustomFields',
},
description: 'The end user specified key of the user defined data.',
default: '',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
description: 'The end user specified value of the user defined data.',
default: '',
},
],
},
],
},
{
displayName: 'Status',
name: 'status',
type: 'options',
options: [
{
name: 'Active',
value: 'ACTIVE',
},
{
name: 'Inactive',
value: 'INACTIVE',
},
{
name: 'Bounced',
value: 'BOUNCED',
},
{
name: 'Unsubscribed',
value: 'UNSUBSCRIBED',
},
{
name: 'Banned',
value: 'BANNED',
},
],
default: '',
description: 'The status of the contact. You can only send email to contacts with ACTIVE status.',
},
{
displayName: 'Tags',
name: 'tags',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getTags',
},
default: [],
description: 'The tags you want to set to the contact.',
},
],
},
] as INodeProperties[];

View file

@ -0,0 +1,278 @@
import {
INodeProperties,
} from 'n8n-workflow';
export const contactListOperations = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'contactList',
],
},
},
options: [
{
name: 'Add',
value: 'add',
description: 'Add a contact to a list',
},
{
name: 'Get All',
value: 'getAll',
description: 'Get all contacts on a list',
},
],
default: 'add',
description: 'The operation to perform.',
},
] as INodeProperties[];
export const contactListFields = [
/* -------------------------------------------------------------------------- */
/* contactList:add */
/* -------------------------------------------------------------------------- */
{
displayName: 'Email',
name: 'email',
required: true,
type: 'string',
displayOptions: {
show: {
operation: [
'add',
],
resource: [
'contactList',
],
},
},
default: '',
description: 'The email address of the contactList',
},
{
displayName: 'List ID',
name: 'listId',
required: true,
type: 'string',
displayOptions: {
show: {
operation: [
'add',
],
resource: [
'contactList',
],
},
},
default: '',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
displayOptions: {
show: {
operation: [
'add',
],
resource: [
'contactList',
],
},
},
default: {},
placeholder: 'Add Field',
options: [
{
displayName: 'Custom Fields',
name: 'customFieldsUi',
type: 'fixedCollection',
default: '',
placeholder: 'Add Custom Field',
typeOptions: {
multipleValues: true,
loadOptionsMethod: 'getCustomFields',
},
options: [
{
name: 'customFieldsValues',
displayName: 'Custom Field',
values: [
{
displayName: 'Key',
name: 'key',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCustomFields',
},
description: 'The end user specified key of the user defined data.',
default: '',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
description: 'The end user specified value of the user defined data.',
default: '',
},
],
},
],
},
{
displayName: 'Status',
name: 'status',
type: 'options',
options: [
{
name: 'Active',
value: 'ACTIVE',
},
{
name: 'Inactive',
value: 'INACTIVE',
},
{
name: 'Bounced',
value: 'BOUNCED',
},
{
name: 'Unsubscribed',
value: 'UNSUBSCRIBED',
},
{
name: 'Banned',
value: 'BANNED',
},
],
default: '',
description: 'The status of the contact. You can only send email to contacts with ACTIVE status.',
},
{
displayName: 'Tags',
name: 'tags',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getTags',
},
default: [],
description: 'The tags you want to set to the contact.',
},
],
},
/* -------------------------------------------------------------------------- */
/* contactList:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'List ID',
name: 'listId',
required: true,
type: 'string',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'contactList',
],
},
},
default: '',
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'contactList',
],
},
},
default: false,
description: 'If all results should be returned or only up to a given limit.',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'contactList',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 500,
},
default: 100,
description: 'How many results to return.',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'contactList',
],
},
},
default: {},
options: [
{
displayName: 'Direction',
name: 'direction',
type: 'options',
options: [
{
name: 'ASC',
value: 'asc',
},
{
name: 'DESC',
value: 'desc',
},
],
default: 'desc',
description: 'Defines the direction in which search results are ordered. Default value is DESC. Note: It has to be using with the Sort By parameter',
},
{
displayName: 'Fields',
name: 'fields',
type: 'string',
default: '',
description: 'A comma-separated list of attributes to include in the response.',
},
{
displayName: 'Sort By',
name: 'sortBy',
type: 'string',
default: 'Defines the field in which search results are sort by. Note: It has to be using with the Direcction parameter',
},
],
},
] as INodeProperties[];

View file

@ -0,0 +1,70 @@
import {
OptionsWithUri,
} from 'request';
import {
IExecuteFunctions,
IExecuteSingleFunctions,
ILoadOptionsFunctions,
} from 'n8n-core';
import {
IDataObject,
} from 'n8n-workflow';
export async function automizyApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, path: string, body: any = {}, qs: IDataObject = {}, option = {}): Promise<any> { // tslint:disable-line:no-any
const credentials = this.getCredentials('automizyApi') as IDataObject;
const options: OptionsWithUri = {
headers: {
'Authorization': `Bearer ${credentials.apiToken}`,
},
method,
body,
qs,
uri: `https://gateway.automizy.com/v2${path}`,
json: true,
};
try {
if (Object.keys(body).length === 0) {
delete options.body;
}
if (Object.keys(qs).length === 0) {
delete options.qs;
}
if (Object.keys(option).length !== 0) {
Object.assign(options, option);
}
//@ts-ignore
return await this.helpers.request.call(this, options);
} catch (error) {
if (error.response && error.response.body) {
throw new Error(
`Automizy error response [${error.statusCode}]: ${error.response.body.title}`
);
}
throw error;
}
}
export async function automizyApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string, method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
const returnData: IDataObject[] = [];
let responseData;
query.limit = 100;
query.page = 1;
do {
responseData = await automizyApiRequest.call(this, method, endpoint, body, query);
query.page++;
returnData.push.apply(returnData, responseData[propertyName]);
} while (
responseData.pageCount !== responseData.page
);
return returnData;
}

View file

@ -0,0 +1,241 @@
import {
INodeProperties,
} from 'n8n-workflow';
export const listOperations = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'list',
],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a list',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a list',
},
{
name: 'Get',
value: 'get',
description: 'Get a list',
},
{
name: 'Get All',
value: 'getAll',
description: 'Get all lists',
},
{
name: 'Update',
value: 'update',
description: 'Update a list',
},
],
default: 'create',
description: 'The operation to perform.',
},
] as INodeProperties[];
export const listFields = [
/* -------------------------------------------------------------------------- */
/* list:create */
/* -------------------------------------------------------------------------- */
{
displayName: 'Name',
name: 'name',
required: true,
type: 'string',
displayOptions: {
show: {
operation: [
'create',
],
resource: [
'list',
],
},
},
default: '',
},
/* -------------------------------------------------------------------------- */
/* list:delete */
/* -------------------------------------------------------------------------- */
{
displayName: 'List ID',
name: 'listId',
required: true,
type: 'string',
displayOptions: {
show: {
operation: [
'delete',
],
resource: [
'list',
],
},
},
default: '',
},
/* -------------------------------------------------------------------------- */
/* list:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'List ID',
name: 'listId',
required: true,
type: 'string',
displayOptions: {
show: {
operation: [
'get',
],
resource: [
'list',
],
},
},
default: '',
},
/* -------------------------------------------------------------------------- */
/* contact:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'list',
],
},
},
default: false,
description: 'If all results should be returned or only up to a given limit.',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'list',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 500,
},
default: 100,
description: 'How many results to return.',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'list',
],
},
},
default: {},
options: [
{
displayName: 'Direction',
name: 'direction',
type: 'options',
options: [
{
name: 'ASC',
value: 'asc',
},
{
name: 'DESC',
value: 'desc',
},
],
default: 'desc',
description: 'Defines the direction in which search results are ordered. Default value is DESC. Note: It has to be using with the Sort By parameter',
},
{
displayName: 'Fields',
name: 'fields',
type: 'string',
default: '',
description: 'A comma-separated list of attributes to include in the response.',
},
{
displayName: 'Sort By',
name: 'sortBy',
type: 'string',
default: 'Defines the field in which search results are sort by. Note: It has to be using with the Direcction parameter',
},
],
},
/* -------------------------------------------------------------------------- */
/* list:update */
/* -------------------------------------------------------------------------- */
{
displayName: 'List ID',
name: 'listId',
required: true,
type: 'string',
displayOptions: {
show: {
operation: [
'update',
],
resource: [
'list',
],
},
},
default: '',
},
{
displayName: 'Name',
name: 'name',
required: true,
type: 'string',
displayOptions: {
show: {
operation: [
'update',
],
resource: [
'list',
],
},
},
default: '',
},
] as INodeProperties[];

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View file

@ -34,6 +34,7 @@
"dist/credentials/Amqp.credentials.js",
"dist/credentials/AsanaApi.credentials.js",
"dist/credentials/AsanaOAuth2Api.credentials.js",
"dist/credentials/AutomizyApi.credentials.js",
"dist/credentials/Aws.credentials.js",
"dist/credentials/AffinityApi.credentials.js",
"dist/credentials/BannerbearApi.credentials.js",
@ -206,6 +207,7 @@
"dist/nodes/Asana/AsanaTrigger.node.js",
"dist/nodes/Affinity/Affinity.node.js",
"dist/nodes/Affinity/AffinityTrigger.node.js",
"dist/nodes/Automizy/Automizy.node.js",
"dist/nodes/Aws/AwsLambda.node.js",
"dist/nodes/Aws/Rekognition/AwsRekognition.node.js",
"dist/nodes/Aws/S3/AwsS3.node.js",