From 119989bc3760b9a8b1a1e57bfa25c23e657e647a Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Tue, 26 Oct 2021 11:32:54 -0500 Subject: [PATCH 1/6] :zap: Add support for moment types to If Node --- packages/nodes-base/nodes/If.node.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/nodes-base/nodes/If.node.ts b/packages/nodes-base/nodes/If.node.ts index 2a2a3bd0bd..5a89f07a10 100644 --- a/packages/nodes-base/nodes/If.node.ts +++ b/packages/nodes-base/nodes/If.node.ts @@ -1,3 +1,4 @@ +import moment = require('moment'); import { IExecuteFunctions } from 'n8n-core'; import { INodeExecutionData, @@ -333,6 +334,8 @@ export class If implements INodeType { returnValue = new Date(value).getTime(); } else if (typeof value === 'number') { returnValue = value; + } if (moment.isMoment(value)) { + returnValue = value.unix(); } if ((value as unknown as object) instanceof Date) { returnValue = (value as unknown as Date).getTime(); } From dc642419df5780aa6adcccf7760a697ccf6d51f7 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Tue, 26 Oct 2021 11:32:33 -0500 Subject: [PATCH 2/6] :zap: Make sure that DateTime Node always returns strings --- packages/nodes-base/nodes/DateTime.node.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/DateTime.node.ts b/packages/nodes-base/nodes/DateTime.node.ts index df586997fd..89e9def2c4 100644 --- a/packages/nodes-base/nodes/DateTime.node.ts +++ b/packages/nodes-base/nodes/DateTime.node.ts @@ -496,7 +496,7 @@ export class DateTime implements INodeType { newItem.binary = item.binary; } - set(newItem, `json.${dataPropertyName}`, newDate); + set(newItem, `json.${dataPropertyName}`, newDate.toISOString()); returnData.push(newItem); } From db738fc824ef695af6b522a86091a9594d7e9e90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Wed, 27 Oct 2021 01:54:03 +0200 Subject: [PATCH 3/6] :zap: Add workflow name and ID to settings (#2369) * :zap: Add workflow name and ID to settings * :hammer: Refactor to use mapGetters --- packages/editor-ui/src/components/WorkflowSettings.vue | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/editor-ui/src/components/WorkflowSettings.vue b/packages/editor-ui/src/components/WorkflowSettings.vue index 77e929b177..04c7d218dc 100644 --- a/packages/editor-ui/src/components/WorkflowSettings.vue +++ b/packages/editor-ui/src/components/WorkflowSettings.vue @@ -3,7 +3,7 @@ :name="WORKFLOW_SETTINGS_MODAL_KEY" width="65%" maxHeight="80%" - title="Workflow Settings" + :title="`Settings for ${workflowName} (#${workflowId})`" :eventBus="modalBus" :scrollable="true" > @@ -191,6 +191,8 @@ import { WORKFLOW_SETTINGS_MODAL_KEY } from '../constants'; import mixins from 'vue-typed-mixins'; +import { mapGetters } from "vuex"; + export default mixins( externalHooks, genericHelpers, @@ -235,6 +237,11 @@ export default mixins( WORKFLOW_SETTINGS_MODAL_KEY, }; }, + + computed: { + ...mapGetters(['workflowName', 'workflowId']), + }, + async mounted () { if (this.$route.params.name === undefined) { this.$showMessage({ From 15d05c7f0130f6ed2cf3c454d34d18f9cf963477 Mon Sep 17 00:00:00 2001 From: Omar Ajoue Date: Wed, 27 Oct 2021 01:58:08 +0200 Subject: [PATCH 4/6] :zap: Fixed ignore response code flag to work properly with return full response (#2370) --- packages/core/src/NodeExecuteFunctions.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index 9aa1cf3e8d..6ef54f37ac 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -438,17 +438,17 @@ async function proxyRequestToAxios( } }) .catch((error) => { - if (configObject.simple === true && error.response) { - resolve({ - body: error.response.data, - headers: error.response.headers, - statusCode: error.response.status, - statusMessage: error.response.statusText, - }); - return; - } if (configObject.simple === false && error.response) { - resolve(error.response.data); + if (configObject.resolveWithFullResponse) { + resolve({ + body: error.response.data, + headers: error.response.headers, + statusCode: error.response.status, + statusMessage: error.response.statusText, + }); + } else { + resolve(error.response.data); + } return; } From 3e1fb3e0c9f7d2588ef64fc77b0ac90159e39b3e Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Tue, 26 Oct 2021 23:45:26 -0400 Subject: [PATCH 5/6] :zap: 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) * :zap: Small improvements to #2238 Co-authored-by: Valentina --- .../nodes/AgileCrm/AgileCrm.node.ts | 105 ++++-- .../nodes/AgileCrm/CompanyDescription.ts | 277 +++++++++++++++- .../nodes/AgileCrm/ContactDescription.ts | 311 ++++++++++++++++-- .../nodes/AgileCrm/DealDescription.ts | 2 - .../nodes/AgileCrm/FilterInterface.ts | 19 ++ .../nodes/AgileCrm/GenericFunctions.ts | 86 ++++- 6 files changed, 745 insertions(+), 55 deletions(-) create mode 100644 packages/nodes-base/nodes/AgileCrm/FilterInterface.ts diff --git a/packages/nodes-base/nodes/AgileCrm/AgileCrm.node.ts b/packages/nodes-base/nodes/AgileCrm/AgileCrm.node.ts index c3401f7d86..a31fdb2164 100644 --- a/packages/nodes-base/nodes/AgileCrm/AgileCrm.node.ts +++ b/packages/nodes-base/nodes/AgileCrm/AgileCrm.node.ts @@ -1,4 +1,7 @@ -import { IExecuteFunctions } from 'n8n-core'; +import { + IExecuteFunctions, +} from 'n8n-core'; + import { IDataObject, INodeExecutionData, @@ -22,16 +25,34 @@ import { dealOperations } from './DealDescription'; -import { IContact, IContactUpdate } from './ContactInterface'; -import { agileCrmApiRequest, agileCrmApiRequestUpdate, validateJSON } from './GenericFunctions'; -import { IDeal } from './DealInterface'; +import { + IContact, + 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 { description: INodeTypeDescription = { displayName: 'Agile CRM', name: 'agileCrm', icon: 'file:agilecrm.png', + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', group: ['transform'], version: 1, description: 'Consume Agile CRM API', @@ -86,7 +107,6 @@ export class AgileCrm implements INodeType { }; - async execute(this: IExecuteFunctions): Promise { const items = this.getInputData(); @@ -113,26 +133,59 @@ export class AgileCrm implements INodeType { responseData = await agileCrmApiRequest.call(this, 'DELETE', endpoint, {}); } 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 (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, {}); - } + contactType = 'PERSON'; } else { - if (returnAll) { - const endpoint = 'api/contacts/companies/list'; - responseData = await agileCrmApiRequest.call(this, 'POST', endpoint, {}); + contactType = 'COMPANY'; + } + 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 { - const limit = this.getNodeParameter('limit', i) as number; - const endpoint = `api/contacts/companies/list?page_size=${limit}`; - responseData = await agileCrmApiRequest.call(this, 'POST', endpoint, {}); + throw new NodeOperationError(this.getNode(), 'At least one condition must be added.'); } + } 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') { @@ -461,15 +514,15 @@ export class AgileCrm implements INodeType { responseData = await agileCrmApiRequest.call(this, 'DELETE', endpoint, {}); } 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) { - const endpoint = 'api/opportunity'; - responseData = await agileCrmApiRequest.call(this, 'GET', endpoint, {}); + const limit = 100; + responseData = await agileCrmApiRequestAllItems.call(this, 'GET', endpoint, undefined, { page_size: limit }); } else { - const limit = this.getNodeParameter('limit', i) as number; - const endpoint = `api/opportunity?page_size=${limit}`; - responseData = await agileCrmApiRequest.call(this, 'GET', endpoint, {}); + const limit = this.getNodeParameter('limit', 0) as number; + responseData = await agileCrmApiRequest.call(this, 'GET', endpoint, undefined, { page_size: limit }); } } else if (operation === 'create') { diff --git a/packages/nodes-base/nodes/AgileCrm/CompanyDescription.ts b/packages/nodes-base/nodes/AgileCrm/CompanyDescription.ts index da5eeef238..6b888cf4ee 100644 --- a/packages/nodes-base/nodes/AgileCrm/CompanyDescription.ts +++ b/packages/nodes-base/nodes/AgileCrm/CompanyDescription.ts @@ -1,6 +1,7 @@ import { INodeProperties, } from 'n8n-workflow'; + export const companyOperations = [ { displayName: 'Operation', @@ -44,6 +45,7 @@ export const companyOperations = [ description: 'The operation to perform.', }, ] as INodeProperties[]; + export const companyFields = [ /* -------------------------------------------------------------------------- */ /* company:get */ @@ -91,7 +93,6 @@ export const companyFields = [ displayName: 'Limit', name: 'limit', type: 'number', - default: 20, displayOptions: { show: { 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 Agile CRM guide 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 */ /* -------------------------------------------------------------------------- */ @@ -657,4 +931,5 @@ export const companyFields = [ }, ], }, + ] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/AgileCrm/ContactDescription.ts b/packages/nodes-base/nodes/AgileCrm/ContactDescription.ts index 82cd03afe1..3ccf9b1cb9 100644 --- a/packages/nodes-base/nodes/AgileCrm/ContactDescription.ts +++ b/packages/nodes-base/nodes/AgileCrm/ContactDescription.ts @@ -71,25 +71,7 @@ export const contactFields = [ /* -------------------------------------------------------------------------- */ /* contact:get all */ /* -------------------------------------------------------------------------- */ - { - displayName: 'Limit', - name: 'limit', - type: 'number', - default: 20, - displayOptions: { - show: { - resource: [ - 'contact', - ], - operation: [ - 'getAll', - ], - returnAll: [ - false, - ], - }, - }, - }, + { displayName: 'Return All', name: 'returnAll', @@ -107,6 +89,296 @@ export const contactFields = [ 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, + ], + }, + }, + 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 Agile CRM guide 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 */ @@ -988,4 +1260,5 @@ export const contactFields = [ }, ], }, + ] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/AgileCrm/DealDescription.ts b/packages/nodes-base/nodes/AgileCrm/DealDescription.ts index 7fb7f55e5f..7dd93643e0 100644 --- a/packages/nodes-base/nodes/AgileCrm/DealDescription.ts +++ b/packages/nodes-base/nodes/AgileCrm/DealDescription.ts @@ -110,8 +110,6 @@ export const dealFields = [ default: false, description: 'If all results should be returned or only up to a given limit.', }, - - /* -------------------------------------------------------------------------- */ /* deal:create */ /* -------------------------------------------------------------------------- */ diff --git a/packages/nodes-base/nodes/AgileCrm/FilterInterface.ts b/packages/nodes-base/nodes/AgileCrm/FilterInterface.ts new file mode 100644 index 0000000000..001dea948a --- /dev/null +++ b/packages/nodes-base/nodes/AgileCrm/FilterInterface.ts @@ -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; +} diff --git a/packages/nodes-base/nodes/AgileCrm/GenericFunctions.ts b/packages/nodes-base/nodes/AgileCrm/GenericFunctions.ts index 0143b0f624..6c01d3c0b8 100644 --- a/packages/nodes-base/nodes/AgileCrm/GenericFunctions.ts +++ b/packages/nodes-base/nodes/AgileCrm/GenericFunctions.ts @@ -1,6 +1,4 @@ -import { - OptionsWithUri -} from 'request'; +import { OptionsWithUri } from 'request'; import { IExecuteFunctions, @@ -10,12 +8,21 @@ import { } from 'n8n-core'; import { - IDataObject, NodeApiError, + IDataObject, + NodeApiError, } 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 { // 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 { // tslint:disable-line:no-any const credentials = await this.getCredentials('agileCrmApi'); const options: OptionsWithUri = { @@ -27,12 +34,18 @@ export async function agileCrmApiRequest(this: IHookFunctions | IExecuteFunction username: credentials!.email as string, password: credentials!.apiKey as string, }, + qs: query, uri: uri || `https://${credentials!.subdomain}.agilecrm.com/dev/${endpoint}`, 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 - if (method !== 'GET' && method !== 'DELETE') { + // And when not sending a form + else if (method !== 'GET' && method !== 'DELETE') { options.body = body; } @@ -41,7 +54,30 @@ export async function agileCrmApiRequest(this: IHookFunctions | IExecuteFunction } catch (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 { // 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 { // tslint:disable-line:no-any @@ -131,3 +167,39 @@ export function validateJSON(json: string | undefined): any { // tslint:disable- } 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; +} From a00961ac886269837b25be5d09b2eb3f7dee7e61 Mon Sep 17 00:00:00 2001 From: Mutasem Date: Wed, 27 Oct 2021 17:42:08 +0200 Subject: [PATCH 6/6] lock version --- packages/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 2ab8191e77..0e449778ef 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -83,7 +83,7 @@ "dependencies": { "@oclif/command": "^1.5.18", "@oclif/errors": "^1.2.2", - "@rudderstack/rudder-sdk-node": "^1.0.2", + "@rudderstack/rudder-sdk-node": "1.0.4", "@types/json-diff": "^0.5.1", "@types/jsonwebtoken": "^8.5.2", "basic-auth": "^2.0.1",