From 8e87723858c244d8a635b1606e7fae588714f7ba Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Wed, 12 Feb 2020 10:04:43 -0500 Subject: [PATCH 1/2] Zoho node --- packages/cli/src/Server.ts | 18 +- .../credentials/ZohoOAuth2Api.credentials.ts | 74 ++++++++ .../nodes-base/nodes/Zoho/GenericFunctions.ts | 58 ++++++ .../nodes-base/nodes/Zoho/LeadDescription.ts | 171 ++++++++++++++++++ .../nodes-base/nodes/Zoho/ZohoCrm.node.ts | 94 ++++++++++ packages/nodes-base/nodes/Zoho/zohoCrm.png | Bin 0 -> 5758 bytes packages/nodes-base/package.json | 6 +- 7 files changed, 417 insertions(+), 4 deletions(-) create mode 100644 packages/nodes-base/credentials/ZohoOAuth2Api.credentials.ts create mode 100644 packages/nodes-base/nodes/Zoho/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/Zoho/LeadDescription.ts create mode 100644 packages/nodes-base/nodes/Zoho/ZohoCrm.node.ts create mode 100644 packages/nodes-base/nodes/Zoho/zohoCrm.png diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index fdc9e94e4e..e2e17a9740 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -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); diff --git a/packages/nodes-base/credentials/ZohoOAuth2Api.credentials.ts b/packages/nodes-base/credentials/ZohoOAuth2Api.credentials.ts new file mode 100644 index 0000000000..3617458a94 --- /dev/null +++ b/packages/nodes-base/credentials/ZohoOAuth2Api.credentials.ts @@ -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', + }, + ]; +} diff --git a/packages/nodes-base/nodes/Zoho/GenericFunctions.ts b/packages/nodes-base/nodes/Zoho/GenericFunctions.ts new file mode 100644 index 0000000000..ab6f00146c --- /dev/null +++ b/packages/nodes-base/nodes/Zoho/GenericFunctions.ts @@ -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 { // 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 { // 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; +} diff --git a/packages/nodes-base/nodes/Zoho/LeadDescription.ts b/packages/nodes-base/nodes/Zoho/LeadDescription.ts new file mode 100644 index 0000000000..198abbe472 --- /dev/null +++ b/packages/nodes-base/nodes/Zoho/LeadDescription.ts @@ -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
users last_request_at value to the current API service time in
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[]; diff --git a/packages/nodes-base/nodes/Zoho/ZohoCrm.node.ts b/packages/nodes-base/nodes/Zoho/ZohoCrm.node.ts new file mode 100644 index 0000000000..2032a57921 --- /dev/null +++ b/packages/nodes-base/nodes/Zoho/ZohoCrm.node.ts @@ -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 { + 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)]; + } +} diff --git a/packages/nodes-base/nodes/Zoho/zohoCrm.png b/packages/nodes-base/nodes/Zoho/zohoCrm.png new file mode 100644 index 0000000000000000000000000000000000000000..8d541a45140da9a4089eb8d3ef5025ee533c6144 GIT binary patch literal 5758 zcmY*-bzBr&*DWw<=c`|NL^qorq(JrZ=SHFR8cloSO`5%y4HGlYpb6lw4H$bM8p z3O+9F&0URYk@j{DE`ms5$iEE1$Ms(r2BH0z;%X}l(NTIsD}it}r{#fiL)jrBAX-{l zxU-pspqixgf6E_t!VoK0S4Tk@%-!7`>dpm4I9tLv1Oxs0(*#Bxjmcsv{f-26|=8w*Q^+h<~|1$qS?7u#6*kAMi=Q984^k4L`RS^&z_P@_2 z0#fW^)*A!o)l3q(Xe*qoD6Lo!%363DKh(vb_*7S%fyl8FJ+qV6 zfz}vn6f8-lN{>lJnr1B71Bjvrh61cUgm zt#!m&5GQo?A}ZnJ%;=)crJ-it1?7eBn69k9R15ew-TUc$5TkdD?YJ+UNJ5Kb7yG=L z{Q!IMNjnmO^`^RFLqSAL)e)|T^fRvWRGE=-A>X|0I|H9w&*F1e{4}ahSEsGa6c1m| z&H2e7&W2f4ReM~B%s`g)wg`iIIU~$Pn+PDPQpd?&EIO1oDDi?Z zd#vo%Mpy*cjPeyHSLYAkq&yp6TIm67$4L|(TQ!OQn9z`_V�fasDkaWhf$zpnpB5 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 literal 0 HcmV?d00001 diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 0ae62f9f07..b62e3bc649 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -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": { From 6a08fc9da3940e96f0b95f1bac4d27d7e33c6804 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Fri, 14 Feb 2020 18:48:58 -0800 Subject: [PATCH 2/2] :zap: Small improvements to ZohoCRM-Node and OAuth fix --- packages/cli/src/Server.ts | 19 ++++++++----------- .../GithubOAuth2Api.credentials.ts | 6 ++++++ .../credentials/OAuth2Api.credentials.ts | 19 +++++++++++++++++++ .../credentials/ZohoOAuth2Api.credentials.ts | 8 +++++++- .../nodes-base/nodes/Zoho/GenericFunctions.ts | 3 --- .../nodes-base/nodes/Zoho/ZohoCrm.node.ts | 8 ++++++-- 6 files changed, 46 insertions(+), 17 deletions(-) diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index e2e17a9740..4fe1ef70d7 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -955,17 +955,14 @@ class App { 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, - // }, - // } - // } + if (_.get(oauthCredentials, 'authentication', 'header') as string === 'body') { + 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, diff --git a/packages/nodes-base/credentials/GithubOAuth2Api.credentials.ts b/packages/nodes-base/credentials/GithubOAuth2Api.credentials.ts index 9466ce447b..afca85f57b 100644 --- a/packages/nodes-base/credentials/GithubOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/GithubOAuth2Api.credentials.ts @@ -37,5 +37,11 @@ export class GithubOAuth2Api implements ICredentialType { type: 'hidden' as NodePropertyTypes, default: '', }, + { + displayName: 'Authentication', + name: 'authentication', + type: 'hidden' as NodePropertyTypes, + default: 'header', + }, ]; } diff --git a/packages/nodes-base/credentials/OAuth2Api.credentials.ts b/packages/nodes-base/credentials/OAuth2Api.credentials.ts index efa5127e6e..ce2a248b62 100644 --- a/packages/nodes-base/credentials/OAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/OAuth2Api.credentials.ts @@ -53,5 +53,24 @@ export class OAuth2Api implements ICredentialType { description: 'For some services additional query parameters have to be set which can be defined here.', placeholder: 'access_type=offline', }, + { + displayName: 'Authentication', + name: 'authentication', + type: 'options' as NodePropertyTypes, + options: [ + { + name: 'Body', + value: 'body', + description: 'Send credentials in body', + }, + { + name: 'Header', + value: 'header', + description: 'Send credentials as Basic Auth header', + }, + ], + default: 'header', + description: 'Resource to consume.', + }, ]; } diff --git a/packages/nodes-base/credentials/ZohoOAuth2Api.credentials.ts b/packages/nodes-base/credentials/ZohoOAuth2Api.credentials.ts index 3617458a94..801b164ffd 100644 --- a/packages/nodes-base/credentials/ZohoOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/ZohoOAuth2Api.credentials.ts @@ -68,7 +68,13 @@ export class ZohoOAuth2Api implements ICredentialType { displayName: 'Auth URI Query Parameters', name: 'authQueryParameters', type: 'hidden' as NodePropertyTypes, - default: 'access_type=online', + default: 'access_type=offline', + }, + { + displayName: 'Authentication', + name: 'authentication', + type: 'hidden' as NodePropertyTypes, + default: 'body', }, ]; } diff --git a/packages/nodes-base/nodes/Zoho/GenericFunctions.ts b/packages/nodes-base/nodes/Zoho/GenericFunctions.ts index ab6f00146c..04fe204858 100644 --- a/packages/nodes-base/nodes/Zoho/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Zoho/GenericFunctions.ts @@ -9,12 +9,9 @@ import { } from 'n8n-workflow'; export async function zohoApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // 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: { diff --git a/packages/nodes-base/nodes/Zoho/ZohoCrm.node.ts b/packages/nodes-base/nodes/Zoho/ZohoCrm.node.ts index 2032a57921..f1d397c2dc 100644 --- a/packages/nodes-base/nodes/Zoho/ZohoCrm.node.ts +++ b/packages/nodes-base/nodes/Zoho/ZohoCrm.node.ts @@ -80,12 +80,16 @@ export class ZohoCrm implements INodeType { // } responseData = await zohoApiRequest.call(this, 'POST', '/leads', body); responseData = responseData.data; - + } else { + throw new Error(`The operation "${operation}" is not known!`); } + } else { + throw new Error(`The resource "${resource}" is not known!`); } + if (Array.isArray(responseData)) { returnData.push.apply(returnData, responseData as IDataObject[]); - } else { + } else if (responseData !== undefined) { returnData.push(responseData as IDataObject); } }