diff --git a/packages/nodes-base/nodes/AgileCrm/AgileCrm.node.ts b/packages/nodes-base/nodes/AgileCrm/AgileCrm.node.ts
index 01ee92de03..4fecdcd94d 100644
--- a/packages/nodes-base/nodes/AgileCrm/AgileCrm.node.ts
+++ b/packages/nodes-base/nodes/AgileCrm/AgileCrm.node.ts
@@ -10,8 +10,8 @@ import {
contactOperations,
contactFields
} from './ContactDescription';
-import { agileCrmApiRequest, validateJSON} from './GenericFunctions';
-import { IContact, IProperty } from './ContactInterface';
+import { agileCrmApiRequest, validateJSON, agileCrmApiRequestUpdate} from './GenericFunctions';
+import { IContact, IProperty, IContactUpdate } from './ContactInterface';
export class AgileCrm implements INodeType {
@@ -223,6 +223,133 @@ export class AgileCrm implements INodeType {
responseData = await agileCrmApiRequest.call(this, 'POST', endpoint, body);
}
+ if(operation === "update") {
+ const contactId = this.getNodeParameter('contactId', i) as string;
+ let contactUpdatePayload : IContactUpdate = {id: contactId};
+ const jsonParameters = this.getNodeParameter('jsonParameters', i) as boolean;
+ const body: IContact = {};
+ let properties : IDataObject[] = [];
+
+ if (jsonParameters) {
+ const additionalFieldsJson = this.getNodeParameter('additionalFieldsJson', i) as string;
+
+ if (additionalFieldsJson !== '' ) {
+
+ if (validateJSON(additionalFieldsJson) !== undefined) {
+
+ Object.assign(body, JSON.parse(additionalFieldsJson));
+
+ } else {
+ throw new Error('Additional fields must be a valid JSON');
+ }
+ }
+ } else {
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
+
+ if (additionalFields.starValue) {
+ body.star_value = additionalFields.starValue as string;
+ }
+ if (additionalFields.leadScore) {
+ body.lead_score = additionalFields.leadScore as string;
+ }
+ if (additionalFields.tags) {
+ body.tags = additionalFields.tags as string[];
+ }
+ if(additionalFields.firstName){
+ properties.push({
+ type: 'SYSTEM',
+ name: 'first_name',
+ value: additionalFields.firstName as string
+ } as IDataObject);
+ }
+ if(additionalFields.lastName){
+ properties.push({
+ type: 'SYSTEM',
+ name: 'last_name',
+ value: additionalFields.lastName as string
+ } as IDataObject);
+ }
+ if(additionalFields.company){
+ properties.push({
+ type: 'SYSTEM',
+ name: 'company',
+ value: additionalFields.company as string
+ } as IDataObject);
+ }
+ if(additionalFields.title){
+ properties.push({
+ type: 'SYSTEM',
+ name: 'title',
+ value: additionalFields.title as string
+ } as IDataObject);
+ }
+ if(additionalFields.emailOptions){
+ //@ts-ignore
+ additionalFields.emailOptions.emailProperties.map(property => {
+ properties.push({
+ type: 'SYSTEM',
+ subtype: property.subtype as string,
+ name: 'email',
+ value: property.email as string
+ } as IDataObject);
+ })
+ }
+ if(additionalFields.addressOptions){
+ //@ts-ignore
+ additionalFields.addressOptions.addressProperties.map(property => {
+ properties.push({
+ type: 'SYSTEM',
+ subtype: property.subtype as string,
+ name: 'address',
+ value: property.address as string
+ } as IDataObject);
+ })
+ }
+ if(additionalFields.websiteOptions){
+ //@ts-ignore
+ additionalFields.websiteOptions.websiteProperties.map(property => {
+ properties.push({
+ type: 'SYSTEM',
+ subtype: property.subtype as string,
+ name: 'webiste',
+ value: property.url as string
+ } as IDataObject);
+ })
+ }
+ if(additionalFields.phoneOptions){
+ //@ts-ignore
+ additionalFields.phoneOptions.phoneProperties.map(property => {
+ properties.push({
+ type: 'SYSTEM',
+ subtype: property.subtype as string,
+ name: 'phone',
+ value: property.number as string
+ } as IDataObject);
+ })
+ }
+ if(additionalFields.customProperties){
+ //@ts-ignore
+ additionalFields.customProperties.customProperty.map(property => {
+ properties.push({
+ type: 'CUSTOM',
+ subtype: property.subtype as string,
+ name: property.name,
+ value: property.value as string
+ } as IDataObject);
+ })
+ }
+ body.properties = properties;
+
+
+
+ }
+
+ Object.assign(contactUpdatePayload, body);
+
+ responseData = await agileCrmApiRequestUpdate.call(this, 'PUT', '', contactUpdatePayload);
+
+ }
+
if (Array.isArray(responseData)) {
returnData.push.apply(returnData, responseData as IDataObject[]);
} else {
diff --git a/packages/nodes-base/nodes/AgileCrm/ContactDescription.ts b/packages/nodes-base/nodes/AgileCrm/ContactDescription.ts
index c9414f5fa8..e37dc65e38 100644
--- a/packages/nodes-base/nodes/AgileCrm/ContactDescription.ts
+++ b/packages/nodes-base/nodes/AgileCrm/ContactDescription.ts
@@ -588,5 +588,478 @@ export const contactFields = [
default: '',
description: 'Unique identifier for a particular contact',
},
+/* -------------------------------------------------------------------------- */
+/* contact:update */
+/* -------------------------------------------------------------------------- */
+{
+ displayName: 'Contact ID',
+ name: 'contactId',
+ type: 'string',
+ required: true,
+ displayOptions: {
+ show: {
+ resource: [
+ 'contact',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ default: '',
+ description: 'Unique identifier for a particular contact',
+},
+{
+ displayName: 'JSON Parameters',
+ name: 'jsonParameters',
+ type: 'boolean',
+ default: false,
+ description: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'contact',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+},
+{
+ displayName: ' Additional Fields',
+ name: 'additionalFieldsJson',
+ type: 'json',
+ typeOptions: {
+ alwaysOpenEditWindow: true,
+ },
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'contact',
+ ],
+ operation: [
+ 'update',
+ ],
+ jsonParameters: [
+ true,
+ ],
+ },
+ },
+
+ description: `Object of values to set as described here.`,
+},
+{
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'contact',
+ ],
+ operation: [
+ 'update',
+ ],
+ jsonParameters: [
+ false,
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Star Value',
+ name: 'starValue',
+ type: 'options',
+ default: '',
+ required: false,
+ description: 'Rating of contact (Max value 5). This is not applicable for companies.',
+ options: [
+ {
+ name: '0',
+ value: 0
+ },
+ {
+ name: '1',
+ value: 1
+ },
+ {
+ name: '2',
+ value: 2
+ },
+ {
+ name: '3',
+ value: 3
+ },
+ {
+ name: '4',
+ value: 4
+ },
+ {
+ name: '5',
+ value: 5
+ },
+ ]
+ },
+ {
+ displayName: 'Lead Score',
+ name: 'leadScore',
+ type: 'number',
+ default: '',
+ description: 'Score of contact. This is not applicable for companies.',
+ required: false,
+ typeOptions: {
+ minValue: 0
+ }
+ },
+ {
+ displayName: 'Tags',
+ name: 'tags',
+ type: 'string',
+ typeOptions: {
+ multipleValues: true,
+ multipleValueButtonText: 'Add Tag',
+ },
+ default: [],
+ placeholder: 'Tag',
+ description: 'Unique identifiers added to contact, for easy management of contacts. This is not applicable for companies.',
+ },
+ {
+ displayName: 'First Name',
+ name: 'firstName',
+ type: 'string',
+ required: false,
+ default: "",
+ placeholder: 'First Name',
+ description: 'Contact first name.',
+ },
+ {
+ displayName: 'Last Name',
+ name: 'lastName',
+ type: 'string',
+ required: false,
+ default: "",
+ placeholder: 'Last Name',
+ description: 'Contact last name.',
+ },
+ {
+ displayName: 'Company',
+ name: 'company',
+ type: 'string',
+ required: false,
+ default: "",
+ placeholder: 'Company',
+ description: 'Company Name.',
+ },
+ {
+ displayName: 'Title',
+ name: 'title',
+ type: 'string',
+ required: false,
+ default: "",
+ placeholder: 'Title',
+ description: 'Professional title.',
+ },
+ {
+ displayName: 'Email',
+ name: 'emailOptions',
+ type: 'fixedCollection',
+ required: false,
+ description: 'Contact email.',
+ typeOptions: {
+ multipleValues: true,
+ },
+ options: [
+ {
+ displayName: 'Email Properties',
+ name: 'emailProperties',
+ values: [
+ {
+ displayName: 'Type',
+ name: 'subtype',
+ type: 'options',
+ required: true,
+ default: "",
+ placeholder: '',
+ description: 'Type of Email',
+ options: [
+ {
+ name: 'Work',
+ value: 'work'
+ },
+ {
+ name: 'Personal',
+ value: 'personal'
+ }
+ ]
+ },
+ {
+ displayName: 'Email',
+ name: 'email',
+ type: 'string',
+ required: true,
+ default: "",
+ placeholder: '',
+ description: 'Email',
+ }
+ ]
+ },
+
+ ]
+ },
+ {
+ displayName: 'Address',
+ name: 'addressOptions',
+ type: 'fixedCollection',
+ required: false,
+ description: 'Contacts address.',
+ typeOptions: {
+ multipleValues: true,
+ },
+ options: [
+ {
+ displayName: 'Address Properties',
+ name: 'addressProperties',
+ values: [
+ {
+ displayName: 'Type',
+ name: 'subtype',
+ type: 'options',
+ required: true,
+ default: "",
+ placeholder: '',
+ description: 'Type of address.',
+ options: [
+ {
+ name: 'Home',
+ value: 'home'
+ },
+ {
+ name: 'Postal',
+ value: 'postal'
+ }
+ ,
+ {
+ name: 'Office',
+ value: 'office'
+ }
+ ]
+ },
+ {
+ displayName: 'Address',
+ name: 'address',
+ type: 'string',
+ required: true,
+ default: "",
+ placeholder: '',
+ description: 'Full address.',
+ }
+ ]
+ },
+
+ ]
+ },
+ {
+ displayName: 'Website',
+ name: 'websiteOptions',
+ type: 'fixedCollection',
+ required: false,
+ description: 'Contacts websites.',
+ typeOptions: {
+ multipleValues: true,
+ },
+ options: [
+ {
+ displayName: 'Website properties.',
+ name: 'websiteProperties',
+ values: [
+ {
+ displayName: 'Type',
+ name: 'subtype',
+ type: 'options',
+ required: true,
+ default: "",
+ placeholder: '',
+ description: 'Type of website.',
+ options: [
+ {
+ name: 'URL',
+ value: 'url',
+ },
+ {
+ name: 'SKYPE',
+ value: 'skype',
+ },
+ {
+ name: 'TWITTER',
+ value: 'twitter',
+ },
+ {
+ name: 'LINKEDIN',
+ value: 'linkedin',
+ },
+ {
+ name: 'FACEBOOK',
+ value: 'facebook',
+ },
+ {
+ name: 'XING',
+ value: 'xing',
+ },
+ {
+ name: 'FEED',
+ value: 'feed',
+ },
+ {
+ name: 'GOOGLE_PLUS',
+ value: 'googlePlus',
+ },
+ {
+ name: 'FLICKR',
+ value: 'flickr',
+ },
+ {
+ name: 'GITHUB',
+ value: 'github',
+ },
+ {
+ name: 'YOUTUBE',
+ value: 'youtube',
+ },
+ ]
+ },
+ {
+ displayName: 'URL',
+ name: 'url',
+ type: 'string',
+ required: true,
+ default: "",
+ placeholder: '',
+ description: 'Website URL',
+ }
+ ]
+ },
+
+ ]
+ },
+ {
+ displayName: 'Phone',
+ name: 'phoneOptions',
+ type: 'fixedCollection',
+ required: false,
+ description: 'Contacts phone.',
+ typeOptions: {
+ multipleValues: true,
+ },
+ options: [
+ {
+ displayName: 'Phone properties',
+ name: 'phoneProperties',
+ values: [
+ {
+ displayName: 'Type',
+ name: 'subtype',
+ type: 'options',
+ required: true,
+ default: "",
+ placeholder: '',
+ description: 'Type of phone number.',
+ options: [
+ {
+ name: 'Home',
+ value: 'home'
+ },
+ {
+ name: 'Work',
+ value: 'work'
+ }
+ ,
+ {
+ name: 'Mobile',
+ value: 'mobile'
+ },
+ {
+ name: 'Main',
+ value: 'main'
+ },
+ {
+ name: 'Home Fax',
+ value: 'homeFax'
+ },
+ {
+ name: 'Work Fax',
+ value: 'workFax'
+ },
+ {
+ name: 'Other',
+ value: 'other'
+ },
+ ]
+ },
+ {
+ displayName: 'Number',
+ name: 'number',
+ type: 'string',
+ required: true,
+ default: "",
+ placeholder: '',
+ description: 'Phone number.',
+ }
+ ]
+ },
+
+ ]
+ },
+ {
+ displayName: 'Custom Properties',
+ name: 'customProperties',
+ type: 'fixedCollection',
+ required: false,
+ description: 'Custom Properties',
+ typeOptions: {
+ multipleValues: true,
+ },
+ options: [
+ {
+ displayName: 'Property',
+ name: 'customProperty',
+ values: [
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ required: true,
+ default: "",
+ placeholder: '',
+ description: 'Property name.'
+ },
+ {
+ displayName: 'Sub Type',
+ name: 'subtype',
+ type: 'string',
+ required: false,
+ default: "",
+ placeholder: '',
+ description: 'Property sub type.',
+ },
+ {
+ displayName: 'Value',
+ name: 'value',
+ type: 'string',
+ required: false,
+ default: "",
+ placeholder: '',
+ description: 'Property value.',
+ }
+ ]
+ },
+
+ ]
+ },
+ ],
+},
] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/AgileCrm/ContactInterface.ts b/packages/nodes-base/nodes/AgileCrm/ContactInterface.ts
index 661a4c4742..ae384df943 100644
--- a/packages/nodes-base/nodes/AgileCrm/ContactInterface.ts
+++ b/packages/nodes-base/nodes/AgileCrm/ContactInterface.ts
@@ -16,3 +16,11 @@ import {
properties?: IDataObject[];
}
+ export interface IContactUpdate {
+ id: string,
+ properties?: IDataObject[],
+ star_value?: string;
+ lead_score?: string;
+ tags?: string[];
+ }
+
diff --git a/packages/nodes-base/nodes/AgileCrm/GenericFunctions.ts b/packages/nodes-base/nodes/AgileCrm/GenericFunctions.ts
index b87fedd024..046b7c187a 100644
--- a/packages/nodes-base/nodes/AgileCrm/GenericFunctions.ts
+++ b/packages/nodes-base/nodes/AgileCrm/GenericFunctions.ts
@@ -12,6 +12,7 @@ import {
import {
IDataObject,
} from 'n8n-workflow';
+import { IContactUpdate } from './ContactInterface';
export async function agileCrmApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: any = {}, query: IDataObject = {}, uri?: string): Promise {
@@ -46,6 +47,86 @@ export async function agileCrmApiRequest(this: IHookFunctions | IExecuteFunction
throw error;
}
+
+}
+
+export async function agileCrmApiRequestUpdate(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string = 'PUT', endpoint?: string, body: any = {}, query: IDataObject = {}, uri?: string): Promise {
+ const baseUri = 'https://n8nio.agilecrm.com/dev/';
+ const credentials = this.getCredentials('agileCrmApi');
+ const options: OptionsWithUri = {
+ method,
+ headers: {
+ 'Accept': 'application/json',
+ },
+ body: {id: body.id},
+ auth: {
+ username: credentials!.email as string,
+ password: credentials!.apiKey as string
+ },
+ uri: uri || baseUri,
+ json: true
+ };
+
+ let successfulUpdates = [];
+ let lastSuccesfulUpdateReturn : any;
+ let payload : IContactUpdate = body;
+
+ try {
+ // Due to API, we must update each property separately
+ if(payload.properties){
+ options.body.properties = payload.properties;
+ options.uri = baseUri + 'api/contacts/edit-properties';
+ lastSuccesfulUpdateReturn = await this.helpers.request!(options);
+
+ // Iterate trough properties and show them as individial updates instead of only vague "properties"
+ payload.properties?.map((property : any) => {
+ successfulUpdates.push(`${property.name} `);
+ })
+
+ delete options.body.properties;
+ }
+ if(payload.lead_score){
+ options.body.lead_score = payload.lead_score;
+ options.uri = baseUri + 'api/contacts/edit/lead-score';
+ lastSuccesfulUpdateReturn = await this.helpers.request!(options);
+
+ successfulUpdates.push('lead_score');
+
+ delete options.body.lead_score;
+ }
+ if(body.tags){
+ options.body.tags = payload.tags;
+ options.uri = baseUri + 'api/contacts/edit/tagaas';
+ lastSuccesfulUpdateReturn = await this.helpers.request!(options);
+
+ payload.tags?.map((tag : string) => {
+ successfulUpdates.push(`(Tag) ${tag} `);
+ })
+
+ delete options.body.tags;
+ }
+ if(body.star_value){
+ options.body.star_value = payload.star_value;
+ options.uri = baseUri + 'api/contacts/edit/add-star';
+ lastSuccesfulUpdateReturn = await this.helpers.request!(options);
+
+ successfulUpdates.push('star_value');
+
+ delete options.body.star_value;
+ }
+
+ return lastSuccesfulUpdateReturn;
+
+ } catch (error) {
+
+ if (error.response && error.response.body && error.response.body.errors) {
+ const errorMessages = error.response.body.errors.map((e: IDataObject) => e.message);
+ throw new Error(`AgileCRM error response [${error.statusCode}]: ${errorMessages.join(' | ')}`);
+ }
+
+ throw new Error(`Not all items updated. Updated items: ${successfulUpdates.join(' , ')} \n \n` + error);
+ }
+
}
export function validateJSON(json: string | undefined): any { // tslint:disable-line:no-any