diff --git a/packages/nodes-base/nodes/Salesforce/AccountDescription.ts b/packages/nodes-base/nodes/Salesforce/AccountDescription.ts index 0944711f4e..09ff764c8e 100644 --- a/packages/nodes-base/nodes/Salesforce/AccountDescription.ts +++ b/packages/nodes-base/nodes/Salesforce/AccountDescription.ts @@ -1,4 +1,6 @@ -import { INodeProperties } from 'n8n-workflow'; +import { + INodeProperties, +} from 'n8n-workflow'; export const accountOperations = [ { @@ -149,6 +151,42 @@ export const accountFields = [ default: '', description: 'Street address for the billing address of this account.', }, + { + displayName: 'Custom Fields', + name: 'customFieldsUi', + placeholder: 'Add Custom Field', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + description: 'Filter by custom fields ', + default: {}, + options: [ + { + name: 'customFieldsValues', + displayName: 'Custom Field', + values: [ + { + displayName: 'Field ID', + name: 'fieldId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getLeadCustomFields', + }, + default: '', + description: 'The ID of the field to add custom field to.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + description: 'The value to set on custom field.', + }, + ], + }, + ], + }, { displayName: 'Description', name: 'description', @@ -367,6 +405,42 @@ export const accountFields = [ default: '', description: 'Street address for the billing address of this account.', }, + { + displayName: 'Custom Fields', + name: 'customFieldsUi', + placeholder: 'Add Custom Field', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + description: 'Filter by custom fields ', + default: {}, + options: [ + { + name: 'customFieldsValues', + displayName: 'Custom Field', + values: [ + { + displayName: 'Field ID', + name: 'fieldId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getLeadCustomFields', + }, + default: '', + description: 'The ID of the field to add custom field to.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + description: 'The value to set on custom field.', + }, + ], + }, + ], + }, { displayName: 'Description', name: 'description', diff --git a/packages/nodes-base/nodes/Salesforce/ContactDescription.ts b/packages/nodes-base/nodes/Salesforce/ContactDescription.ts index 741e9c3841..432a023719 100644 --- a/packages/nodes-base/nodes/Salesforce/ContactDescription.ts +++ b/packages/nodes-base/nodes/Salesforce/ContactDescription.ts @@ -1,4 +1,6 @@ -import { INodeProperties } from 'n8n-workflow'; +import { + INodeProperties, +} from 'n8n-workflow'; export const contactOperations = [ { @@ -62,7 +64,7 @@ export const contactOperations = [ export const contactFields = [ /* -------------------------------------------------------------------------- */ -/* contact:create */ +/* contact:create */ /* -------------------------------------------------------------------------- */ { displayName: 'Last Name', @@ -130,6 +132,42 @@ export const contactFields = [ default: '', description: 'The birth date of the contact.', }, + { + displayName: 'Custom Fields', + name: 'customFieldsUi', + placeholder: 'Add Custom Field', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + description: 'Filter by custom fields ', + default: {}, + options: [ + { + name: 'customFieldsValues', + displayName: 'Custom Field', + values: [ + { + displayName: 'Field ID', + name: 'fieldId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getLeadCustomFields', + }, + default: '', + description: 'The ID of the field to add custom field to.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + description: 'The value to set on custom field.', + }, + ], + }, + ], + }, { displayName: 'Department', name: 'department', @@ -314,7 +352,7 @@ export const contactFields = [ ], }, /* -------------------------------------------------------------------------- */ -/* contact:update */ +/* contact:update */ /* -------------------------------------------------------------------------- */ { displayName: 'Contact ID', @@ -382,6 +420,42 @@ export const contactFields = [ default: '', description: 'The birth date of the contact.', }, + { + displayName: 'Custom Fields', + name: 'customFieldsUi', + placeholder: 'Add Custom Field', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + description: 'Filter by custom fields ', + default: {}, + options: [ + { + name: 'customFieldsValues', + displayName: 'Custom Field', + values: [ + { + displayName: 'Field ID', + name: 'fieldId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getLeadCustomFields', + }, + default: '', + description: 'The ID of the field to add custom field to.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + description: 'The value to set on custom field.', + }, + ], + }, + ], + }, { displayName: 'Department', name: 'department', diff --git a/packages/nodes-base/nodes/Salesforce/CustomObjectDescription.ts b/packages/nodes-base/nodes/Salesforce/CustomObjectDescription.ts new file mode 100644 index 0000000000..921b6c5aac --- /dev/null +++ b/packages/nodes-base/nodes/Salesforce/CustomObjectDescription.ts @@ -0,0 +1,396 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const customObjectOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'customObject', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a custom object record', + }, + { + name: 'Get', + value: 'get', + description: 'Get a custom object record', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all custom object records', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a custom object record', + }, + { + name: 'Update', + value: 'update', + description: 'Update a custom object record', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const customObjectFields = [ + +/* -------------------------------------------------------------------------- */ +/* customObject:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Custom Object', + name: 'customObject', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCustomObjects', + }, + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'customObject', + ], + operation: [ + 'create', + ], + }, + }, + description: 'Name of the custom object', + }, + { + displayName: 'Fields', + name: 'customFieldsUi', + placeholder: 'Add Field', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + displayOptions: { + show: { + resource: [ + 'customObject', + ], + operation: [ + 'create', + ], + }, + }, + description: 'Filter by custom fields ', + default: {}, + options: [ + { + name: 'customFieldsValues', + displayName: 'Custom Field', + values: [ + { + displayName: 'Field ID', + name: 'fieldId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCustomObjectFields', + loadOptionsDependsOn: [ + 'customObject', + ], + }, + default: '', + description: 'The ID of the field.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + description: 'The value to set on custom field.', + }, + ], + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* customObject:update */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Custom Object', + name: 'customObject', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCustomObjects', + }, + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'customObject', + ], + operation: [ + 'update', + ], + }, + }, + description: 'Name of the custom object', + }, + { + displayName: 'Record ID', + name: 'recordId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'customObject', + ], + operation: [ + 'update', + ], + }, + }, + description: 'Record id to be updated', + }, + { + displayName: 'Fields', + name: 'customFieldsUi', + placeholder: 'Add Field', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + description: 'Filter by custom fields ', + default: {}, + displayOptions: { + show: { + resource: [ + 'customObject', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + name: 'customFieldsValues', + displayName: 'Custom Field', + values: [ + { + displayName: 'Field ID', + name: 'fieldId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCustomObjectFields', + loadOptionsDependsOn: [ + 'customObject', + ], + }, + default: '', + description: 'The ID of the field.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + description: 'The value to set on custom field.', + }, + ], + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* customObject:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Custom Object', + name: 'customObject', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCustomObjects', + }, + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'customObject', + ], + operation: [ + 'get', + ], + }, + }, + description: 'Name of the custom object', + }, + { + displayName: 'Record ID', + name: 'recordId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'customObject', + ], + operation: [ + 'get', + ], + }, + }, + description: 'Record id to be retrieved', + }, +/* -------------------------------------------------------------------------- */ +/* customObject:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Custom Object', + name: 'customObject', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCustomObjects', + }, + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'customObject', + ], + operation: [ + 'delete', + ], + }, + }, + description: 'Name of the custom object', + }, + { + displayName: 'Record ID', + name: 'recordId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'customObject', + ], + operation: [ + 'delete', + ], + }, + }, + description: 'Record id to be deleted', + }, +/* -------------------------------------------------------------------------- */ +/* customObject:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Custom Object', + name: 'customObject', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCustomObjects', + }, + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'customObject', + ], + operation: [ + 'getAll', + ], + }, + }, + description: 'Name of the custom object', + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'customObject', + ], + 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: [ + 'customObject', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 100, + }, + default: 50, + description: 'How many results to return.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'customObject', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Fields', + name: 'fields', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getCustomObjectFields', + loadOptionsDependsOn: [ + 'customObject', + ], + }, + default: '', + description: 'Fields to include separated by ,', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Salesforce/GenericFunctions.ts b/packages/nodes-base/nodes/Salesforce/GenericFunctions.ts index 3c76dfc03a..fee3421842 100644 --- a/packages/nodes-base/nodes/Salesforce/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Salesforce/GenericFunctions.ts @@ -17,7 +17,7 @@ export async function salesforceApiRequest(this: IExecuteFunctions | IExecuteSin const subdomain = ((credentials!.accessTokenUrl as string).match(/https:\/\/(.+).salesforce\.com/) || [])[1]; const options: OptionsWithUri = { method, - body: method === "GET" ? undefined : body, + body: method === 'GET' ? undefined : body, qs, uri: `https://${subdomain}.salesforce.com/services/data/v39.0${uri || endpoint}`, json: true diff --git a/packages/nodes-base/nodes/Salesforce/LeadDescription.ts b/packages/nodes-base/nodes/Salesforce/LeadDescription.ts index 6c3c22c39e..11dfa70e82 100644 --- a/packages/nodes-base/nodes/Salesforce/LeadDescription.ts +++ b/packages/nodes-base/nodes/Salesforce/LeadDescription.ts @@ -1,4 +1,6 @@ -import { INodeProperties } from 'n8n-workflow'; +import { + INodeProperties, +} from 'n8n-workflow'; export const leadOperations = [ { @@ -77,7 +79,7 @@ export const leadFields = [ ], operation: [ 'create', - ] + ], }, }, description: 'Company of the lead. If person account record types have been enabled, and if the value of Company is null, the lead converts to a person account.', @@ -95,7 +97,7 @@ export const leadFields = [ ], operation: [ 'create', - ] + ], }, }, description: 'Required. Last name of the lead. Limited to 80 characters.', @@ -135,6 +137,42 @@ export const leadFields = [ default: '', description: 'City for the address of the lead.', }, + { + displayName: 'Custom Fields', + name: 'customFieldsUi', + placeholder: 'Add Custom Field', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + description: 'Filter by custom fields ', + default: {}, + options: [ + { + name: 'customFieldsValues', + displayName: 'Custom Field', + values: [ + { + displayName: 'Field ID', + name: 'fieldId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getLeadCustomFields', + }, + default: '', + description: 'The ID of the field to add custom field to.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + description: 'The value to set on custom field.', + }, + ], + }, + ], + }, { displayName: 'Description', name: 'description', @@ -342,6 +380,42 @@ export const leadFields = [ default: '', description: 'Company of the lead. If person account record types have been enabled, and if the value of Company is null, the lead converts to a person account.', }, + { + displayName: 'Custom Fields', + name: 'customFieldsUi', + placeholder: 'Add Custom Field', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + description: 'Filter by custom fields ', + default: {}, + options: [ + { + name: 'customFieldsValues', + displayName: 'Custom Field', + values: [ + { + displayName: 'Field ID', + name: 'fieldId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getLeadCustomFields', + }, + default: '', + description: 'The ID of the field to add custom field to.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + description: 'The value to set on custom field.', + }, + ], + }, + ], + }, { displayName: 'Description', name: 'description', diff --git a/packages/nodes-base/nodes/Salesforce/OpportunityDescription.ts b/packages/nodes-base/nodes/Salesforce/OpportunityDescription.ts index 2d0f3b1d25..b53ca58ebe 100644 --- a/packages/nodes-base/nodes/Salesforce/OpportunityDescription.ts +++ b/packages/nodes-base/nodes/Salesforce/OpportunityDescription.ts @@ -1,4 +1,6 @@ -import { INodeProperties } from 'n8n-workflow'; +import { + INodeProperties, +} from 'n8n-workflow'; export const opportunityOperations = [ { @@ -163,6 +165,42 @@ export const opportunityFields = [ default: '', description: 'Id of the campaign that needs to be fetched', }, + { + displayName: 'Custom Fields', + name: 'customFieldsUi', + placeholder: 'Add Custom Field', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + description: 'Filter by custom fields ', + default: {}, + options: [ + { + name: 'customFieldsValues', + displayName: 'Custom Field', + values: [ + { + displayName: 'Field ID', + name: 'fieldId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getLeadCustomFields', + }, + default: '', + description: 'The ID of the field to add custom field to.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + description: 'The value to set on custom field.', + }, + ], + }, + ], + }, { displayName: 'Description', name: 'description', @@ -322,6 +360,42 @@ export const opportunityFields = [ default: '', description: 'Required. Date when the opportunity is expected to close.', }, + { + displayName: 'Custom Fields', + name: 'customFieldsUi', + placeholder: 'Add Custom Field', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + description: 'Filter by custom fields ', + default: {}, + options: [ + { + name: 'customFieldsValues', + displayName: 'Custom Field', + values: [ + { + displayName: 'Field ID', + name: 'fieldId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getLeadCustomFields', + }, + default: '', + description: 'The ID of the field to add custom field to.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + description: 'The value to set on custom field.', + }, + ], + }, + ], + }, { displayName: 'Description', name: 'description', diff --git a/packages/nodes-base/nodes/Salesforce/Salesforce.node.ts b/packages/nodes-base/nodes/Salesforce/Salesforce.node.ts index 8f6873f6ef..51d8c2351c 100644 --- a/packages/nodes-base/nodes/Salesforce/Salesforce.node.ts +++ b/packages/nodes-base/nodes/Salesforce/Salesforce.node.ts @@ -52,6 +52,11 @@ import { IContact, } from './ContactInterface'; +import { + customObjectFields, + customObjectOperations, +} from './CustomObjectDescription'; + import { flowFields, flowOperations, @@ -149,6 +154,11 @@ export class Salesforce implements INodeType { value: 'contact', description: 'Represents a contact, which is an individual associated with an account.', }, + { + name: 'Custom Object', + value: 'customObject', + description: 'Represents a custom object.', + }, { name: 'Flow', value: 'flow', @@ -157,7 +167,7 @@ export class Salesforce implements INodeType { { name: 'Lead', value: 'lead', - description: 'Represents a prospect or potential .', + description: 'Represents a prospect or potential.', }, { name: 'Opportunity', @@ -182,6 +192,8 @@ export class Salesforce implements INodeType { ...leadFields, ...contactOperations, ...contactFields, + ...customObjectOperations, + ...customObjectFields, ...opportunityOperations, ...opportunityFields, ...accountOperations, @@ -243,6 +255,13 @@ export class Salesforce implements INodeType { const returnData: INodePropertyOptions[] = []; // TODO: find a way to filter this object to get just the lead sources instead of the whole object const { fields } = await salesforceApiRequest.call(this, 'GET', '/sobjects/lead/describe'); + + for (const aja of fields as IDataObject[]) { + if (aja.custom === true) { + console.log(aja); + } + } + for (const field of fields) { if (field.name === 'LeadSource') { for (const pickValue of field.picklistValues) { @@ -257,6 +276,25 @@ export class Salesforce implements INodeType { } return returnData; }, + // Get all the lead custom fields to display them to user so that he can + // select them easily + async getLeadCustomFields(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + // TODO: find a way to filter this object to get just the lead sources instead of the whole object + const { fields } = await salesforceApiRequest.call(this, 'GET', '/sobjects/lead/describe'); + + for (const field of fields) { + if (field.custom === true) { + const fieldName = field.label; + const fieldId = field.name; + returnData.push({ + name: fieldName, + value: fieldId, + }); + } + } + return returnData; + }, // Get all the accounts to display them to user so that he can // select them easily async getAccounts(this: ILoadOptionsFunctions): Promise { @@ -573,6 +611,43 @@ export class Salesforce implements INodeType { } return returnData; }, + + // Get all the custom objects recurrence instances to display them to user so that he can + // select them easily + async getCustomObjects(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + // TODO: find a way to filter this object to get just the lead sources instead of the whole object + const { sobjects: objects } = await salesforceApiRequest.call(this, 'GET', '/sobjects'); + for (const object of objects) { + if (object.custom === true) { + const objectName = object.label; + const objectId = object.name; + returnData.push({ + name: objectName, + value: objectId, + }); + } + } + return returnData; + }, + + // Get all the custom objects fields recurrence instances to display them to user so that he can + // select them easily + async getCustomObjectFields(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + // TODO: find a way to filter this object to get just the lead sources instead of the whole object + const customObject = this.getCurrentNodeParameter('customObject') as string; + const { fields } = await salesforceApiRequest.call(this, 'GET', `/sobjects/${customObject}/describe`); + for (const field of fields) { + const fieldName = field.label; + const fieldId = field.name; + returnData.push({ + name: fieldName, + value: fieldId, + }); + } + return returnData; + }, }, }; @@ -658,6 +733,16 @@ export class Salesforce implements INodeType { if (additionalFields.numberOfEmployees !== undefined) { body.NumberOfEmployees = additionalFields.numberOfEmployees as number; } + if (additionalFields.customFieldsUi) { + const customFields = (additionalFields.customFieldsUi as IDataObject).customFieldsValues as IDataObject[]; + if (customFields) { + for (const customField of customFields) { + //@ts-ignore + body[customField.fieldId] = customField.value; + } + } + } + responseData = await salesforceApiRequest.call(this, 'POST', '/sobjects/lead', body); } //https://developer.salesforce.com/docs/api-explorer/sobject/Lead/patch-lead-id @@ -737,6 +822,15 @@ export class Salesforce implements INodeType { if (updateFields.numberOfEmployees !== undefined) { body.NumberOfEmployees = updateFields.numberOfEmployees as number; } + if (updateFields.customFieldsUi) { + const customFields = (updateFields.customFieldsUi as IDataObject).customFieldsValues as IDataObject[]; + if (customFields) { + for (const customField of customFields) { + //@ts-ignore + body[customField.fieldId] = customField.value; + } + } + } responseData = await salesforceApiRequest.call(this, 'PATCH', `/sobjects/lead/${leadId}`, body); } //https://developer.salesforce.com/docs/api-explorer/sobject/Lead/get-lead-id @@ -912,6 +1006,15 @@ export class Salesforce implements INodeType { if (additionalFields.emailBouncedReason !== undefined) { body.EmailBouncedReason = additionalFields.emailBouncedReason as string; } + if (additionalFields.customFieldsUi) { + const customFields = (additionalFields.customFieldsUi as IDataObject).customFieldsValues as IDataObject[]; + if (customFields) { + for (const customField of customFields) { + //@ts-ignore + body[customField.fieldId] = customField.value; + } + } + } responseData = await salesforceApiRequest.call(this, 'POST', '/sobjects/contact', body); } //https://developer.salesforce.com/docs/api-explorer/sobject/Contact/patch-contact-id @@ -1012,6 +1115,15 @@ export class Salesforce implements INodeType { if (updateFields.emailBouncedReason !== undefined) { body.EmailBouncedReason = updateFields.emailBouncedReason as string; } + if (updateFields.customFieldsUi) { + const customFields = (updateFields.customFieldsUi as IDataObject).customFieldsValues as IDataObject[]; + if (customFields) { + for (const customField of customFields) { + //@ts-ignore + body[customField.fieldId] = customField.value; + } + } + } responseData = await salesforceApiRequest.call(this, 'PATCH', `/sobjects/contact/${contactId}`, body); } //https://developer.salesforce.com/docs/api-explorer/sobject/Contact/get-contact-id @@ -1089,6 +1201,74 @@ export class Salesforce implements INodeType { responseData = await salesforceApiRequest.call(this, 'POST', '/sobjects/note', body); } } + if (resource === 'customObject') { + if (operation === 'create') { + const customObject = this.getNodeParameter('customObject', i) as string; + const customFieldsUi = this.getNodeParameter('customFieldsUi', i) as IDataObject; + const body: IDataObject = {}; + if (customFieldsUi) { + const customFields = (customFieldsUi as IDataObject).customFieldsValues as IDataObject[]; + if (customFields) { + for (const customField of customFields) { + //@ts-ignore + body[customField.fieldId] = customField.value; + } + } + } + responseData = await salesforceApiRequest.call(this, 'POST', `/sobjects/${customObject}`, body); + } + if (operation === 'update') { + const recordId = this.getNodeParameter('recordId', i) as string; + const customObject = this.getNodeParameter('customObject', i) as string; + const customFieldsUi = this.getNodeParameter('customFieldsUi', i) as IDataObject; + const body: IDataObject = {}; + if (customFieldsUi) { + const customFields = (customFieldsUi as IDataObject).customFieldsValues as IDataObject[]; + if (customFields) { + for (const customField of customFields) { + //@ts-ignore + body[customField.fieldId] = customField.value; + } + } + } + responseData = await salesforceApiRequest.call(this, 'PATCH', `/sobjects/${customObject}/${recordId}`, body); + } + if (operation === 'get') { + const customObject = this.getNodeParameter('customObject', i) as string; + const recordId = this.getNodeParameter('recordId', i) as string; + responseData = await salesforceApiRequest.call(this, 'GET', `/sobjects/${customObject}/${recordId}`); + } + if (operation === 'getAll') { + const customObject = this.getNodeParameter('customObject', i) as string; + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const options = this.getNodeParameter('options', i) as IDataObject; + let fields = ['id']; + if (options.fields) { + fields = options.fields as string[]; + } + try { + if (returnAll) { + qs.q = `SELECT ${fields.join(',')} FROM ${customObject}`; + responseData = await salesforceApiRequestAllItems.call(this, 'records', 'GET', '/query', {}, qs); + } else { + const limit = this.getNodeParameter('limit', i) as number; + qs.q = `SELECT ${fields.join(',')} FROM ${customObject} Limit ${limit}`; + responseData = await salesforceApiRequestAllItems.call(this, 'records', 'GET', '/query', {}, qs); + } + } catch(err) { + throw new Error(`Salesforce Error: ${err}`); + } + } + if (operation === 'delete') { + const customObject = this.getNodeParameter('customObject', i) as string; + const recordId = this.getNodeParameter('recordId', i) as string; + try { + responseData = await salesforceApiRequest.call(this, 'DELETE', `/sobjects/${customObject}/${recordId}`); + } catch(err) { + throw new Error(`Salesforce Error: ${err}`); + } + } + } if (resource === 'opportunity') { //https://developer.salesforce.com/docs/api-explorer/sobject/Opportunity/post-opportunity if (operation === 'create') { @@ -1134,6 +1314,15 @@ export class Salesforce implements INodeType { if (additionalFields.forecastCategoryName !== undefined) { body.ForecastCategoryName = additionalFields.forecastCategoryName as string; } + if (additionalFields.customFieldsUi) { + const customFields = (additionalFields.customFieldsUi as IDataObject).customFieldsValues as IDataObject[]; + if (customFields) { + for (const customField of customFields) { + //@ts-ignore + body[customField.fieldId] = customField.value; + } + } + } responseData = await salesforceApiRequest.call(this, 'POST', '/sobjects/opportunity', body); } //https://developer.salesforce.com/docs/api-explorer/sobject/Opportunity/post-opportunity @@ -1183,6 +1372,15 @@ export class Salesforce implements INodeType { if (updateFields.forecastCategoryName !== undefined) { body.ForecastCategoryName = updateFields.forecastCategoryName as string; } + if (updateFields.customFieldsUi) { + const customFields = (updateFields.customFieldsUi as IDataObject).customFieldsValues as IDataObject[]; + if (customFields) { + for (const customField of customFields) { + //@ts-ignore + body[customField.fieldId] = customField.value; + } + } + } responseData = await salesforceApiRequest.call(this, 'PATCH', `/sobjects/opportunity/${opportunityId}`, body); } //https://developer.salesforce.com/docs/api-explorer/sobject/Opportunity/get-opportunity-id @@ -1326,6 +1524,15 @@ export class Salesforce implements INodeType { if (additionalFields.shippingPostalCode !== undefined) { body.ShippingPostalCode = additionalFields.shippingPostalCode as string; } + if (additionalFields.customFieldsUi) { + const customFields = (additionalFields.customFieldsUi as IDataObject).customFieldsValues as IDataObject[]; + if (customFields) { + for (const customField of customFields) { + //@ts-ignore + body[customField.fieldId] = customField.value; + } + } + } responseData = await salesforceApiRequest.call(this, 'POST', '/sobjects/account', body); } //https://developer.salesforce.com/docs/api-explorer/sobject/Account/patch-account-id @@ -1408,6 +1615,15 @@ export class Salesforce implements INodeType { if (updateFields.shippingPostalCode !== undefined) { body.ShippingPostalCode = updateFields.shippingPostalCode as string; } + if (updateFields.customFieldsUi) { + const customFields = (updateFields.customFieldsUi as IDataObject).customFieldsValues as IDataObject[]; + if (customFields) { + for (const customField of customFields) { + //@ts-ignore + body[customField.fieldId] = customField.value; + } + } + } responseData = await salesforceApiRequest.call(this, 'PATCH', `/sobjects/account/${accountId}`, body); } //https://developer.salesforce.com/docs/api-explorer/sobject/Account/get-account-id @@ -1708,6 +1924,15 @@ export class Salesforce implements INodeType { if (additionalFields.recurrenceRegeneratedType !== undefined) { body.RecurrenceRegeneratedType = additionalFields.recurrenceRegeneratedType as string; } + if (additionalFields.customFieldsUi) { + const customFields = (additionalFields.customFieldsUi as IDataObject).customFieldsValues as IDataObject[]; + if (customFields) { + for (const customField of customFields) { + //@ts-ignore + body[customField.fieldId] = customField.value; + } + } + } responseData = await salesforceApiRequest.call(this, 'POST', '/sobjects/task', body); } //https://developer.salesforce.com/docs/api-explorer/sobject/Task/patch-task-id @@ -1787,6 +2012,15 @@ export class Salesforce implements INodeType { if (updateFields.recurrenceRegeneratedType !== undefined) { body.RecurrenceRegeneratedType = updateFields.recurrenceRegeneratedType as string; } + if (updateFields.customFieldsUi) { + const customFields = (updateFields.customFieldsUi as IDataObject).customFieldsValues as IDataObject[]; + if (customFields) { + for (const customField of customFields) { + //@ts-ignore + body[customField.fieldId] = customField.value; + } + } + } responseData = await salesforceApiRequest.call(this, 'PATCH', `/sobjects/task/${taskId}`, body); } //https://developer.salesforce.com/docs/api-explorer/sobject/Task/get-task-id diff --git a/packages/nodes-base/nodes/Salesforce/TaskDescription.ts b/packages/nodes-base/nodes/Salesforce/TaskDescription.ts index 6bde264cad..3db8033387 100644 --- a/packages/nodes-base/nodes/Salesforce/TaskDescription.ts +++ b/packages/nodes-base/nodes/Salesforce/TaskDescription.ts @@ -1,4 +1,6 @@ -import { INodeProperties } from 'n8n-workflow'; +import { + INodeProperties, +} from 'n8n-workflow'; export const taskOperations = [ { @@ -143,6 +145,42 @@ export const taskFields = [ }, description: 'The type of call being answered: Inbound, Internal, or Outbound.', }, + { + displayName: 'Custom Fields', + name: 'customFieldsUi', + placeholder: 'Add Custom Field', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + description: 'Filter by custom fields ', + default: {}, + options: [ + { + name: 'customFieldsValues', + displayName: 'Custom Field', + values: [ + { + displayName: 'Field ID', + name: 'fieldId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getLeadCustomFields', + }, + default: '', + description: 'The ID of the field to add custom field to.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + description: 'The value to set on custom field.', + }, + ], + }, + ], + }, { displayName: 'Description', name: 'description', @@ -458,6 +496,42 @@ export const taskFields = [ }, description: 'The type of call being answered: Inbound, Internal, or Outbound.', }, + { + displayName: 'Custom Fields', + name: 'customFieldsUi', + placeholder: 'Add Custom Field', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + description: 'Filter by custom fields ', + default: {}, + options: [ + { + name: 'customFieldsValues', + displayName: 'Custom Field', + values: [ + { + displayName: 'Field ID', + name: 'fieldId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getLeadCustomFields', + }, + default: '', + description: 'The ID of the field to add custom field to.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + description: 'The value to set on custom field.', + }, + ], + }, + ], + }, { displayName: 'Description', name: 'description',