From 60165d5fe2e7bd2c2b518b4ff70efb7dd7ad08a2 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Tue, 22 Oct 2019 11:30:35 +0200 Subject: [PATCH] :zap: Allow to automatically encode custom values with Pipedrive update --- .../nodes/Pipedrive/GenericFunctions.ts | 99 ++++++++++++++----- .../nodes/Pipedrive/Pipedrive.node.ts | 54 +++++++++- 2 files changed, 124 insertions(+), 29 deletions(-) diff --git a/packages/nodes-base/nodes/Pipedrive/GenericFunctions.ts b/packages/nodes-base/nodes/Pipedrive/GenericFunctions.ts index fb8112dab8..32e8194359 100644 --- a/packages/nodes-base/nodes/Pipedrive/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Pipedrive/GenericFunctions.ts @@ -12,12 +12,18 @@ import { OptionsWithUri } from 'request'; export interface ICustomInterface { name: string; + key: string; options?: Array<{ id: number; label: string; }>; } +export interface ICustomProperties { + [key: string]: ICustomInterface; +} + + /** * Make an API request to Pipedrive * @@ -141,15 +147,14 @@ export async function pipedriveApiRequestAllItems(this: IHookFunctions | IExecut /** - * Converts names and values of custom properties to their actual values + * Gets the custom properties from Pipedrive * * @export * @param {(IHookFunctions | IExecuteFunctions)} this * @param {string} resource - * @param {IDataObject[]} items - * @returns {Promise} + * @returns {Promise} */ -export async function pipedriveResolveCustomProperties(this: IHookFunctions | IExecuteFunctions, resource: string, items: IDataObject[]): Promise { +export async function pipedriveGetCustomProperties(this: IHookFunctions | IExecuteFunctions, resource: string): Promise { const endpoints: { [key: string]: string } = { 'activity': '/activityFields', @@ -170,37 +175,83 @@ export async function pipedriveResolveCustomProperties(this: IHookFunctions | IE // Get the custom properties and their values const responseData = await pipedriveApiRequest.call(this, requestMethod, endpoints[resource], body, qs); - const customProperties: { - [key: string]: ICustomInterface; - } = {}; + const customProperties: ICustomProperties = {}; for (const customPropertyData of responseData.data) { customProperties[customPropertyData.key] = customPropertyData; } + return customProperties; +} + + + +/** + * Converts names and values of custom properties from their actual values to the + * Pipedrive internal ones + * + * @export + * @param {ICustomProperties} customProperties + * @param {IDataObject} item + */ +export function pipedriveEncodeCustomProperties(customProperties: ICustomProperties, item: IDataObject): void { let customPropertyData; - for (const item of items) { - // Itterate over all keys and replace the custom ones - for (const key of Object.keys(item)) { - if (customProperties[key] !== undefined) { - // Is a custom property - customPropertyData = customProperties[key]; + for (const key of Object.keys(item)) { + customPropertyData = Object.values(customProperties).find(customPropertyData => customPropertyData.name === key); - // Check if also the value has to be resolved or just the key - if (item[key] !== null && item[key] !== undefined && customPropertyData.options !== undefined && Array.isArray(customPropertyData.options)) { - // Has an option key so get the actual option-value - const propertyOption = customPropertyData.options.find(option => option.id.toString() === item[key]!.toString()); + if (customPropertyData !== undefined) { + // Is a custom property - if (propertyOption !== undefined) { - item[customPropertyData.name as string] = propertyOption.label; - delete item[key]; - } - } else { - // Does already represent the actual value or is null - item[customPropertyData.name as string] = item[key]; + // Check if also the value has to be resolved or just the key + if (item[key] !== null && item[key] !== undefined && customPropertyData.options !== undefined && Array.isArray(customPropertyData.options)) { + // Has an option key so get the actual option-value + const propertyOption = customPropertyData.options.find(option => option.label.toString() === item[key]!.toString()); + + if (propertyOption !== undefined) { + item[customPropertyData.key as string] = propertyOption.id; delete item[key]; } + } else { + // Does already represent the actual value or is null + item[customPropertyData.key as string] = item[key]; + delete item[key]; + } + } + } +} + + + +/** + * Converts names and values of custom properties to their actual values + * + * @export + * @param {ICustomProperties} customProperties + * @param {IDataObject} item + */ +export function pipedriveResolveCustomProperties(customProperties: ICustomProperties, item: IDataObject): void { + let customPropertyData; + + // Itterate over all keys and replace the custom ones + for (const key of Object.keys(item)) { + if (customProperties[key] !== undefined) { + // Is a custom property + customPropertyData = customProperties[key]; + + // Check if also the value has to be resolved or just the key + if (item[key] !== null && item[key] !== undefined && customPropertyData.options !== undefined && Array.isArray(customPropertyData.options)) { + // Has an option key so get the actual option-value + const propertyOption = customPropertyData.options.find(option => option.id.toString() === item[key]!.toString()); + + if (propertyOption !== undefined) { + item[customPropertyData.name as string] = propertyOption.label; + delete item[key]; + } + } else { + // Does already represent the actual value or is null + item[customPropertyData.name as string] = item[key]; + delete item[key]; } } } diff --git a/packages/nodes-base/nodes/Pipedrive/Pipedrive.node.ts b/packages/nodes-base/nodes/Pipedrive/Pipedrive.node.ts index 18ba2062c3..3f4d4da5fe 100644 --- a/packages/nodes-base/nodes/Pipedrive/Pipedrive.node.ts +++ b/packages/nodes-base/nodes/Pipedrive/Pipedrive.node.ts @@ -10,8 +10,11 @@ import { } from 'n8n-workflow'; import { + ICustomProperties, pipedriveApiRequest, pipedriveApiRequestAllItems, + pipedriveEncodeCustomProperties, + pipedriveGetCustomProperties, pipedriveResolveCustomProperties, } from './GenericFunctions'; @@ -1960,6 +1963,27 @@ export class Pipedrive implements INodeType { default: false, description: 'By default do custom properties get returned only as ID instead of their actual name. Also option fields contain only the ID instead of their actual value. If this option gets set they get automatically resolved.', }, + { + displayName: 'Encode Properties', + name: 'encodeProperties', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'activity', + 'deal', + 'organization', + 'person', + 'product', + ], + operation: [ + 'update', + ], + }, + }, + default: false, + description: 'By default do custom properties have to be set as ID instead of their actual name. Also option fields have to be set as ID instead of their actual value. If this option gets set they get automatically encoded.', + }, { displayName: 'Return All', name: 'returnAll', @@ -2020,6 +2044,23 @@ export class Pipedrive implements INodeType { const resource = this.getNodeParameter('resource', 0) as string; const operation = this.getNodeParameter('operation', 0) as string; + let customProperties: ICustomProperties | undefined; + if (['get', 'getAll', 'update'].includes(operation) && ['activity', 'deal', 'organization', 'person', 'product'].includes(resource)) { + // Request the custom properties once in the beginning to not query it multiple + // times if multiple items get updated + + let getCustomProperties = false; + if (['update'].includes(operation)) { + getCustomProperties = this.getNodeParameter('encodeProperties', 0, false) as boolean; + } else { + getCustomProperties = this.getNodeParameter('resolveProperties', 0, false) as boolean; + } + + if (getCustomProperties === true) { + customProperties = await pipedriveGetCustomProperties.call(this, resource); + } + } + for (let i = 0; i < items.length; i++) { requestMethod = 'GET'; @@ -2415,6 +2456,11 @@ export class Pipedrive implements INodeType { if (returnAll === true) { responseData = await pipedriveApiRequestAllItems.call(this, requestMethod, endpoint, body, qs); } else { + + if (customProperties !== undefined) { + pipedriveEncodeCustomProperties(customProperties!, body); + } + responseData = await pipedriveApiRequest.call(this, requestMethod, endpoint, body, qs, formData, downloadFile); } @@ -2445,11 +2491,9 @@ export class Pipedrive implements INodeType { } } - if (['get', 'getAll'].includes(operation) && ['activity', 'deal', 'organization', 'person', 'product'].includes(resource)) { - const resolveProperties = this.getNodeParameter('resolveProperties', 0) as boolean; - - if (resolveProperties === true) { - await pipedriveResolveCustomProperties.call(this, resource, returnData); + if (customProperties !== undefined) { + for (const item of returnData) { + await pipedriveResolveCustomProperties(customProperties, item); } }