Zoho node

This commit is contained in:
Ricardo Espinoza 2020-02-12 10:04:43 -05:00
parent 6e6ece1bcb
commit 8e87723858
7 changed files with 417 additions and 4 deletions

View file

@ -913,7 +913,7 @@ class App {
// Verify and store app code. Generate access tokens and store for respective credential.
this.app.get('/rest/oauth2-credential/callback', async (req: express.Request, res: express.Response) => {
const {code, state: stateEncoded} = req.query;
const {code, state: stateEncoded } = req.query;
if (code === undefined || stateEncoded === undefined) {
throw new Error('Insufficient parameters for OAuth2 callback');
@ -953,6 +953,20 @@ class App {
return ResponseHelper.sendErrorResponse(res, errorResponse);
}
let options = {};
// Here we need a variable that can be set on the credentials
// so that base on that include the credentails on the body or
// leave it as default with woukd include the credentails on the header.
// if (thatvariableistrue) {
// options = {
// body: {
// client_id: _.get(oauthCredentials, 'clientId') as string,
// client_secret: _.get(oauthCredentials, 'clientSecret', '') as string,
// },
// }
// }
const oAuthObj = new clientOAuth2({
clientId: _.get(oauthCredentials, 'clientId') as string,
clientSecret: _.get(oauthCredentials, 'clientSecret', '') as string,
@ -962,7 +976,7 @@ class App {
scopes: _.split(_.get(oauthCredentials, 'scope', 'openid,') as string, ',')
});
const oauthToken = await oAuthObj.code.getToken(req.originalUrl);
const oauthToken = await oAuthObj.code.getToken(req.originalUrl, options);
if (oauthToken === undefined) {
const errorResponse = new ResponseHelper.ResponseError('Unable to get access tokens!', undefined, 404);

View file

@ -0,0 +1,74 @@
import {
ICredentialType,
NodePropertyTypes,
} from 'n8n-workflow';
export class ZohoOAuth2Api implements ICredentialType {
name = 'zohoOAuth2Api';
extends = [
'oAuth2Api',
];
displayName = 'Zoho OAuth2 API';
properties = [
{
displayName: 'Authorization URL',
name: 'authUrl',
type: 'options' as NodePropertyTypes,
options: [
{
name: 'https://accounts.zoho.com/oauth/v2/auth',
value: 'https://accounts.zoho.com/oauth/v2/auth',
description: 'For the EU, AU, and IN domains',
},
{
name: 'https://accounts.zoho.com.cn/oauth/v2/auth',
value: 'https://accounts.zoho.com.cn/oauth/v2/auth',
description: 'For the CN domain',
},
],
default: 'https://accounts.zoho.com/oauth/v2/auth',
required: true,
},
{
displayName: 'Access Token URL',
name: 'accessTokenUrl',
type: 'options' as NodePropertyTypes,
options: [
{
name: 'US - https://accounts.zoho.com/oauth/v2/token',
value: 'https://accounts.zoho.com/oauth/v2/token',
},
{
name: 'AU - https://accounts.zoho.com.au/oauth/v2/token',
value: 'https://accounts.zoho.com.au/oauth/v2/token',
},
{
name: 'EU - https://accounts.zoho.eu/oauth/v2/token',
value: 'https://accounts.zoho.eu/oauth/v2/token',
},
{
name: 'IN - https://accounts.zoho.in/oauth/v2/token',
value: 'https://accounts.zoho.in/oauth/v2/token',
},
{
name: 'CN - https://accounts.zoho.com.cn/oauth/v2/token',
value: ' https://accounts.zoho.com.cn/oauth/v2/token',
},
],
default: 'https://accounts.zoho.com/oauth/v2/token',
required: true,
},
{
displayName: 'Scope',
name: 'scope',
type: 'hidden' as NodePropertyTypes,
default: 'ZohoCRM.modules.ALL',
},
{
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden' as NodePropertyTypes,
default: 'access_type=online',
},
];
}

View file

@ -0,0 +1,58 @@
import { OptionsWithUri } from 'request';
import {
IExecuteFunctions,
IExecuteSingleFunctions,
ILoadOptionsFunctions,
} from 'n8n-core';
import {
IDataObject
} from 'n8n-workflow';
export async function zohoApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
const credentials = this.getCredentials('zohoOAuth2Api');
const options: OptionsWithUri = {
headers: {
'Content-Type': 'application/json',
//@ts-ignore
Authorization: `Zoho-oauthtoken ${credentials!.oauthTokenData.access_token}`
},
method,
body: {
data: [
body,
],
},
qs,
uri: uri || `https://www.zohoapis.com/crm/v2${resource}`,
json: true
};
try {
//@ts-ignore
return await this.helpers.requestOAuth.call(this, 'zohoOAuth2Api', options);
} catch (error) {
if (error.response && error.response.body && error.response.body.message) {
// Try to return the error prettier
throw new Error(`Zoho error response [${error.statusCode}]: ${error.response.body.message}`);
}
throw error;
}
}
export async function zohoApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string ,method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
const returnData: IDataObject[] = [];
let responseData;
let uri: string | undefined;
do {
responseData = await zohoApiRequest.call(this, method, endpoint, body, query, uri);
uri = responseData.nextRecordsUrl;
returnData.push.apply(returnData, responseData[propertyName]);
} while (
responseData.nextRecordsUrl !== undefined &&
responseData.nextRecordsUrl !== null
);
return returnData;
}

View file

@ -0,0 +1,171 @@
import { INodeProperties } from "n8n-workflow";
export const leadOperations = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'lead',
],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a new lead',
},
{
name: 'Get',
value: 'get',
description: 'Get data of a lead',
},
{
name: 'Get All',
value: 'getAll',
description: 'Get data of all leads',
},
{
name: 'Update',
value: 'update',
description: 'Update new lead',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a lead',
}
],
default: 'create',
description: 'The operation to perform.',
},
] as INodeProperties[];
export const leadFields = [
/* -------------------------------------------------------------------------- */
/* lead:create */
/* -------------------------------------------------------------------------- */
{
displayName: 'Last Name',
name: 'lastName',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
resource: [
'lead',
],
operation: [
'create',
],
},
},
description: `User's last name`,
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
operation: [
'create',
],
resource: [
'lead',
],
},
},
options: [
{
displayName: 'Avatar',
name: 'avatar',
type: 'string',
default: '',
description: 'An avatar image URL. note: the image url needs to be https.',
},
{
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
description: 'Name of the user',
},
{
displayName: 'Phone',
name: 'phone',
type: 'string',
default: '',
description: 'The phone number of the user',
},
{
displayName: 'Unsubscribed From Emails',
name: 'unsubscribedFromEmails',
type: 'boolean',
default: false,
description: 'Whether the Lead is unsubscribed from emails',
},
{
displayName: 'Update Last Request At',
name: 'updateLastRequestAt',
type: 'boolean',
default: false,
description: 'A boolean value, which if true, instructs Intercom to update the<br />users last_request_at value to the current API service time in<br />UTC. default value if not sent is false.',
},
{
displayName: 'Companies',
name: 'companies',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getCompanies',
},
default: [],
description: 'Identifies the companies this user belongs to.',
},
{
displayName: 'UTM Source',
name: 'utmSource',
type: 'string',
default: '',
description: 'An avatar image URL. note: the image url needs to be https.',
},
{
displayName: 'UTM Medium',
name: 'utmMedium',
type: 'string',
default: '',
description: 'Identifies what type of link was used',
},
{
displayName: 'UTM Campaign',
name: 'utmCampaign',
type: 'string',
default: '',
description: 'Identifies a specific product promotion or strategic campaign',
},
{
displayName: 'UTM Term',
name: 'utmTerm',
type: 'string',
default: '',
description: 'Identifies search terms',
},
{
displayName: 'UTM Content',
name: 'utmContent',
type: 'string',
default: '',
description: 'Identifies what specifically was clicked to bring the user to the site',
},
]
},
] as INodeProperties[];

View file

@ -0,0 +1,94 @@
import {
IExecuteFunctions,
} from 'n8n-core';
import {
IDataObject,
INodeExecutionData,
INodeTypeDescription,
INodeType,
} from 'n8n-workflow';
import {
zohoApiRequest,
zohoApiRequestAllItems,
} from './GenericFunctions';
import {
leadOperations,
leadFields,
} from './LeadDescription';
export class ZohoCrm implements INodeType {
description: INodeTypeDescription = {
displayName: 'Zoho CRM',
name: 'zohoCrm',
icon: 'file:zohoCrm.png',
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
group: ['input'],
version: 1,
description: 'Consume Zoho CRM API.',
defaults: {
name: 'Zoho CRM',
color: '#CE2232',
},
inputs: ['main'],
outputs: ['main'],
credentials: [
{
name: 'zohoOAuth2Api',
required: true,
},
],
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
options: [
{
name: 'Lead',
value: 'lead',
},
],
default: 'lead',
description: 'The resource to operate on.',
},
...leadOperations,
...leadFields,
],
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: IDataObject[] = [];
const length = items.length as unknown as number;
let responseData;
for (let i = 0; i < length; i++) {
const resource = this.getNodeParameter('resource', 0) as string;
const operation = this.getNodeParameter('operation', 0) as string;
if (resource === 'lead') {
if (operation === 'create') {
const lastName = this.getNodeParameter('lastName', i) as string;
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
const body = {
Last_Name: lastName,
};
// if (additionalFields.email) {
// // @ts-ignore
// body.email = additionalFields.email as string;
// }
responseData = await zohoApiRequest.call(this, 'POST', '/leads', body);
responseData = responseData.data;
}
}
if (Array.isArray(responseData)) {
returnData.push.apply(returnData, responseData as IDataObject[]);
} else {
returnData.push(responseData as IDataObject);
}
}
return [this.helpers.returnJsonArray(returnData)];
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View file

@ -79,7 +79,8 @@
"dist/credentials/TypeformApi.credentials.js",
"dist/credentials/TogglApi.credentials.js",
"dist/credentials/VeroApi.credentials.js",
"dist/credentials/WordpressApi.credentials.js"
"dist/credentials/WordpressApi.credentials.js",
"dist/credentials/ZohoOAuth2Api.credentials.js"
],
"nodes": [
"dist/nodes/ActiveCampaign/ActiveCampaign.node.js",
@ -172,7 +173,8 @@
"dist/nodes/WriteBinaryFile.node.js",
"dist/nodes/Webhook.node.js",
"dist/nodes/Wordpress/Wordpress.node.js",
"dist/nodes/Xml.node.js"
"dist/nodes/Xml.node.js",
"dist/nodes/Zoho/ZohoCrm.node.js"
]
},
"devDependencies": {