├── lib └── js │ ├── org.js │ ├── users.js │ ├── actions.js │ ├── functions.js │ ├── crmapi.js │ ├── OAuth.js │ ├── settings.js │ ├── attachments.js │ ├── modules.js │ ├── mysql │ └── mysql_util.js │ ├── util.js │ └── ZCRMRestClient.js ├── package.json └── README.md /lib/js/org.js: -------------------------------------------------------------------------------- 1 | 2 | var util = require('./util'); 3 | 4 | var org = function org() 5 | { 6 | return { 7 | get : function (input) 8 | { 9 | return util.promiseResponse(util.constructRequestDetails(input, "org", HTTP_METHODS.GET, true));//No I18N 10 | } 11 | } 12 | } 13 | 14 | module.exports = org; -------------------------------------------------------------------------------- /lib/js/users.js: -------------------------------------------------------------------------------- 1 | var util = require('./util'); 2 | 3 | var users = function users() 4 | { 5 | 6 | return { 7 | get : function (input) 8 | { 9 | return util.promiseResponse(util.constructRequestDetails(input, "users/{id}", HTTP_METHODS.GET, true));//No I18N 10 | } 11 | } 12 | } 13 | 14 | 15 | module.exports = users; -------------------------------------------------------------------------------- /lib/js/actions.js: -------------------------------------------------------------------------------- 1 | var util = require("./util"); 2 | 3 | var actions = function actions() 4 | { 5 | return { 6 | convert : function (input) 7 | { 8 | 9 | return util.promiseResponse(util.constructRequestDetails(input, "Leads/{id}/actions/convert", HTTP_METHODS.POST, false));//No I18N 10 | } 11 | } 12 | } 13 | 14 | module.exports = actions; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zcrmsdk", 3 | "version": "0.0.21", 4 | "description": "Node SDK for Zoho CRM", 5 | "main": "lib/js/ZCRMRestClient", 6 | "dependencies": { 7 | "fs": "0.0.1-security", 8 | "mysql": "^2.18.1", 9 | "properties-reader": "0.0.16", 10 | "querystring": "^0.2.0", 11 | "request": "^2.88.2" 12 | }, 13 | "keywords": [ 14 | "Node", 15 | "Zoho", 16 | "CRM", 17 | "OAuth2", 18 | "API" 19 | ], 20 | "author": "Zoho CRM API Team (support@zohocrm.com)" 21 | } 22 | -------------------------------------------------------------------------------- /lib/js/functions.js: -------------------------------------------------------------------------------- 1 | var util = require('./util'); 2 | var url = "functions/{api_name}/actions/execute"; 3 | 4 | var functions = function functions(){ 5 | 6 | return{ 7 | 8 | executeFunctionsInGet :function(input){ 9 | 10 | 11 | return util.promiseResponse(util.constructRequestDetails(input, url , HTTP_METHODS.GET, true));//No I18N 12 | }, 13 | 14 | executeFunctionsInPost :function(input){ 15 | 16 | return util.promiseResponse(util.constructRequestDetails(input, url , HTTP_METHODS.POST, true));//No I18N 17 | } 18 | } 19 | 20 | } 21 | 22 | module.exports = functions; 23 | -------------------------------------------------------------------------------- /lib/js/crmapi.js: -------------------------------------------------------------------------------- 1 | 2 | var modules = require('./modules'); 3 | var settings = require('./settings'); 4 | var actions = require('./actions'); 5 | var users = require('./users'); 6 | var org = require('./org'); 7 | var attachments = require('./attachments'); 8 | var functions = require('./functions'); 9 | 10 | global.HTTP_METHODS 11 | 12 | global.HTTP_METHODS = { 13 | GET : "GET",//No I18N 14 | POST : "POST",//No I18N 15 | PUT : "PUT",//No I18N 16 | DELETE : "DELETE"//No I18N 17 | }; 18 | 19 | var API = (function (argument) { 20 | return { 21 | 22 | MODULES : new modules(), 23 | SETTINGS : new settings(), 24 | ACTIONS : new actions(), 25 | USERS : new users(), 26 | ORG : new org(), 27 | ATTACHMENTS : new attachments(), 28 | FUNCTIONS :new functions() 29 | } 30 | 31 | })(this) 32 | 33 | module.exports = API; -------------------------------------------------------------------------------- /lib/js/OAuth.js: -------------------------------------------------------------------------------- 1 | 2 | var config = null; 3 | var qs = require('querystring'); 4 | var httpclient = require('request'); 5 | 6 | var actionvsurl = { 7 | 8 | generate_token:'/oauth/v2/token' 9 | } 10 | 11 | var mand_configurations = { 12 | 13 | generate_token : ['client_id','client_secret','redirect_uri','code','grant_type'], 14 | refresh_access_token : ['client_id','client_secret','grant_type','refresh_token'] 15 | } 16 | 17 | 18 | var OAuth = function (configuration,action) { 19 | if (!configuration) 20 | throw new Error('Missing configuration for Zoho OAuth2 service'); 21 | assertConfigAttributesAreSet(configuration, mand_configurations[action]); 22 | config = configuration; 23 | }; 24 | 25 | 26 | function assertConfigAttributesAreSet(configuration, attributes) { 27 | attributes.forEach(function (attribute) { 28 | if (!configuration[attribute]) 29 | throw new Error('Missing configuration for Zoho OAuth service: '+ attribute); 30 | }); 31 | } 32 | 33 | OAuth.constructurl=function(action){ 34 | 35 | var crmclient = require('./ZCRMRestClient'); 36 | var url = "https://"+crmclient.getIAMUrl()+actionvsurl[action]+'?' + qs.stringify(config); 37 | return url; 38 | 39 | } 40 | 41 | OAuth.generateTokens=function(url){ 42 | return new Promise(function(resolve,reject){ 43 | 44 | httpclient.post(url,{ 45 | 46 | },function (err, response) { 47 | 48 | var resultObj = {}; 49 | 50 | if(err){ 51 | 52 | resolve(err); 53 | } 54 | resolve(response); 55 | 56 | }); 57 | }) 58 | } 59 | 60 | 61 | module.exports = OAuth; 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /lib/js/settings.js: -------------------------------------------------------------------------------- 1 | var util = require('./util'); 2 | 3 | var settings = function settings() 4 | { 5 | 6 | return { 7 | getFields : function (input) 8 | { 9 | return util.promiseResponse(util.constructRequestDetails(input, "settings/fields/{id}", HTTP_METHODS.GET, true));//No I18N 10 | }, 11 | getLayouts : function (input) 12 | { 13 | return util.promiseResponse(util.constructRequestDetails(input, "settings/layouts/{id}", HTTP_METHODS.GET, true));//No I18N 14 | }, 15 | getCustomViews : function (input) 16 | { 17 | return util.promiseResponse(util.constructRequestDetails(input, "settings/custom_views/{id}", HTTP_METHODS.GET, true));//No I18N 18 | }, 19 | updateCustomViews : function (input) 20 | { 21 | return util.promiseResponse(util.constructRequestDetails(input, "settings/custom_views/{id}", HTTP_METHODS.PUT, true));//No I18N 22 | }, 23 | getModules : function (input) 24 | { 25 | return util.promiseResponse(util.constructRequestDetails(input, "settings/modules" + ((input && input.module) ? "/" + input.module : ""), HTTP_METHODS.GET, false));//No I18N 26 | }, 27 | getRoles : function (input) 28 | { 29 | return util.promiseResponse(util.constructRequestDetails(input, "settings/roles/{id}", HTTP_METHODS.GET, true));//No I18N 30 | }, 31 | getProfiles : function (input) 32 | { 33 | return util.promiseResponse(util.constructRequestDetails(input, "settings/profiles/{id}", HTTP_METHODS.GET, true));//No I18N 34 | }, 35 | getRelatedLists : function (input) 36 | { 37 | return util.promiseResponse(util.constructRequestDetails(input, "settings/related_lists/{id}", HTTP_METHODS.GET, true));//No I18N 38 | } 39 | } 40 | } 41 | 42 | module.exports = settings; -------------------------------------------------------------------------------- /lib/js/attachments.js: -------------------------------------------------------------------------------- 1 | 2 | var util = require('./util'); 3 | var attachments = function attachments() 4 | { 5 | 6 | return { 7 | uploadFile : function (input) 8 | { 9 | return util.promiseResponse(util.constructRequestDetails(input, input.module+ "/{id}/Attachments", HTTP_METHODS.POST, false));//No I18N 10 | }, 11 | deleteFile : function (input) 12 | { 13 | return util.promiseResponse(util.constructRequestDetails(input, input.module+ "/{id}/Attachments/"+input.relatedId, HTTP_METHODS.DELETE, false));//No I18N 14 | }, 15 | downloadFile : function (input) 16 | { 17 | input.download_file = true; 18 | return util.promiseResponse(util.constructRequestDetails(input, input.module+ "/{id}/Attachments/"+input.relatedId, HTTP_METHODS.GET, false));//No I18N 19 | }, 20 | uploadLink : function (input) 21 | { 22 | return util.promiseResponse(util.constructRequestDetails(input, input.module+ "/{id}/Attachments", HTTP_METHODS.POST, false));//No I18N 23 | }, 24 | uploadPhoto : function (input) 25 | { 26 | return util.promiseResponse(util.constructRequestDetails(input, input.module+ "/{id}/photo", HTTP_METHODS.POST, false));//No I18N 27 | }, 28 | downloadPhoto : function (input) 29 | { 30 | input.download_file = true; 31 | return util.promiseResponse(util.constructRequestDetails(input, input.module + "/{id}/photo", HTTP_METHODS.GET, false));//No I18N 32 | }, 33 | deletePhoto : function (input) 34 | { 35 | return util.promiseResponse(util.constructRequestDetails(input, input.module + "/{id}/photo", HTTP_METHODS.DELETE, false));//No I18N 36 | }, 37 | getAttachments : function (input) 38 | { 39 | return util.promiseResponse(util.constructRequestDetails(input, input.module + "/{id}/Attachments", HTTP_METHODS.GET, false));//No I18N 40 | } 41 | } 42 | } 43 | 44 | module.exports = attachments; -------------------------------------------------------------------------------- /lib/js/modules.js: -------------------------------------------------------------------------------- 1 | var util = require('./util'); 2 | 3 | var modules = function modules() 4 | { 5 | 6 | return { 7 | get : function(input) 8 | { 9 | return util.promiseResponse(util.constructRequestDetails(input, input.module + "/{id}", HTTP_METHODS.GET, false));//No I18N 10 | }, 11 | post : function(input) 12 | { 13 | return util.promiseResponse(util.constructRequestDetails(input, input.module + "/{id}", HTTP_METHODS.POST, false));//No I18N 14 | }, 15 | put : function(input) 16 | { 17 | return util.promiseResponse(util.constructRequestDetails(input, input.module + "/{id}", HTTP_METHODS.PUT, false));//No I18N 18 | }, 19 | delete : function (input) 20 | { 21 | return util.promiseResponse(util.constructRequestDetails(input, input.module + "/{id}", HTTP_METHODS.DELETE, false));//No I18N 22 | }, 23 | getAllDeletedRecords : function (input) 24 | { 25 | if (input.params) 26 | { 27 | input.params.type = "all"; 28 | } 29 | else 30 | { 31 | input.params = { 32 | "type" : "all"//No I18N 33 | }; 34 | } 35 | 36 | return util.promiseResponse(util.constructRequestDetails(input, input.module + "/deleted", HTTP_METHODS.GET, false));//No I18N 37 | }, 38 | getRecycleBinRecords : function (input) 39 | { 40 | if (input.params) 41 | { 42 | input.type = "recycle"; 43 | } 44 | else 45 | { 46 | input.params = { 47 | "type" : "recycle"//No I18N 48 | }; 49 | } 50 | 51 | return util.promiseResponse(util.constructRequestDetails(input, input.module + "/deleted", HTTP_METHODS.GET, false));//No I18N 52 | }, 53 | getPermanentlyDeletedRecords : function (input) 54 | { 55 | if (input.params) 56 | { 57 | input.type = "permanent"; 58 | } 59 | else 60 | { 61 | input.params = { 62 | "type" : "permanent"//No I18N 63 | }; 64 | } 65 | 66 | return util.promiseResponse(util.constructRequestDetails(input, input.module + "/deleted", HTTP_METHODS.GET, false));//No I18N 67 | }, 68 | search : function (input) 69 | { 70 | return util.promiseResponse(util.constructRequestDetails(input, input.module + "/search", HTTP_METHODS.GET, false));//No I18N 71 | } 72 | } 73 | } 74 | 75 | module.exports = modules; -------------------------------------------------------------------------------- /lib/js/mysql/mysql_util.js: -------------------------------------------------------------------------------- 1 | 2 | var mysql_util = {}; 3 | var mysql = require('mysql'); 4 | 5 | mysql_util.saveOAuthTokens = function(config_obj){ 6 | 7 | return new Promise(function(resolve,reject){ 8 | 9 | var con = getConnection(); 10 | 11 | var sql = "INSERT INTO oauthtokens (useridentifier, accesstoken,refreshtoken,expirytime) VALUES ('"+config_obj.user_identifier+"','"+config_obj.access_token+"','"+config_obj.refresh_token+"',"+config_obj.expires_in+")"; 12 | 13 | mysql_util.deleteOAuthTokens().then(function(){ 14 | 15 | con.connect(function(err){ 16 | 17 | if(err){ 18 | throw new Error(err); 19 | } 20 | 21 | con.query(sql,function(err,result){ 22 | 23 | if (err) { 24 | throw new Error(err); 25 | con.end(); 26 | } 27 | 28 | con.end(); 29 | 30 | resolve(); 31 | 32 | }) 33 | 34 | 35 | }) 36 | 37 | }) 38 | 39 | }) 40 | } 41 | 42 | mysql_util.getOAuthTokens = function(){ 43 | 44 | return new Promise(function(resolve,reject){ 45 | 46 | var con = getConnection(); 47 | 48 | var crmclient = require('../ZCRMRestClient'); 49 | con.connect(function(err){ 50 | 51 | if(err) Promise.reject(err); 52 | 53 | var sql = "Select * from oauthtokens where useridentifier = '"+crmclient.getUserIdentifier()+"'"; 54 | 55 | con.query(sql,function(err,result){ 56 | 57 | if(err) { 58 | throw new Error(err); 59 | con.end(); 60 | } 61 | 62 | con.end(); 63 | if(result != undefined && result.length != 0) 64 | { 65 | var modified_result ={}; 66 | modified_result["access_token"] = result[0].accesstoken; 67 | modified_result["refresh_token"] = result[0].refreshtoken; 68 | modified_result["expires_in"] = result[0].expirytime; 69 | modified_result["user_identifier"] = result[0].useridentifier; 70 | resolve(modified_result); 71 | } 72 | resolve(result); 73 | }); 74 | 75 | }); 76 | }) 77 | 78 | } 79 | 80 | mysql_util.updateOAuthTokens = function(config_obj){ 81 | 82 | 83 | return new Promise(function(resolve,reject){ 84 | 85 | var con = getConnection(); 86 | var crmclient = require('../ZCRMRestClient'); 87 | 88 | con.connect(function(err){ 89 | 90 | if(err) throw err; 91 | 92 | var sql = "update oauthtokens set accesstoken = '"+config_obj.access_token+"' , expirytime="+config_obj.expires_in+" where useridentifier = '"+crmclient.getUserIdentifier()+"'"; 93 | 94 | con.query(sql,function(err,result){ 95 | 96 | if(err) { 97 | throw new Error(err); 98 | con.end(); 99 | } 100 | 101 | con.end(); 102 | 103 | resolve(result); 104 | 105 | }) 106 | }) 107 | }); 108 | } 109 | 110 | 111 | mysql_util.deleteOAuthTokens = function(){ 112 | 113 | return new Promise(function(resolve,reject){ 114 | 115 | var con = getConnection(); 116 | 117 | var crmclient = require('../ZCRMRestClient'); 118 | 119 | var sql = "delete from oauthtokens where useridentifier='"+crmclient.getUserIdentifier()+"'"; 120 | 121 | con.connect(function(err){ 122 | 123 | if(err) throw err; 124 | 125 | con.query(sql,function(err,result){ 126 | 127 | if(err) { 128 | throw new Error(err); 129 | } 130 | 131 | con.end(); 132 | 133 | resolve(result); 134 | }) 135 | 136 | 137 | }) 138 | }) 139 | 140 | } 141 | 142 | 143 | function getConnection(){ 144 | 145 | var crmclient = require('../ZCRMRestClient'); 146 | var con = mysql.createConnection({ 147 | 148 | host: "localhost", 149 | user: crmclient.getMySQLUserName(), 150 | password: crmclient.getMYSQLPassword(), 151 | database: "zohooauth" 152 | 153 | }); 154 | 155 | return con; 156 | 157 | } 158 | 159 | module.exports = mysql_util; -------------------------------------------------------------------------------- /lib/js/util.js: -------------------------------------------------------------------------------- 1 | 2 | function promiseResponse(request) { 3 | 4 | var crmclient = require('./ZCRMRestClient'); 5 | var OAuth = require('./OAuth'); 6 | 7 | return new Promise(function(resolve,reject){ 8 | var persistenceModule = require(crmclient.getPersistenceModule()); 9 | 10 | persistenceModule.getOAuthTokens(crmclient.getUserIdentifier()).then(function(response){ 11 | 12 | var date = new Date(); 13 | var current_time = date.getTime(); 14 | 15 | var expires_in = response.expires_in; 16 | var refresh_token = response.refresh_token; 17 | 18 | if(current_time > expires_in){ 19 | 20 | var config = crmclient.getConfig_refresh(refresh_token) 21 | 22 | new OAuth(config,'refresh_access_token'); 23 | var url = OAuth.constructurl('generate_token'); 24 | 25 | OAuth.generateTokens(url).then(function(response){ 26 | var result_obj = crmclient.parseAndConstructObject(response); 27 | 28 | persistenceModule.updateOAuthTokens(result_obj).then(function(response){ 29 | 30 | makeapicall(request).then(function(response){ 31 | 32 | resolve(response); 33 | 34 | }) 35 | }); 36 | }) 37 | } 38 | 39 | else{ 40 | 41 | makeapicall(request).then(function(response){ 42 | 43 | resolve(response); 44 | 45 | }) 46 | } 47 | }) 48 | }) 49 | } 50 | 51 | 52 | function makeapicall(request){ 53 | 54 | return new Promise(function(resolve,reject){ 55 | 56 | var crmclient = require('./ZCRMRestClient'); 57 | var httpclient = require('request'); 58 | var OAuth = require('./OAuth'); 59 | var persistenceModule = require(crmclient.getPersistenceModule()); 60 | var qs = require('querystring'); 61 | 62 | persistenceModule.getOAuthTokens(crmclient.getUserIdentifier()).then(function(result_obj){ 63 | var access_token = result_obj.access_token; 64 | var baseUrl = "https://"+crmclient.getAPIURL()+"/crm/"+crmclient.getVersion() +"/"+ request.url; 65 | if (request.params) 66 | { 67 | baseUrl = baseUrl + '?' + request.params; 68 | } 69 | 70 | var api_headers = {}; 71 | var encoding ="utf8"; 72 | var req_body = null; 73 | var formData = null; 74 | 75 | if (request.download_file){ 76 | encoding = "binary";//No I18N 77 | } 78 | 79 | var form_Data = null; 80 | 81 | if (request.x_file_content) { 82 | var FormData = require('form-data'); 83 | form_Data = new FormData(); 84 | form_Data.append('file', request.x_file_content);//No I18N 85 | req_body = form_Data; 86 | api_headers = form_Data.getHeaders(); 87 | } 88 | else{ 89 | req_body = request.body || null; 90 | } 91 | 92 | if(request.headers){ 93 | var header_keys = Object.keys(request.headers); 94 | for(i in header_keys){ 95 | api_headers[header_keys[i]] = request.headers[header_keys[i]]; 96 | } 97 | } 98 | 99 | api_headers.Authorization = 'Zoho-oauthtoken '+access_token; 100 | api_headers["User-Agent"] = 'Zoho CRM Node SDK'; 101 | 102 | httpclient({ 103 | 104 | uri : baseUrl, 105 | method : request.type, 106 | headers : api_headers, 107 | body:req_body, 108 | encoding: encoding 109 | 110 | },function(error,response,body){ 111 | 112 | if(error){ 113 | resolve(error); 114 | } 115 | else if(response.statusCode == 204){ 116 | 117 | var respObj = { 118 | "message" : "no data", //No I18N 119 | "status_code" : "204" //No I18N 120 | } 121 | resolve(JSON.stringify(respObj)); 122 | 123 | }else{ 124 | if (request.download_file){ 125 | 126 | var filename; 127 | var disposition =response.headers["content-disposition"];//No I18N 128 | if (disposition && disposition.indexOf('attachment') !== -1) { 129 | var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/; 130 | var matches = filenameRegex.exec(disposition); 131 | if (matches != null && matches[1]) { 132 | filename = matches[1].replace(/['"]/g, ''); 133 | filename = filename.replace('UTF-8',''); 134 | } 135 | } 136 | response.filename = filename; 137 | resolve(response); 138 | } 139 | else{ 140 | resolve(response); 141 | } 142 | } 143 | }); 144 | }) 145 | }) 146 | } 147 | 148 | 149 | function createParams(parameters) 150 | { 151 | 152 | var params, key; 153 | for (key in parameters) 154 | { 155 | if (parameters.hasOwnProperty(key)) { 156 | if (params) 157 | { 158 | params = params + key + '=' + parameters[key] + '&'; 159 | } 160 | else 161 | { 162 | params = key + '=' + parameters[key] + '&'; 163 | } 164 | } 165 | } 166 | 167 | return params; 168 | } 169 | 170 | function constructRequestDetails(input, url, type, isModuleParam) 171 | { 172 | 173 | var requestDetails = {}; 174 | 175 | requestDetails.type = type; 176 | 177 | if (input != undefined) 178 | { 179 | if (input.id) 180 | { 181 | url = url.replace("{id}", input.id); 182 | } 183 | else 184 | { 185 | url = url.replace("/{id}", ""); 186 | } 187 | if(input.api_name){ 188 | 189 | url = url.replace("{api_name}",input.api_name); 190 | 191 | 192 | var params = {}; 193 | if(input.params){ 194 | 195 | params = input.params; 196 | } 197 | params.auth_type = "oauth"; 198 | input.params = params; 199 | } 200 | else{ 201 | 202 | url = url.replace("/{api_name}",""); 203 | 204 | } 205 | if (input.params) 206 | { 207 | requestDetails.params = createParams(input.params) + (input.module && isModuleParam ? "module=" + input.module : "");//No I18N 208 | } 209 | if (!requestDetails.params && isModuleParam) 210 | { 211 | requestDetails.params = "module=" + input.module;//No I18N 212 | } 213 | if (input.body && (type == HTTP_METHODS.POST || type == HTTP_METHODS.PUT)) 214 | { 215 | requestDetails.body = JSON.stringify(input.body); 216 | } 217 | if (input.x_file_content) 218 | { 219 | requestDetails.x_file_content = input.x_file_content; 220 | } 221 | if (input.download_file) 222 | { 223 | requestDetails.download_file = input.download_file; 224 | } 225 | if(input.headers){ 226 | 227 | requestDetails.headers = input.headers; 228 | } 229 | } 230 | requestDetails.url = url; 231 | 232 | return requestDetails; 233 | 234 | 235 | } 236 | 237 | module.exports = { 238 | 239 | constructRequestDetails : constructRequestDetails, 240 | promiseResponse : promiseResponse, 241 | 242 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Archival Notice: 3 | 4 | This SDK is archived. You can continue to use it, but no new features or support requests will be accepted. For the new version, refer to 5 | https://www.zoho.com/crm/developer/docs/sdk/server-side/nodejs-sdk.html 6 | 7 | # Node JS SDK for Zoho CRM 8 | 9 | ## Abstract 10 | 11 | Node SDK is a wrapper for Zoho CRM APIs. Hence invoking a Zoho CRM API from your Node application is just a function call which provide the most appropriate response. 12 | 13 | This SDK supports both single user as well as multi user authentication. 14 | 15 | ## Registering a Zoho Client 16 | 17 | Since Zoho CRM APIs are authenticated with OAuth2 standards, you should register your client app with Zoho. To register your app: 18 | 19 | - Visit this page https://accounts.zoho.com/developerconsole. 20 | 21 | - Click on “Add Client ID”. 22 | 23 | - Enter Client Name, Client Domain and Redirect URI. 24 | 25 | - Select the Client Type as "Web based". 26 | 27 | - Click “Create”. 28 | 29 | - Your Client app would have been created and displayed by now. 30 | 31 | - The newly registered app's Client ID and Client Secret can be found by clicking Options → Edit.
(Options is the three dot icon at the right corner). 32 | 33 | ## Installation of Node CRM SDK 34 | 35 | Node JS SDK will be installed and a package named 'zcrmsdk' will be created in the installation directory. 36 | 37 | >npm install zcrmsdk 38 | 39 | Once installed it can be used in the code as below, 40 | 41 | >var ZCRMRestClient = require('zcrmsdk') 42 | 43 | ## API Usage 44 | 45 | ## Configurations 46 | 47 | Your OAuth Client details should be given to the SDK as a property file. In the SDK, you need to configure a file named oauth_configuration.properties. Please place the respective values in that file. You can place it under resources/ package from where the SDK is used. 48 | 49 | 50 | zcrmsdk will try reading file from **'resources/oauth_configuration.properties'** 51 | 52 | 53 | Please fill the values for the following keys alone. 54 | Based on your domain(EU,CN), please change the value of crm.iamurl. Default value set as US domain. 55 | 56 | 57 | ``` 58 | 59 | [zoho] 60 | crm.iamurl= 61 | crm.clientid= 62 | crm.clientsecret= 63 | crm.redirecturl= 64 | 65 | ``` 66 | 67 | crm.clientid, crm.clientsecret and crm.redirecturl are your OAuth client’s configurations that you get after registering your Zoho client. 68 | crm.iamurl is the accounts URL. It may be accounts.zoho.com or accounts.zoho.eu. If the crm.iamurl is not specified, by default the URL will be accounts.zoho.com. 69 | 70 | In configuration.properties file: 71 | 72 | ``` 73 | [crm] 74 | api.url= 75 | api.user_identifier= 76 | api.tokenmanagement= 77 | 78 | [mysql] 79 | username= 80 | password= 81 | 82 | ``` 83 | api.url is the URL used to call APIs. By default, the URL is www.zohoapis.com. 84 | api.user_identifier will be empty by default. For single user authentication, this key can be filled with the respective email id, so that all calls happens by using this user's authentication. 85 | api.tokenmanagement is given as a measure to manage and maintain tokens. If tokenmanagement is not provided, then sdk's default implementation(mysql) will be followed. 86 | username and password can be given here if you already have one created for your MySQL. 87 | The above keys specified in configuration.properties file are all optional. 88 | 89 | user_identifier can be set in two ways . 90 | 1.Mentioning it in api.user_identifier in configuration.properties file 91 | 2.Can be set via code using set setUserIdentifier. 92 | 93 | If user_identifier is not set via both the ways then default value 'zcrm_default_user' will be set by the sdk itself . 94 | 95 | 96 | ## Token Storage Mechanism 97 | 98 | To use the default token storage provided by the SDK, the following are to be done: 99 | 100 | **Mysql should be running in default port in localhost.** 101 | 102 | Database with name **zohooauth** should be created and a table with below configurations should be present in the database. Table, named **"oauthtokens"**, should have the columns **"useridentifier" (varchar) "accesstoken" (varchar), "refreshtoken" (varchar) and "expirytime" (bigint)**. 103 | 104 | Once the configuration is set, storage and retrieval of tokens will be handled by the SDK. 105 | If the user wants to utilize their own mechanism, they can mention it in configuration.properties by providing the respective module in api.tokenmanagement. 106 | 107 | This module should contain the below methods, 108 | **saveOAuthTokens(token_obj)** 109 | **updateOAuthTokens(token_obj)** 110 | Irrespective of response, the next execution happens. So care should be taken by the user in handling their module. 111 | **getOAuthTokens()** 112 | The expected response for this method : JSON array containing json response with expirytime, refreshtoken and accesstoken fields. 113 | 114 | 115 | ## Generating self-authorized grant and refresh token 116 | 117 | For self client apps, the self authorized grant token should be generated from the Zoho Developer Console (https://accounts.zoho.com/developerconsole) 118 | 119 | 120 | - Visit https://accounts.zoho.com/developerconsole 121 | 122 | - Click Options → Self Client of the client for which you wish to authorize. 123 | 124 | - Enter one or more (comma separated) valid Zoho CRM scopes that you wish to authorize in the “Scope” field and choose the time of expiry. Provide “aaaserver.profile.READ” scope along with Zoho CRM scopes. 125 | - Copy the grant token for backup. 126 | 127 | - Generate refresh_token from grant token by making a POST request with the URL below https://accounts.zoho.com/oauth/v2/token?code={grant_token}&redirect_uri={redirect_uri}&client_id={client_id}&client_secret={client_secret}&grant_type=authorization_code 128 | 129 | - Copy the refresh token for backup. 130 | 131 | Please note that the generated grant token is valid only for the stipulated time you chose while generating it. Hence, the access and refresh tokens should be generated within that time. 132 | 133 | Each time server is restarted, this function has to be called and both the configuration files should be populated with proper values before calling this function, else exception will be thrown. 134 | 135 | **All functions return promises in zcrm node sdk.** 136 | 137 | 138 | ## Initialize 139 | 140 | Below snippet has to be called before starting the app 141 | 142 | ``` 143 | var ZCRMRestClient = require('zcrmsdk'); 144 | 145 | ZCRMRestClient.initialize().then(function(){ 146 | 147 | ... 148 | 149 | }); 150 | 151 | ``` 152 | 153 | ## Generating access and refresh token from granttoken 154 | 155 | ``` 156 | 157 | ZCRMRestClient.generateAuthTokens(user_identifier,grant_token).then(function(auth_response){ 158 | 159 | console.log("access token :"+auth_response.access_token); 160 | console.log("refresh token :"+auth_response.refresh_token); 161 | console.log("expires in :"+auth_response.expires_in); 162 | 163 | }); 164 | 165 | ``` 166 | 167 | ## Generating access token from refresh token 168 | 169 | This will be handled by sdk itself if the access and refresh token is generated by sdk.Developer need not call this explicitly. 170 | 171 | ``` 172 | ZCRMRestClient.generateAuthTokenfromRefreshToken(user_identifier,refresh_token).then(function(auth_response){ 173 | 174 | console.log("access token :"+auth_response.access_token); 175 | console.log("refresh token :"+auth_response.refresh_token); 176 | console.log("expires in :"+auth_response.expires_in); 177 | 178 | }); 179 | 180 | ``` 181 | 182 | ## Sample API Calls 183 | 184 | ``` 185 | var input ={}; 186 | input.module = "Leads"; 187 | 188 | var params = {}; 189 | params.page = 0; 190 | params.per_page = 5; 191 | input.params = params; 192 | 193 | crmclient.API.MODULES.get(input).then(function(response){ 194 | 195 | var result = " Top 5 Leads"; 196 | var data = response.body; 197 | data = JSON.parse(data); 198 | data = data.data; 199 | for (i in data){ 200 | 201 | var record = data[i]; 202 | var name = record.Full_Name; 203 | 204 | result+="
"+name+""; 205 | 206 | } 207 | 208 | result+=""; 209 | 210 | }) 211 | ``` 212 | 213 | 214 | 215 | ## Hierarchy 216 | zcrmsdk 217 | 218 | ``` 219 | API 220 | ORG 221 | get 222 | MODULES 223 | get 224 | post 225 | put 226 | delete 227 | getAllDeletedRecords 228 | getRecycleBinRecords 229 | getPermanentlyDeletedRecords 230 | search 231 | SETTINGS 232 | getFields 233 | getLayouts 234 | getCustomViews 235 | updateCustomViews 236 | getModules 237 | getRoles 238 | getProfiles 239 | getRelatedLists 240 | ACTIONS 241 | convert 242 | USERS 243 | get 244 | ATTACHMENTS 245 | uploadFile 246 | deleteFile 247 | downloadFile 248 | uploadLink 249 | uploadPhoto 250 | downloadPhoto 251 | deletePhoto 252 | FUNCTIONS 253 | executeFunctionsInGet 254 | executeFunctionsInPost 255 | ``` 256 | 257 | 258 | As appearing in the hierarchy, zcrmsdk entity class has instance variables to fetch its own properties and to fetch data of its immediate child entities through an API call. 259 | 260 | For example, to call an API to get module data, the request should be zcrmsdk.API.MODULES.{operation_type}. The operation types can be GET, POST, PUT, DELETE or CREATE. 261 | 262 | 263 | 264 | ## Response Handling 265 | All API calls will give the actual API response given by Zoho APIs, except file download. 266 | 267 | For file download, the response will contain an extra field filename. 268 | 269 | ## Error Handling: 270 | All errors will be thrown explicitly and care should be taken in catching the same. 271 | 272 | 273 | 274 | -------------------------------------------------------------------------------- /lib/js/ZCRMRestClient.js: -------------------------------------------------------------------------------- 1 | 2 | var OAuth = require('./OAuth'); 3 | var path = require("path"); 4 | 5 | var client_id = null; 6 | var client_secret = null; 7 | var redirect_url = null; 8 | var user_identifier = null; 9 | var persistenceModule = path.dirname(__filename)+"/mysql/mysql_util" ; 10 | var iamurl = "accounts.zoho.com"; 11 | var baseURL = "www.zohoapis.com"; 12 | var version = "v2"; 13 | var mysql_username = "root"; 14 | var mysql_password = ""; 15 | var default_user_identifier = "zcrm_default_user"; 16 | 17 | var ZCRMRestClient = function(){}; 18 | 19 | ZCRMRestClient.initialize = function(configJSON){ 20 | 21 | 22 | return new Promise(function(resolve,reject){ 23 | 24 | if(configJSON){ 25 | 26 | ZCRMRestClient.initializeWithValues(configJSON); 27 | resolve(); 28 | 29 | } 30 | 31 | var PropertiesReader = require('properties-reader'); 32 | var properties = PropertiesReader('resources/oauth_configuration.properties'); 33 | var client_id = properties.get('zoho.crm.clientid'); 34 | var client_secret = properties.get('zoho.crm.clientsecret'); 35 | var redirect_url = properties.get('zoho.crm.redirecturl'); 36 | var iam_url =properties.get('zoho.crm.iamurl')?properties.get('zoho.crm.iamurl'):iamurl; 37 | 38 | var config_properties = PropertiesReader('resources/configuration.properties'); 39 | 40 | persistenceModule = config_properties.get('crm.api.tokenmanagement')?config_properties.get('crm.api.tokenmanagement'):persistenceModule; 41 | 42 | baseURL = config_properties.get('crm.api.url')?config_properties.get('crm.api.url'):baseURL; 43 | 44 | mysql_username = config_properties.get('mysql.username')?config_properties.get('mysql.username'):mysql_username; 45 | 46 | mysql_password = config_properties.get('mysql.password')?config_properties.get('mysql.password'):mysql_password; 47 | 48 | 49 | if(config_properties.get('crm.api.user_identifier')){ 50 | 51 | ZCRMRestClient.setUserIdentifier(config_properties.get('crm.api.user_identifier')); 52 | } 53 | else{ 54 | 55 | ZCRMRestClient.setUserIdentifier(default_user_identifier); 56 | } 57 | 58 | if(!client_id || !client_secret || !redirect_url){ 59 | 60 | throw new Error("Populate the oauth_configuration.properties file"); 61 | 62 | } 63 | 64 | ZCRMRestClient.setClientId(client_id); 65 | ZCRMRestClient.setClientSecret(client_secret); 66 | ZCRMRestClient.setRedirectURL(redirect_url); 67 | ZCRMRestClient.setIAMUrl(iam_url); 68 | 69 | 70 | resolve(); 71 | 72 | }) 73 | } 74 | 75 | ZCRMRestClient.initializeWithValues = function(configJSON){ 76 | 77 | var mandatory_values = ['client_id','client_secret','redirect_url'] 78 | mandatory_values.forEach(function (key) { 79 | if (!configJSON[key]){ 80 | throw new Error('Missing configuration for Zoho OAuth service: '+ key); 81 | } 82 | }) 83 | var client_id = configJSON.client_id; 84 | var client_secret = configJSON.client_secret; 85 | var redirect_url = configJSON.redirect_url; 86 | var iam_url = configJSON.iamurl?configJSON.iamurl:iamurl; 87 | var useridentifier = configJSON.user_identifier?configJSON.user_identifier:default_user_identifier; 88 | 89 | mysql_username = configJSON.mysql_username?configJSON.mysql_username:mysql_username; 90 | mysql_password = configJSON.mysql_password?configJSON.mysql_password:mysql_password; 91 | baseURL = configJSON.base_url?configJSON.base_url:baseURL; 92 | version = configJSON.version?configJSON.version:version; 93 | persistenceModule = configJSON.tokenmanagement?configJSON.tokenmanagement:persistenceModule; 94 | 95 | ZCRMRestClient.setClientId(client_id); 96 | ZCRMRestClient.setClientSecret(client_secret); 97 | ZCRMRestClient.setRedirectURL(redirect_url); 98 | ZCRMRestClient.setIAMUrl(iam_url); 99 | ZCRMRestClient.setUserIdentifier(useridentifier); 100 | 101 | } 102 | 103 | ZCRMRestClient.generateAuthTokens = function(user_identifier,grant_token){ 104 | 105 | return new Promise(function(resolve,reject){ 106 | 107 | if(!user_identifier){ 108 | 109 | user_identifier = ZCRMRestClient.getUserIdentifier(); 110 | } 111 | 112 | var config = ZCRMRestClient.getConfig(grant_token); 113 | new OAuth(config,"generate_token"); 114 | var api_url = OAuth.constructurl("generate_token"); 115 | 116 | OAuth.generateTokens(api_url).then(function(response){ 117 | 118 | if(response.statusCode!=200){ 119 | 120 | throw new Error("Problem occured while generating access token from grant token. Response : "+JSON.stringify(response)); 121 | 122 | } 123 | 124 | var persistence_module = require(persistenceModule); 125 | var resultObj = ZCRMRestClient.parseAndConstructObject(response); 126 | resultObj.user_identifier = user_identifier; 127 | 128 | if(resultObj.access_token){ 129 | 130 | persistence_module.saveOAuthTokens(resultObj).then(function(save_resp){ 131 | 132 | ZCRMRestClient.setUserIdentifier(user_identifier), 133 | 134 | resolve(resultObj) 135 | 136 | } 137 | 138 | ); 139 | } 140 | else{ 141 | 142 | throw new Error("Problem occured while generating access token and refresh token from grant token.Response : "+JSON.stringify(response)); 143 | } 144 | }) 145 | }) 146 | } 147 | 148 | 149 | ZCRMRestClient.generateAuthTokenfromRefreshToken = function(user_identifier,refresh_token){ 150 | 151 | return new Promise(function(resolve,reject){ 152 | 153 | if(!user_identifier){ 154 | 155 | user_identifier = ZCRMRestClient.getUserIdentifier(); 156 | } 157 | 158 | var config = ZCRMRestClient.getConfig_refresh(refresh_token); 159 | new OAuth(config,"refresh_access_token"); 160 | var api_url = OAuth.constructurl("generate_token"); 161 | 162 | OAuth.generateTokens(api_url).then(function(response){ 163 | 164 | if(response.statusCode!=200){ 165 | 166 | throw new Error("Problem occured while generating access token from refresh token . Response : "+JSON.stringify(response)); 167 | 168 | } 169 | var persistence_module = require(persistenceModule); 170 | var resultObj = ZCRMRestClient.parseAndConstructObject(response); 171 | 172 | resultObj.user_identifier = user_identifier; 173 | resultObj.refresh_token = refresh_token; 174 | 175 | if(resultObj.access_token){ 176 | 177 | persistence_module.saveOAuthTokens(resultObj).then(function(save_response){ 178 | 179 | ZCRMRestClient.setUserIdentifier(user_identifier), 180 | resolve(resultObj) 181 | 182 | } 183 | 184 | ); 185 | } 186 | else{ 187 | throw new Error("Problem occured while generating access token from refresh token. Response : "+JSON.stringify(response)); 188 | 189 | } 190 | 191 | 192 | }) 193 | }) 194 | }; 195 | 196 | 197 | ZCRMRestClient.getConfig = function(grant_token){ 198 | 199 | var config = {}; 200 | 201 | config.client_id = ZCRMRestClient.getClientId(); 202 | config.client_secret = ZCRMRestClient.getClientSecret(); 203 | config.code = grant_token; 204 | config.redirect_uri = ZCRMRestClient.getRedirectURL(); 205 | config.grant_type = "authorization_code"; 206 | 207 | return config; 208 | 209 | }; 210 | 211 | ZCRMRestClient.getConfig_refresh = function(refresh_token){ 212 | 213 | var config = {}; 214 | 215 | config.client_id = ZCRMRestClient.getClientId(); 216 | config.client_secret = ZCRMRestClient.getClientSecret(); 217 | config.refresh_token = refresh_token; 218 | config.grant_type = "refresh_token"; 219 | 220 | return config; 221 | 222 | } 223 | 224 | ZCRMRestClient.setClientId = function(clientid){ 225 | 226 | client_id = clientid; 227 | } 228 | 229 | ZCRMRestClient.setClientSecret = function(clientsecret){ 230 | 231 | client_secret = clientsecret; 232 | 233 | } 234 | 235 | ZCRMRestClient.setRedirectURL = function(redirecturl){ 236 | 237 | redirect_url = redirecturl; 238 | } 239 | 240 | ZCRMRestClient.setUserIdentifier = function(useridentifier){ 241 | 242 | user_identifier = useridentifier; 243 | 244 | } 245 | 246 | ZCRMRestClient.setIAMUrl = function(iam_url){ 247 | 248 | iamurl = iam_url; 249 | } 250 | 251 | ZCRMRestClient.setBaseURL = function(baseurl){ 252 | 253 | baseURL = baseurl; 254 | } 255 | 256 | ZCRMRestClient.getClientId = function(){ 257 | 258 | return client_id; 259 | 260 | } 261 | 262 | ZCRMRestClient.getClientSecret = function(){ 263 | 264 | return client_secret; 265 | 266 | } 267 | 268 | ZCRMRestClient.getRedirectURL = function(){ 269 | 270 | return redirect_url; 271 | } 272 | 273 | ZCRMRestClient.getUserIdentifier = function(){ 274 | 275 | if(!user_identifier){ 276 | 277 | return default_user_identifier; 278 | } 279 | return user_identifier; 280 | } 281 | 282 | ZCRMRestClient.getPersistenceModule = function(){ 283 | 284 | return persistenceModule; 285 | } 286 | 287 | ZCRMRestClient.getAPIURL = function(){ 288 | 289 | return baseURL; 290 | } 291 | 292 | ZCRMRestClient.getVersion = function(){ 293 | 294 | return version; 295 | } 296 | ZCRMRestClient.getIAMUrl = function(){ 297 | 298 | return iamurl; 299 | } 300 | ZCRMRestClient.getMySQLUserName = function(){ 301 | 302 | return mysql_username; 303 | } 304 | ZCRMRestClient.getMYSQLPassword = function(){ 305 | 306 | return mysql_password; 307 | 308 | } 309 | ZCRMRestClient.parseAndConstructObject = function(response){ 310 | 311 | var body = response["body"]; 312 | body = JSON.parse(body); 313 | 314 | var date = new Date(); 315 | var current_time = date.getTime(); 316 | 317 | var resultObj = {}; 318 | 319 | if(body.access_token){ 320 | 321 | resultObj.access_token = body.access_token; 322 | 323 | if(body.refresh_token){ 324 | 325 | resultObj.refresh_token = body.refresh_token; 326 | } 327 | if(!body.expires_in_sec){ 328 | body.expires_in = body.expires_in*1000; 329 | } 330 | resultObj.expires_in = body.expires_in+current_time; 331 | } 332 | return resultObj; 333 | 334 | } 335 | 336 | ZCRMRestClient.API = require('./crmapi'); 337 | 338 | module.exports = ZCRMRestClient; --------------------------------------------------------------------------------