mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 20:24:05 -08:00
⚡ Add search filters to contacts and companies (AgileCRM) (#2373)
* Added search options for the AgileCRM node * Adjusting AgileCRM getAll operation (using Magento 2 node as a reference) * ⚡ Small improvements to #2238 Co-authored-by: Valentina <valentina.lilova98@gmail.com>
This commit is contained in:
parent
15d05c7f01
commit
3e1fb3e0c9
|
@ -1,4 +1,7 @@
|
||||||
import { IExecuteFunctions } from 'n8n-core';
|
import {
|
||||||
|
IExecuteFunctions,
|
||||||
|
} from 'n8n-core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
IDataObject,
|
IDataObject,
|
||||||
INodeExecutionData,
|
INodeExecutionData,
|
||||||
|
@ -22,16 +25,34 @@ import {
|
||||||
dealOperations
|
dealOperations
|
||||||
} from './DealDescription';
|
} from './DealDescription';
|
||||||
|
|
||||||
import { IContact, IContactUpdate } from './ContactInterface';
|
import {
|
||||||
import { agileCrmApiRequest, agileCrmApiRequestUpdate, validateJSON } from './GenericFunctions';
|
IContact,
|
||||||
import { IDeal } from './DealInterface';
|
IContactUpdate,
|
||||||
|
} from './ContactInterface';
|
||||||
|
|
||||||
|
import {
|
||||||
|
agileCrmApiRequest, agileCrmApiRequestAllItems,
|
||||||
|
agileCrmApiRequestUpdate,
|
||||||
|
getFilterRules,
|
||||||
|
simplifyResponse,
|
||||||
|
validateJSON,
|
||||||
|
} from './GenericFunctions';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IDeal,
|
||||||
|
} from './DealInterface';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IFilter,
|
||||||
|
ISearchConditions,
|
||||||
|
} from './FilterInterface';
|
||||||
|
|
||||||
export class AgileCrm implements INodeType {
|
export class AgileCrm implements INodeType {
|
||||||
description: INodeTypeDescription = {
|
description: INodeTypeDescription = {
|
||||||
displayName: 'Agile CRM',
|
displayName: 'Agile CRM',
|
||||||
name: 'agileCrm',
|
name: 'agileCrm',
|
||||||
icon: 'file:agilecrm.png',
|
icon: 'file:agilecrm.png',
|
||||||
|
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||||
group: ['transform'],
|
group: ['transform'],
|
||||||
version: 1,
|
version: 1,
|
||||||
description: 'Consume Agile CRM API',
|
description: 'Consume Agile CRM API',
|
||||||
|
@ -86,7 +107,6 @@ export class AgileCrm implements INodeType {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||||
|
|
||||||
const items = this.getInputData();
|
const items = this.getInputData();
|
||||||
|
@ -113,26 +133,59 @@ export class AgileCrm implements INodeType {
|
||||||
responseData = await agileCrmApiRequest.call(this, 'DELETE', endpoint, {});
|
responseData = await agileCrmApiRequest.call(this, 'DELETE', endpoint, {});
|
||||||
|
|
||||||
} else if (operation === 'getAll') {
|
} else if (operation === 'getAll') {
|
||||||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
const simple = this.getNodeParameter('simple', 0) as boolean;
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', 0) as boolean;
|
||||||
|
const filterType = this.getNodeParameter('filterType', i) as string;
|
||||||
|
const sort = this.getNodeParameter('options.sort.sort', i, {}) as { direction: string, field: string };
|
||||||
|
const body: IDataObject = {};
|
||||||
|
const filterJson: IFilter = {};
|
||||||
|
|
||||||
|
let contactType = '';
|
||||||
if (resource === 'contact') {
|
if (resource === 'contact') {
|
||||||
if (returnAll) {
|
contactType = 'PERSON';
|
||||||
const endpoint = 'api/contacts';
|
|
||||||
responseData = await agileCrmApiRequest.call(this, 'GET', endpoint, {});
|
|
||||||
} else {
|
} else {
|
||||||
const limit = this.getNodeParameter('limit', i) as number;
|
contactType = 'COMPANY';
|
||||||
const endpoint = `api/contacts?page_size=${limit}`;
|
|
||||||
responseData = await agileCrmApiRequest.call(this, 'GET', endpoint, {});
|
|
||||||
}
|
}
|
||||||
|
filterJson.contact_type = contactType;
|
||||||
|
|
||||||
|
if (filterType === 'manual') {
|
||||||
|
const conditions = this.getNodeParameter('filters.conditions', i, []) as ISearchConditions[];
|
||||||
|
const matchType = this.getNodeParameter('matchType', i) as string;
|
||||||
|
let rules;
|
||||||
|
if (conditions.length !== 0) {
|
||||||
|
rules = getFilterRules(conditions, matchType);
|
||||||
|
Object.assign(filterJson, rules);
|
||||||
} else {
|
} else {
|
||||||
if (returnAll) {
|
throw new NodeOperationError(this.getNode(), 'At least one condition must be added.');
|
||||||
const endpoint = 'api/contacts/companies/list';
|
|
||||||
responseData = await agileCrmApiRequest.call(this, 'POST', endpoint, {});
|
|
||||||
} else {
|
|
||||||
const limit = this.getNodeParameter('limit', i) as number;
|
|
||||||
const endpoint = `api/contacts/companies/list?page_size=${limit}`;
|
|
||||||
responseData = await agileCrmApiRequest.call(this, 'POST', endpoint, {});
|
|
||||||
}
|
}
|
||||||
|
} else if (filterType === 'json') {
|
||||||
|
const filterJsonRules = this.getNodeParameter('filterJson', i) as string;
|
||||||
|
if (validateJSON(filterJsonRules) !== undefined) {
|
||||||
|
Object.assign(filterJson, JSON.parse(filterJsonRules) as IFilter);
|
||||||
|
} else {
|
||||||
|
throw new NodeOperationError(this.getNode(), 'Filter (JSON) must be a valid json');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
body.filterJson = JSON.stringify(filterJson);
|
||||||
|
|
||||||
|
if (sort) {
|
||||||
|
if (sort.direction === 'ASC') {
|
||||||
|
body.global_sort_key = sort.field;
|
||||||
|
} else if (sort.direction === 'DESC') {
|
||||||
|
body.global_sort_key = `-${sort.field}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returnAll) {
|
||||||
|
body.page_size = 100;
|
||||||
|
responseData = await agileCrmApiRequestAllItems.call(this, 'POST', `api/filters/filter/dynamic-filter`, body, undefined, undefined, true);
|
||||||
|
} else {
|
||||||
|
body.page_size = this.getNodeParameter('limit', 0) as number;
|
||||||
|
responseData = await agileCrmApiRequest.call(this, 'POST', `api/filters/filter/dynamic-filter`, body, undefined, undefined, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (simple) {
|
||||||
|
responseData = simplifyResponse(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (operation === 'create') {
|
} else if (operation === 'create') {
|
||||||
|
@ -461,15 +514,15 @@ export class AgileCrm implements INodeType {
|
||||||
responseData = await agileCrmApiRequest.call(this, 'DELETE', endpoint, {});
|
responseData = await agileCrmApiRequest.call(this, 'DELETE', endpoint, {});
|
||||||
|
|
||||||
} else if (operation === 'getAll') {
|
} else if (operation === 'getAll') {
|
||||||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
const returnAll = this.getNodeParameter('returnAll', 0) as boolean;
|
||||||
|
const endpoint = 'api/opportunity';
|
||||||
|
|
||||||
if (returnAll) {
|
if (returnAll) {
|
||||||
const endpoint = 'api/opportunity';
|
const limit = 100;
|
||||||
responseData = await agileCrmApiRequest.call(this, 'GET', endpoint, {});
|
responseData = await agileCrmApiRequestAllItems.call(this, 'GET', endpoint, undefined, { page_size: limit });
|
||||||
} else {
|
} else {
|
||||||
const limit = this.getNodeParameter('limit', i) as number;
|
const limit = this.getNodeParameter('limit', 0) as number;
|
||||||
const endpoint = `api/opportunity?page_size=${limit}`;
|
responseData = await agileCrmApiRequest.call(this, 'GET', endpoint, undefined, { page_size: limit });
|
||||||
responseData = await agileCrmApiRequest.call(this, 'GET', endpoint, {});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (operation === 'create') {
|
} else if (operation === 'create') {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import {
|
import {
|
||||||
INodeProperties,
|
INodeProperties,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
export const companyOperations = [
|
export const companyOperations = [
|
||||||
{
|
{
|
||||||
displayName: 'Operation',
|
displayName: 'Operation',
|
||||||
|
@ -44,6 +45,7 @@ export const companyOperations = [
|
||||||
description: 'The operation to perform.',
|
description: 'The operation to perform.',
|
||||||
},
|
},
|
||||||
] as INodeProperties[];
|
] as INodeProperties[];
|
||||||
|
|
||||||
export const companyFields = [
|
export const companyFields = [
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* company:get */
|
/* company:get */
|
||||||
|
@ -91,7 +93,6 @@ export const companyFields = [
|
||||||
displayName: 'Limit',
|
displayName: 'Limit',
|
||||||
name: 'limit',
|
name: 'limit',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
default: 20,
|
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
resource: [
|
resource: [
|
||||||
|
@ -105,7 +106,280 @@ export const companyFields = [
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
default: 20,
|
||||||
|
description: 'Number of results to fetch.',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
displayName: 'Filter',
|
||||||
|
name: 'filterType',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'None',
|
||||||
|
value: 'none',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Build Manually',
|
||||||
|
value: 'manual',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'JSON',
|
||||||
|
value: 'json',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'company',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: 'none',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Must Match',
|
||||||
|
name: 'matchType',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Any filter',
|
||||||
|
value: 'anyFilter',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'All Filters',
|
||||||
|
value: 'allFilters',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'company',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
filterType: [
|
||||||
|
'manual',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: 'anyFilter',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Simplify Response',
|
||||||
|
name: 'simple',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'company',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: false,
|
||||||
|
description: 'Return a simplified version of the response instead of the raw data.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Filters',
|
||||||
|
name: 'filters',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'company',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
filterType: [
|
||||||
|
'manual',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
placeholder: 'Add Condition',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Conditions',
|
||||||
|
name: 'conditions',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Field',
|
||||||
|
name: 'field',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'Any searchable field.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Condition Type',
|
||||||
|
name: 'condition_type',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Equals',
|
||||||
|
value: 'EQUALS',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Not Equal',
|
||||||
|
value: 'NOTEQUALS',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Last',
|
||||||
|
value: 'LAST',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Between',
|
||||||
|
value: 'BETWEEN',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'On',
|
||||||
|
value: 'ON',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Before',
|
||||||
|
value: 'BEFORE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'After',
|
||||||
|
value: 'AFTER',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'EQUALS',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Value',
|
||||||
|
name: 'value',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Value 2',
|
||||||
|
name: 'value2',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
condition_type: [
|
||||||
|
'BETWEEN',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'See <a href="https://github.com/agilecrm/rest-api#121-get-contacts-by-dynamic-filter" target="_blank">Agile CRM guide</a> to creating filters',
|
||||||
|
name: 'jsonNotice',
|
||||||
|
type: 'notice',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'company',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
filterType: [
|
||||||
|
'json',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Filters (JSON)',
|
||||||
|
name: 'filterJson',
|
||||||
|
type: 'string',
|
||||||
|
typeOptions: {
|
||||||
|
alwaysOpenEditWindow: true,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'company',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
filterType: [
|
||||||
|
'json',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Options',
|
||||||
|
name: 'options',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Option',
|
||||||
|
default: {},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'company',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Sort',
|
||||||
|
name: 'sort',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
placeholder: 'Add Sort',
|
||||||
|
default: [],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Sort',
|
||||||
|
name: 'sort',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Direction',
|
||||||
|
name: 'direction',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Ascending',
|
||||||
|
value: 'ASC',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Descending',
|
||||||
|
value: 'DESC',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'ASC',
|
||||||
|
description: 'The sorting direction',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Field',
|
||||||
|
name: 'field',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: `The sorting field`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* company:create */
|
/* company:create */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
|
@ -657,4 +931,5 @@ export const companyFields = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
] as INodeProperties[];
|
] as INodeProperties[];
|
||||||
|
|
|
@ -71,25 +71,7 @@ export const contactFields = [
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* contact:get all */
|
/* contact:get all */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
|
||||||
displayName: 'Limit',
|
|
||||||
name: 'limit',
|
|
||||||
type: 'number',
|
|
||||||
default: 20,
|
|
||||||
displayOptions: {
|
|
||||||
show: {
|
|
||||||
resource: [
|
|
||||||
'contact',
|
|
||||||
],
|
|
||||||
operation: [
|
|
||||||
'getAll',
|
|
||||||
],
|
|
||||||
returnAll: [
|
|
||||||
false,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
displayName: 'Return All',
|
displayName: 'Return All',
|
||||||
name: 'returnAll',
|
name: 'returnAll',
|
||||||
|
@ -107,6 +89,296 @@ export const contactFields = [
|
||||||
default: false,
|
default: false,
|
||||||
description: 'If all results should be returned or only up to a given limit.',
|
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,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: 20,
|
||||||
|
description: 'Number of results to fetch.',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
displayName: 'Filter',
|
||||||
|
name: 'filterType',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'None',
|
||||||
|
value: 'none',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Build Manually',
|
||||||
|
value: 'manual',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'JSON',
|
||||||
|
value: 'json',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'contact',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: 'none',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Must Match',
|
||||||
|
name: 'matchType',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Any filter',
|
||||||
|
value: 'anyFilter',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'All Filters',
|
||||||
|
value: 'allFilters',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'contact',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
filterType: [
|
||||||
|
'manual',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: 'anyFilter',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Simplify Response',
|
||||||
|
name: 'simple',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'contact',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: false,
|
||||||
|
description: 'Return a simplified version of the response instead of the raw data.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Filters',
|
||||||
|
name: 'filters',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'contact',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
filterType: [
|
||||||
|
'manual',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
placeholder: 'Add Condition',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Conditions',
|
||||||
|
name: 'conditions',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Field',
|
||||||
|
name: 'field',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'Any searchable field.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Condition Type',
|
||||||
|
name: 'condition_type',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Equals',
|
||||||
|
value: 'EQUALS',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Not Equal',
|
||||||
|
value: 'NOTEQUALS',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Last',
|
||||||
|
value: 'LAST',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Between',
|
||||||
|
value: 'BETWEEN',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'On',
|
||||||
|
value: 'ON',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Before',
|
||||||
|
value: 'BEFORE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'After',
|
||||||
|
value: 'AFTER',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'EQUALS',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Value',
|
||||||
|
name: 'value',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Value 2',
|
||||||
|
name: 'value2',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
condition_type: [
|
||||||
|
'BETWEEN',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'See <a href="https://github.com/agilecrm/rest-api#121-get-contacts-by-dynamic-filter" target="_blank">Agile CRM guide</a> to creating filters',
|
||||||
|
name: 'jsonNotice',
|
||||||
|
type: 'notice',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'contact',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
filterType: [
|
||||||
|
'json',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Filters (JSON)',
|
||||||
|
name: 'filterJson',
|
||||||
|
type: 'string',
|
||||||
|
typeOptions: {
|
||||||
|
alwaysOpenEditWindow: true,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'contact',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
filterType: [
|
||||||
|
'json',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Options',
|
||||||
|
name: 'options',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Option',
|
||||||
|
default: {},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'contact',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Sort',
|
||||||
|
name: 'sort',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
placeholder: 'Add Sort',
|
||||||
|
default: [],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Sort',
|
||||||
|
name: 'sort',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Direction',
|
||||||
|
name: 'direction',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Ascending',
|
||||||
|
value: 'ASC',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Descending',
|
||||||
|
value: 'DESC',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'ASC',
|
||||||
|
description: 'The sorting direction',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Field',
|
||||||
|
name: 'field',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: `The sorting field`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* contact:create */
|
/* contact:create */
|
||||||
|
@ -988,4 +1260,5 @@ export const contactFields = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
] as INodeProperties[];
|
] as INodeProperties[];
|
||||||
|
|
|
@ -110,8 +110,6 @@ export const dealFields = [
|
||||||
default: false,
|
default: false,
|
||||||
description: 'If all results should be returned or only up to a given limit.',
|
description: 'If all results should be returned or only up to a given limit.',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* deal:create */
|
/* deal:create */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
19
packages/nodes-base/nodes/AgileCrm/FilterInterface.ts
Normal file
19
packages/nodes-base/nodes/AgileCrm/FilterInterface.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
export interface ISearchConditions {
|
||||||
|
field?: string;
|
||||||
|
condition_type?: string;
|
||||||
|
value?: string;
|
||||||
|
value2?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IFilterRules {
|
||||||
|
LHS?: string;
|
||||||
|
CONDITION?: string;
|
||||||
|
RHS?: string;
|
||||||
|
RHS_NEW?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IFilter {
|
||||||
|
or_rules?: IFilterRules;
|
||||||
|
rules?: IFilterRules;
|
||||||
|
contact_type?: string;
|
||||||
|
}
|
|
@ -1,6 +1,4 @@
|
||||||
import {
|
import { OptionsWithUri } from 'request';
|
||||||
OptionsWithUri
|
|
||||||
} from 'request';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
IExecuteFunctions,
|
IExecuteFunctions,
|
||||||
|
@ -10,12 +8,21 @@ import {
|
||||||
} from 'n8n-core';
|
} from 'n8n-core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
IDataObject, NodeApiError,
|
IDataObject,
|
||||||
|
NodeApiError,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { IContactUpdate } from './ContactInterface';
|
|
||||||
|
|
||||||
|
import {
|
||||||
|
IContactUpdate,
|
||||||
|
} from './ContactInterface';
|
||||||
|
|
||||||
export async function agileCrmApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: any = {}, query: IDataObject = {}, uri?: string): Promise<any> { // tslint:disable-line:no-any
|
import {
|
||||||
|
IFilterRules,
|
||||||
|
ISearchConditions,
|
||||||
|
} from './FilterInterface';
|
||||||
|
|
||||||
|
export async function agileCrmApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
|
||||||
|
method: string, endpoint: string, body: any = {}, query: IDataObject = {}, uri?: string, sendAsForm?: boolean): Promise<any> { // tslint:disable-line:no-any
|
||||||
|
|
||||||
const credentials = await this.getCredentials('agileCrmApi');
|
const credentials = await this.getCredentials('agileCrmApi');
|
||||||
const options: OptionsWithUri = {
|
const options: OptionsWithUri = {
|
||||||
|
@ -27,12 +34,18 @@ export async function agileCrmApiRequest(this: IHookFunctions | IExecuteFunction
|
||||||
username: credentials!.email as string,
|
username: credentials!.email as string,
|
||||||
password: credentials!.apiKey as string,
|
password: credentials!.apiKey as string,
|
||||||
},
|
},
|
||||||
|
qs: query,
|
||||||
uri: uri || `https://${credentials!.subdomain}.agilecrm.com/dev/${endpoint}`,
|
uri: uri || `https://${credentials!.subdomain}.agilecrm.com/dev/${endpoint}`,
|
||||||
json: true,
|
json: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// To send the request as 'content-type': 'application/x-www-form-urlencoded' add form to options instead of body
|
||||||
|
if(sendAsForm) {
|
||||||
|
options.form = body;
|
||||||
|
}
|
||||||
// Only add Body property if method not GET or DELETE to avoid 400 response
|
// Only add Body property if method not GET or DELETE to avoid 400 response
|
||||||
if (method !== 'GET' && method !== 'DELETE') {
|
// And when not sending a form
|
||||||
|
else if (method !== 'GET' && method !== 'DELETE') {
|
||||||
options.body = body;
|
options.body = body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +54,30 @@ export async function agileCrmApiRequest(this: IHookFunctions | IExecuteFunction
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new NodeApiError(this.getNode(), error);
|
throw new NodeApiError(this.getNode(), error);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function agileCrmApiRequestAllItems(this: IHookFunctions | ILoadOptionsFunctions | IExecuteFunctions,
|
||||||
|
method: string, resource: string, body: any = {}, query: IDataObject = {}, uri?: string, sendAsForm?: boolean): Promise<any> { // tslint:disable-line:no-any
|
||||||
|
// https://github.com/agilecrm/rest-api#11-listing-contacts-
|
||||||
|
|
||||||
|
const returnData: IDataObject[] = [];
|
||||||
|
let responseData;
|
||||||
|
do {
|
||||||
|
responseData = await agileCrmApiRequest.call(this, method, resource, body, query, uri, sendAsForm);
|
||||||
|
if (responseData.length !== 0) {
|
||||||
|
returnData.push.apply(returnData, responseData);
|
||||||
|
if (sendAsForm) {
|
||||||
|
body.cursor = responseData[responseData.length-1].cursor;
|
||||||
|
} else {
|
||||||
|
query.cursor = responseData[responseData.length-1].cursor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (
|
||||||
|
responseData.length !== 0 &&
|
||||||
|
responseData[responseData.length-1].hasOwnProperty('cursor')
|
||||||
|
);
|
||||||
|
|
||||||
|
return returnData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function agileCrmApiRequestUpdate(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method = 'PUT', endpoint?: string, body: any = {}, query: IDataObject = {}, uri?: string): Promise<any> { // tslint:disable-line:no-any
|
export async function agileCrmApiRequestUpdate(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method = 'PUT', endpoint?: string, body: any = {}, query: IDataObject = {}, uri?: string): Promise<any> { // tslint:disable-line:no-any
|
||||||
|
@ -131,3 +167,39 @@ export function validateJSON(json: string | undefined): any { // tslint:disable-
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getFilterRules(conditions: ISearchConditions[], matchType: string): IDataObject { // tslint:disable-line:no-any
|
||||||
|
const rules = [];
|
||||||
|
|
||||||
|
for (const key in conditions) {
|
||||||
|
if (conditions.hasOwnProperty(key)) {
|
||||||
|
const searchConditions: ISearchConditions = conditions[key] as ISearchConditions;
|
||||||
|
const rule: IFilterRules = {
|
||||||
|
LHS: searchConditions.field,
|
||||||
|
CONDITION: searchConditions.condition_type,
|
||||||
|
RHS: searchConditions.value as string,
|
||||||
|
RHS_NEW: searchConditions.value2 as string,
|
||||||
|
};
|
||||||
|
rules.push(rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchType === 'anyFilter') {
|
||||||
|
return {
|
||||||
|
or_rules: rules,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return {
|
||||||
|
rules,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function simplifyResponse(records: [{ id: string, properties: [{ name: string, value: string }] } ]) {
|
||||||
|
const results = [];
|
||||||
|
for (const record of records) {
|
||||||
|
results.push(record.properties.reduce((obj, value) => Object.assign(obj, { [`${value.name}`]: value.value }), { id: record.id }));
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue