import { OptionsWithUri } from 'request'; import { IExecuteFunctions, IExecuteSingleFunctions, IHookFunctions, ILoadOptionsFunctions, } from 'n8n-core'; import { IDataObject, NodeApiError, } 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 { // tslint:disable-line:no-any const credentials = await this.getCredentials('agileCrmApi'); const options: OptionsWithUri = { method, headers: { 'Accept': 'application/json', }, auth: { username: credentials!.email as string, password: credentials!.apiKey as string, }, uri: uri || `https://${credentials!.subdomain}.agilecrm.com/dev/${endpoint}`, json: true, }; // Only add Body property if method not GET or DELETE to avoid 400 response if (method !== 'GET' && method !== 'DELETE') { options.body = body; } try { return await this.helpers.request!(options); } catch (error) { throw new NodeApiError(this.getNode(), error); } } 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 const credentials = await this.getCredentials('agileCrmApi'); const baseUri = `https://${credentials!.subdomain}.agilecrm.com/dev/`; 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, }; const successfulUpdates = []; let lastSuccesfulUpdateReturn: any; // tslint:disable-line:no-any const payload: IContactUpdate = body; try { // Due to API, we must update each property separately. For user it looks like one seamless update 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) => { // tslint:disable-line:no-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/tags'; 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 (successfulUpdates.length === 0) { throw new NodeApiError(this.getNode(), error); } else { throw new NodeApiError(this.getNode(), error, { message: `Not all properties updated. Updated properties: ${successfulUpdates.join(', ')}`, description: error.message, httpCode: error.statusCode }); } } } export function validateJSON(json: string | undefined): any { // tslint:disable-line:no-any let result; try { result = JSON.parse(json!); } catch (exception) { result = undefined; } return result; }