mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
node setup
This commit is contained in:
parent
976e02efcc
commit
9ac9ff3557
23
packages/nodes-base/credentials/AgileCrmApi.credentials.ts
Normal file
23
packages/nodes-base/credentials/AgileCrmApi.credentials.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import {
|
||||
ICredentialType,
|
||||
NodePropertyTypes,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export class AgileCrmApi implements ICredentialType {
|
||||
name = 'agileCrmApi';
|
||||
displayName = 'AgileCRM API';
|
||||
properties = [
|
||||
{
|
||||
displayName: 'Email',
|
||||
name: 'email',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'API Key',
|
||||
name: 'apiKey',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
},
|
||||
];
|
||||
}
|
148
packages/nodes-base/nodes/AgileCrm/AgileCrm.node.ts
Normal file
148
packages/nodes-base/nodes/AgileCrm/AgileCrm.node.ts
Normal file
|
@ -0,0 +1,148 @@
|
|||
import { IExecuteFunctions } from 'n8n-core';
|
||||
import {
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
IDataObject
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
contactOperations,
|
||||
contactFields
|
||||
} from './ContactDescription';
|
||||
import { agileCrmApiRequest, validateJSON} from './GenericFunctions';
|
||||
import { IContact, IProperty } from './ContactInterface';
|
||||
|
||||
|
||||
export class AgileCrm implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'AgileCRM',
|
||||
name: 'agileCrm',
|
||||
icon: 'file:agilecrm.png',
|
||||
group: ['transform'],
|
||||
version: 1,
|
||||
description: 'Consume AgileCRM API',
|
||||
defaults: {
|
||||
name: 'AgileCRM',
|
||||
color: '#772244',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
credentials: [
|
||||
{
|
||||
name: 'agileCrmApi',
|
||||
required: true,
|
||||
}
|
||||
],
|
||||
properties: [
|
||||
// Node properties which the user gets displayed and
|
||||
// can change on the node.
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Contact',
|
||||
value: 'contact'
|
||||
}
|
||||
],
|
||||
default: 'contact',
|
||||
description: 'Resource to consume.',
|
||||
},
|
||||
// CONTACT
|
||||
...contactOperations,
|
||||
...contactFields,
|
||||
],
|
||||
|
||||
};
|
||||
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
|
||||
const items = this.getInputData();
|
||||
const returnData: IDataObject[] = [];
|
||||
const length = items.length as unknown as number;
|
||||
let responseData;
|
||||
const qs: IDataObject = {};
|
||||
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 === 'contact'){
|
||||
|
||||
if(operation === 'get'){
|
||||
const contactId = this.getNodeParameter('contactId', i) as string;
|
||||
|
||||
const endpoint = `api/contacts/${contactId}`;
|
||||
responseData = await agileCrmApiRequest.call(this, 'GET', endpoint);
|
||||
|
||||
}
|
||||
|
||||
if(operation === 'getAll'){
|
||||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
|
||||
if (returnAll) {
|
||||
const endpoint = `api/contacts`;
|
||||
responseData = await agileCrmApiRequest.call(this, 'GET', endpoint);
|
||||
} else {
|
||||
const limit = this.getNodeParameter('limit', i) as number;
|
||||
const endpoint = `api/contacts?page_size=${limit}`;
|
||||
responseData = await agileCrmApiRequest.call(this, 'GET', endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
if(operation === 'create'){
|
||||
const jsonParameters = this.getNodeParameter('jsonParameters', i) as boolean;
|
||||
const body: IContact = {};
|
||||
|
||||
if (jsonParameters) {
|
||||
const additionalFieldsJson = this.getNodeParameter('additionalFieldsJson', i) as string;
|
||||
|
||||
if (additionalFieldsJson !== '' ) {
|
||||
|
||||
if (validateJSON(additionalFieldsJson) !== undefined) {
|
||||
|
||||
Object.assign(body, JSON.parse(additionalFieldsJson));
|
||||
|
||||
} else {
|
||||
throw new Error('Additional fields must be a valid JSON');
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||
|
||||
if (additionalFields.starValue) {
|
||||
body.star_value = additionalFields.starValue as string;
|
||||
}
|
||||
if (additionalFields.leadScore) {
|
||||
body.lead_score = additionalFields.leadScore as string;
|
||||
}
|
||||
if (additionalFields.tags) {
|
||||
body.tags = additionalFields.tags as string[];
|
||||
}
|
||||
if (additionalFields.properties) {
|
||||
body.properties = (additionalFields.properties as IDataObject).property as IDataObject[];
|
||||
}
|
||||
}
|
||||
const endpoint = 'api/contacts';
|
||||
console.log(body);
|
||||
responseData = await agileCrmApiRequest.call(this, 'POST', endpoint, body);
|
||||
}
|
||||
|
||||
if (Array.isArray(responseData)) {
|
||||
returnData.push.apply(returnData, responseData as IDataObject[]);
|
||||
} else {
|
||||
returnData.push(responseData as IDataObject);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return [this.helpers.returnJsonArray(returnData)];
|
||||
}
|
||||
|
||||
}
|
473
packages/nodes-base/nodes/AgileCrm/ContactDescription.ts
Normal file
473
packages/nodes-base/nodes/AgileCrm/ContactDescription.ts
Normal file
|
@ -0,0 +1,473 @@
|
|||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const contactOperations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get a contact',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
description: 'Get all contacts',
|
||||
},
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Create a new contact',
|
||||
},
|
||||
],
|
||||
default: 'get',
|
||||
description: 'The operation to perform.',
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const contactFields = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* contact:get */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Contact ID',
|
||||
name: 'contactId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'Unique identifier for a particular contact',
|
||||
},
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* contact:get all */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'If all results should be returned or only up to a given limit.',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* ticket:create */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
{
|
||||
displayName: 'JSON Parameters',
|
||||
name: 'jsonParameters',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: ' Additional Fields',
|
||||
name: 'additionalFieldsJson',
|
||||
type: 'json',
|
||||
typeOptions: {
|
||||
alwaysOpenEditWindow: true,
|
||||
},
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
jsonParameters: [
|
||||
true,
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
description: `Object of values to set as described <a href="https://github.com/agilecrm/rest-api#1-contacts---companies-api" target="_blank">here</a>.`,
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
jsonParameters: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Star Value',
|
||||
name: 'starValue',
|
||||
type: 'options',
|
||||
default: '',
|
||||
required: false,
|
||||
description: 'Rating of contact (Max value 5). This is not applicable for companies.',
|
||||
options: [
|
||||
{
|
||||
name: '0',
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
name: '1',
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
name: '2',
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
name: '3',
|
||||
value: 3
|
||||
},
|
||||
{
|
||||
name: '4',
|
||||
value: 4
|
||||
},
|
||||
{
|
||||
name: '5',
|
||||
value: 5
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
displayName: 'Lead Score',
|
||||
name: 'leadScore',
|
||||
type: 'number',
|
||||
default: '',
|
||||
description: 'Score of contact. This is not applicable for companies.',
|
||||
required: false,
|
||||
typeOptions: {
|
||||
minValue: 0
|
||||
}
|
||||
},
|
||||
{
|
||||
displayName: 'Tags',
|
||||
name: 'tags',
|
||||
type: 'string',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
multipleValueButtonText: 'Add Tag',
|
||||
},
|
||||
default: [],
|
||||
placeholder: 'Tag',
|
||||
description: 'Unique identifiers added to contact, for easy management of contacts. This is not applicable for companies.',
|
||||
},
|
||||
{
|
||||
displayName: 'Properties',
|
||||
name: 'properties',
|
||||
type: 'fixedCollection',
|
||||
default: {},
|
||||
description: 'Contact properties are represented by list of JSON objects, each JSON object should follow the prototype shown. Custom fields will have type as CUSTOM and others will have type as SYSTEM.',
|
||||
required: true,
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Property',
|
||||
name: 'property',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Type',
|
||||
name: 'type',
|
||||
type: 'options',
|
||||
default: 'SYSTEM',
|
||||
required: true,
|
||||
description: 'Type of the field.',
|
||||
options: [
|
||||
{
|
||||
name: 'SYSTEM',
|
||||
value: 'SYSTEM',
|
||||
},
|
||||
{
|
||||
name: 'CUSTOM',
|
||||
value: 'CUSTOM'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
description: 'Name of the field.'
|
||||
},
|
||||
{
|
||||
displayName: 'Sub Type',
|
||||
name: 'subType',
|
||||
default: '',
|
||||
required: false,
|
||||
type: 'options',
|
||||
description: 'Name of the field.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
type: [
|
||||
'SYSTEM'
|
||||
],
|
||||
name: [
|
||||
'email'
|
||||
]
|
||||
}
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Work',
|
||||
value: 'work',
|
||||
|
||||
},
|
||||
{
|
||||
name: 'Personal',
|
||||
value: 'personal',
|
||||
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
displayName: 'Sub Type',
|
||||
name: 'subType',
|
||||
default: '',
|
||||
required: false,
|
||||
type: 'options',
|
||||
description: 'Name of the field.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
type: [
|
||||
'SYSTEM'
|
||||
],
|
||||
name: [
|
||||
'phone'
|
||||
]
|
||||
}
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Work',
|
||||
value: 'work',
|
||||
|
||||
},
|
||||
{
|
||||
name: 'Home',
|
||||
value: 'home',
|
||||
},
|
||||
{
|
||||
name: 'Mobile',
|
||||
value: 'mobile',
|
||||
},
|
||||
{
|
||||
name: 'Main',
|
||||
value: 'main',
|
||||
},
|
||||
{
|
||||
name: 'Home Fax',
|
||||
value: 'homeFax',
|
||||
},
|
||||
{
|
||||
name: 'Work Fax',
|
||||
value: 'workFax',
|
||||
},
|
||||
{
|
||||
name: 'Other',
|
||||
value: 'other',
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
displayName: 'Sub Type',
|
||||
name: 'subType',
|
||||
default: '',
|
||||
required: false,
|
||||
type: 'options',
|
||||
description: 'Name of the field.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
type: [
|
||||
'SYSTEM'
|
||||
],
|
||||
name: [
|
||||
'address'
|
||||
]
|
||||
}
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Home',
|
||||
value: 'home',
|
||||
},
|
||||
{
|
||||
name: 'Postal',
|
||||
value: 'postal',
|
||||
},
|
||||
{
|
||||
name: 'Office',
|
||||
value: 'office',
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
displayName: 'Sub Type',
|
||||
name: 'subType',
|
||||
default: '',
|
||||
required: false,
|
||||
type: 'options',
|
||||
description: 'Name of the field.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
type: [
|
||||
'SYSTEM'
|
||||
],
|
||||
name: [
|
||||
'website'
|
||||
]
|
||||
}
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'URL',
|
||||
value: 'url',
|
||||
},
|
||||
{
|
||||
name: 'SKYPE',
|
||||
value: 'skype',
|
||||
},
|
||||
{
|
||||
name: 'TWITTER',
|
||||
value: 'twitter',
|
||||
},
|
||||
{
|
||||
name: 'LINKEDIN',
|
||||
value: 'linkedin',
|
||||
},
|
||||
{
|
||||
name: 'FACEBOOK',
|
||||
value: 'facebook',
|
||||
},
|
||||
{
|
||||
name: 'XING',
|
||||
value: 'xing',
|
||||
},
|
||||
{
|
||||
name: 'FEED',
|
||||
value: 'feed',
|
||||
},
|
||||
{
|
||||
name: 'GOOGLE_PLUS',
|
||||
value: 'googlePlus',
|
||||
},
|
||||
{
|
||||
name: 'FLICKR',
|
||||
value: 'flickr',
|
||||
},
|
||||
{
|
||||
name: 'GITHUB',
|
||||
value: 'github',
|
||||
},
|
||||
{
|
||||
name: 'YOUTUBE',
|
||||
value: 'youtube',
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
displayName: 'Sub Type',
|
||||
name: 'subType',
|
||||
default: '',
|
||||
required: false,
|
||||
type: 'string',
|
||||
description: 'Name of the field.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
type: [
|
||||
'CUSTOM'
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
default: '',
|
||||
required: false,
|
||||
type: 'string',
|
||||
description: 'Value of the property.'
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
]
|
||||
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
] as INodeProperties[];
|
18
packages/nodes-base/nodes/AgileCrm/ContactInterface.ts
Normal file
18
packages/nodes-base/nodes/AgileCrm/ContactInterface.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import {
|
||||
IDataObject,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export interface IProperty {
|
||||
type: string;
|
||||
name: string;
|
||||
subtype?: string;
|
||||
value?: string;
|
||||
}
|
||||
|
||||
export interface IContact {
|
||||
star_value?: string;
|
||||
lead_score?: string;
|
||||
tags?: string[];
|
||||
properties?: IDataObject[];
|
||||
}
|
||||
|
59
packages/nodes-base/nodes/AgileCrm/GenericFunctions.ts
Normal file
59
packages/nodes-base/nodes/AgileCrm/GenericFunctions.ts
Normal file
|
@ -0,0 +1,59 @@
|
|||
import {
|
||||
OptionsWithUri,
|
||||
} from 'request';
|
||||
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
IHookFunctions,
|
||||
ILoadOptionsFunctions,
|
||||
IExecuteSingleFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
|
||||
export async function agileCrmApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: any = {}, query: IDataObject = {}, uri?: string): Promise<any> {
|
||||
|
||||
const node = this.getNodeParameter('credentials', 1);
|
||||
const credentials = this.getCredentials('agileCrmApi');
|
||||
|
||||
const options: OptionsWithUri = {
|
||||
method,
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
body: body! || {},
|
||||
auth: {
|
||||
username: credentials!.email as string,
|
||||
password: credentials!.apiKey as string
|
||||
},
|
||||
uri: uri || `https://n8nio.agilecrm.com/dev/${endpoint}`,
|
||||
json: true
|
||||
};
|
||||
|
||||
|
||||
try {
|
||||
return await this.helpers.request!(options);
|
||||
} catch (error) {
|
||||
|
||||
if (error.response && error.response.body && error.response.body.errors) {
|
||||
const errorMessages = error.response.body.errors.map((e: IDataObject) => e.message);
|
||||
throw new Error(`AgileCRM error response [${error.statusCode}]: ${errorMessages.join(' | ')}`);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function validateJSON(json: string | undefined): any { // tslint:disable-line:no-any
|
||||
let result;
|
||||
try {
|
||||
result = JSON.parse(json!);
|
||||
} catch (exception) {
|
||||
result = undefined;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
BIN
packages/nodes-base/nodes/AgileCrm/agilecrm.png
Normal file
BIN
packages/nodes-base/nodes/AgileCrm/agilecrm.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
|
@ -27,6 +27,7 @@
|
|||
"n8n": {
|
||||
"credentials": [
|
||||
"dist/credentials/ActiveCampaignApi.credentials.js",
|
||||
"dist/credentials/AgileCrm.credentials.js",
|
||||
"dist/credentials/AcuitySchedulingApi.credentials.js",
|
||||
"dist/credentials/AirtableApi.credentials.js",
|
||||
"dist/credentials/Amqp.credentials.js",
|
||||
|
@ -113,6 +114,7 @@
|
|||
"nodes": [
|
||||
"dist/nodes/ActiveCampaign/ActiveCampaign.node.js",
|
||||
"dist/nodes/ActiveCampaign/ActiveCampaignTrigger.node.js",
|
||||
"dist/nodes/AgileCrm/AgileCrm.node.js",
|
||||
"dist/nodes/Airtable/Airtable.node.js",
|
||||
"dist/nodes/AcuityScheduling/AcuitySchedulingTrigger.node.js",
|
||||
"dist/nodes/Amqp/Amqp.node.js",
|
||||
|
|
Loading…
Reference in a new issue