Configure the Functions Directory
We will now begin coding the Lead Manager application by configuring the Advanced I/O function.
If you initialized the Advanced I/O Function in Java, its directory, functions/crmCRUD, contains:
- The CRMCRUD.java main function file
- The catalyst-config.json configuration file
- Java library files in the lib folder
- .classpath and .project dependency files
If you initialized the Advanced I/O function in Node.js, its directory, functions/crm_crud, contains:
- The index.js main function file
- The catalyst-config.json configuration file
- Node modules
- package.json and package-lock.json dependency files
You will be adding code in CRMCRUD.java or index.js based on the stack you initialized.
You can use any IDE to configure the function.
For the Java function, you can directly copy the code below and paste it in CRMCRUD.java located in the functions/crmCRUD directory and save the file.
View code for CRMCRUD.java
Copied
import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.logging.Level; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.catalyst.advanced.CatalystAdvancedIOHandler; import com.zc.auth.connectors.ZCConnection; import com.zc.component.ZCUserDetail; import com.zc.component.object.ZCObject; import com.zc.component.object.ZCRowObject; import com.zc.component.object.ZCTable; import com.zc.component.users.ZCUser; import com.zc.component.zcql.ZCQL; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import okhttp3.HttpUrl; import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; public class CRMCRUD implements CatalystAdvancedIOHandler { private static final Logger LOGGER = Logger.getLogger(CRMCRUD.class.getName()); private String apiUrl = "https://www.zohoapis.com/crm/v2/Leads"; private String GET = "GET"; private String POST = "POST"; private String PUT = "PUT"; private String DELETE = "DELETE"; private String CLIENT_ID = "{{YOUR_CLIENT_ID}}"; //Add your client ID private String CLIENT_SECRET = "{{YOUR_CLIENT_SECRET}}"; //Add your client secret OkHttpClient client = new OkHttpClient(); @Override @SuppressWarnings("unchecked") public void runner(HttpServletRequest request, HttpServletResponse response) throws Exception { try { String url = request.getRequestURI(); String method = request.getMethod(); String responseData = ""; String recordID = ""; Pattern p = Pattern.compile("([0-9]+)"); MediaType mediaType = MediaType.parse("application/json"); JSONParser jsonParser = new JSONParser(); JSONObject data = new JSONObject(); org.json.simple.JSONArray reqData = new org.json.simple.JSONArray(); //Fetches the Refresh Token by calling the getRefreshToken() function, and inserts it along with the userID in the Token table if (Pattern.matches("/generateToken", url) && method.equals(GET)) { String code = request.getParameter("code"); String domain = (request.getHeader("host").contains("localhost") ? ("http://" + request.getHeader("host")) : ("https://") + request.getHeader("host").split(":")[0]); ZCUserDetail details = ZCUser.getInstance().getCurrentUser(); ZCObject object = ZCObject.getInstance(); ZCRowObject row = ZCRowObject.getInstance(); row.set("refresh_token", getRefreshToken(code, domain)); row.set("userId", details.getUserId()); ZCTable tab = object.getTable(1824000000686079L); //Replace this with the Table ID of your table tab.insertRow(row); response.setStatus(200); response.sendRedirect(domain + "/app/index.html"); //Fetches the user details by calling the getUserDetails() function } else if (Pattern.matches("/getUserDetails", url) && method.equals(GET)) { ArrayList<ZCRowObject> user = getUserDetails(); JSONObject resp = new JSONObject(); if (user.isEmpty()) { resp.put("userId", null); response.setContentType("application/json"); response.getWriter().write(resp.toJSONString()); response.setStatus(200); } else { resp.put("userId", user.get(0).get("Token", "userId")); response.setContentType("application/json"); response.getWriter().write(resp.toJSONString()); response.setStatus(200); } //Executes various APIs to access, add, or modify leads in CRM //Fetches all leads } else if (Pattern.matches("/crmData", url) && method.equals(GET)) { responseData = getResponse(GET, null); //Fetches a particular lead } else if (Pattern.matches("/crmData/([0-9]+)", url) && method.equals(GET)) { Matcher m = p.matcher(url); if (m.find()) { recordID = m.group(1); } apiUrl = apiUrl + "/" + recordID; responseData = getResponse(GET, null); //Adds a new lead } else if (Pattern.matches("/crmData", url) && method.equals(POST)) { ServletInputStream requestBody = request.getInputStream(); JSONObject jsonObject = (JSONObject) jsonParser.parse(new InputStreamReader(requestBody, "UTF-8")); reqData.add(jsonObject); data.put("data", reqData); RequestBody body = RequestBody.create(mediaType, data.toString()); responseData = getResponse(POST, body); //Deletes a lead } else if (Pattern.matches("/crmData/([0-9]+)", url) && method.equals(DELETE)) { Matcher m = p.matcher(url); if (m.find()) { recordID = m.group(1); } apiUrl = apiUrl + "/" + recordID; responseData = getResponse(DELETE, null); //Edits a lead } else if (Pattern.matches("/crmData/([0-9]+)", url) && method.equals(PUT)) { Matcher m = p.matcher(url); if (m.find()) { recordID = m.group(1); } apiUrl = apiUrl + "/" + recordID; ServletInputStream requestBody = request.getInputStream(); JSONObject jsonObject = (JSONObject) jsonParser.parse(new InputStreamReader(requestBody, "UTF-8")); reqData.add(jsonObject); data.put("data", reqData); RequestBody body = RequestBody.create(mediaType, data.toJSONString()); responseData = getResponse(PUT, body); } else { LOGGER.log(Level.SEVERE, "Error. Invalid Request"); //The actions are logged. You can check the logs from Catalyst Logs. response.setStatus(404); responseData = "Error. Invalid Request"; response.getWriter().write(responseData); } response.setContentType("application/json"); response.getWriter().write(responseData); response.setStatus(200); } catch (Exception e) { LOGGER.log(Level.SEVERE, "Exception in CRM Function ", e); response.setStatus(500); response.getWriter().write(e.toString()); } } @SuppressWarnings("unchecked") //Fetches an Access Token using the Refresh Token public String getAccessToken(Long userId) throws Exception { JSONObject authJson = new JSONObject(); JSONObject connectorJson = new JSONObject(); String query = "SELECT refresh_token FROM Token where UserId=" + userId; ArrayList<ZCRowObject> rowList = ZCQL.getInstance().executeQuery(query); authJson.put("client_id", CLIENT_ID); authJson.put("client_secret", CLIENT_SECRET); authJson.put("auth_url", "https://accounts.zoho.com/oauth/v2/token"); authJson.put("refresh_url", "https://accounts.zoho.com/oauth/v2/token"); authJson.put("refresh_token", rowList.get(0).get("Token", "refresh_token")); connectorJson.put(userId.toString(), authJson); return ZCConnection.getInstance(connectorJson).getConnector(userId.toString()).getAccessToken(); } //Fetches the Refresh Token by passing the required details public String getRefreshToken(String code, String domain) throws Exception { HttpUrl.Builder urlBuilder = HttpUrl.parse("https://accounts.zoho.com/oauth/v2/token").newBuilder(); urlBuilder.addQueryParameter("code", code); urlBuilder.addQueryParameter("client_id", CLIENT_ID); urlBuilder.addQueryParameter("client_secret", CLIENT_SECRET); urlBuilder.addQueryParameter("grant_type", "authorization_code"); urlBuilder.addQueryParameter("redirect_uri", domain + "/server/crmCRUD/generateToken"); String URL = urlBuilder.build().toString(); MediaType mediaType = MediaType.parse("text/plain"); RequestBody body = RequestBody.create(mediaType, ""); Request getResponse = new Request.Builder().url(URL).method(POST, body).build(); JSONParser jsonParser = new JSONParser(); JSONObject data = (JSONObject) jsonParser.parse(client.newCall(getResponse).execute().body().string()); return data.get("refresh_token").toString(); } //Passes the Access Token fetched to obtain the authorization needed to perform each action on the CRM module public String getResponse(String METHOD, RequestBody body) throws Exception { Long userId = ZCUser.getInstance().getCurrentUser().getUserId(); String accessToken = getAccessToken(userId); Request getResponse = new Request.Builder().url(apiUrl).method(METHOD, body) .addHeader("Authorization", "Zoho-oauthtoken " + accessToken).build(); return client.newCall(getResponse).execute().body().string(); } //Fetches the record from the Token table that contains the Refresh Token, by passing the userID public ArrayList<ZCRowObject> getUserDetails() throws Exception { Long userId = ZCUser.getInstance().getCurrentUser().getUserId(); String query = "SELECT * FROM Token where UserId=" + userId; ArrayList<ZCRowObject> rowList = ZCQL.getInstance().executeQuery(query); return rowList; } }
- Client ID in line 37
- Client Secret in line 38
- Table ID in line 68
Install Express and Node Fetch Frameworks for Node.js
The Node.js function requires three frameworks to be installed. You must install the express, node-fetch, and axios dependencies to import those packages in your code.
express
To install Express.js, navigate to the Node function's directory (functions/crm_crud) in your terminal and execute the following command:
$ npm install express --save
This will install the Express module and save the dependencies.
node-fetch
To install node-fetch, execute the following command from the Node function's directory navigate to the Node function's directory (functions/crm_crud):
$ npm install node-fetch
This will install the module.
axios
To install axios, execute the following command from the Node function's directory navigate to the Node function's directory (functions/crm_crud):
$ npm install axios
This will install the module.
This information will also be updated in the package.json file.
{
"name": "crm_crud",
"version": "1.0.0",
"main": "index.js",
"author": "yashashwini.p@zohocorp.com",
"dependencies": {
"axios": "^0.27.2",
"express": "^4.18.1",
"node-fetch": "^3.2.6",
"zcatalyst-sdk-node": "latest"
}
}
You can now add the code in the function file.
Copy the code below and paste it in index.js located in functions/crm_crud directory and save the file.
View code for index.js
Copied
"use strict"; const express = require("express"); const http = require("https"); const app = express(); app.use(express.json()); const catalyst = require("zcatalyst-sdk-node"); const HOST = "www.zohoapis.com"; const AUTH_HOST = "https://accounts.zoho.com/oauth/v2/token"; const PORT = 443; const axios = require("axios"); const CLIENTID = '{{YOUR_CLIENT_ID}}'; //Add your client ID const CLIENT_SECRET = '{{YOUR_CLIENT_SECRET}}'; //Add your client secret app.get("/generateToken", async (req, res) => { try { const catalystApp = catalyst.initialize(req); const code = req.query.code; let userManagement = catalystApp.userManagement(); let userDetails = await userManagement.getCurrentUser(); const domain = `${process.env.X_ZOHO_CATALYST_IS_LOCAL === 'true' ? "http" : "https"}://${process.env.X_ZOHO_CATALYST_IS_LOCAL === 'true' ? req.headers.host : req.headers.host.split(':')[0]}` const refresh_token = await getRefreshToken(code, res, domain); const userId = userDetails.user_id; const catalystTable = catalystApp.datastore().table("Token"); await catalystTable.insertRow({ refresh_token, userId, }); res.status(200).redirect(`${domain}/app/index.html`); } catch (err) { console.log(err); res .status(500) .send({ message: "Internal Server Error. Please try again after sometime.", error: err, }); } }); app.get("/getUserDetails", async (req, res) => { try { const catalystApp = catalyst.initialize(req); const userDetails = await getUserDetails(catalystApp); if (userDetails.length !== 0) { res.status(200).send({ userId: userDetails[0].Token.userId }); } else { res.status(200).send({ userId: null }); } } catch (err) { console.log(err); res .status(500) .send({ message: "Internal Server Error in Getting User Details. Please try again after sometime.", error: err, }); } }); app.get("/crmData", async (req, res) => { try { console.log(); const catalystApp = catalyst.initialize(req); const userDetails = await getUserDetails(catalystApp); const accessToken = await getAccessToken(catalystApp, userDetails); const options = { hostname: HOST, port: PORT, method: "GET", path: `/crm/v2/Leads`, headers: { Authorization: `Zoho-oauthtoken ${accessToken}`, }, }; var data = ""; const request = http.request(options, function (response) { response.on("data", function (chunk) { data += chunk; }); response.on("end", function () { console.log(response.statusCode); res.setHeader("content-type", "application/json"); res.status(200).send(data); }); }); request.end(); } catch (err) { console.log(err); res .status(500) .send({ message: "Internal Server Error. Please try again after sometime.", }); } }); app.get("/crmData/:id", async (req, res) => { try { const catalystApp = catalyst.initialize(req); const userDetails = await getUserDetails(catalystApp); const accessToken = await getAccessToken(catalystApp, userDetails); const options = { hostname: HOST, port: PORT, method: "GET", path: `/crm/v2/Leads/${req.params.id}`, headers: { Authorization: `Zoho-oauthtoken ${accessToken}`, }, }; var data = ""; const request = http.request(options, function (response) { response.on("data", function (chunk) { data += chunk; }); response.on("end", function () { res.setHeader("content-type", "application/json"); res.status(200).send(data); }); }); request.end(); } catch (err) { console.log(err); res.status(500).send({ message: "Internal Server Error. Please try again after sometime.", }); } }); app.post("/crmData", async (req, res) => { try { const catalystApp = catalyst.initialize(req); const createData = req.body; const reqData = []; reqData.push(createData); const data = { data: reqData, }; if (!createData) { res.status(400).send({ message: "Data Not Found" }); } const userDetails = await getUserDetails(catalystApp); const accessToken = await getAccessToken(catalystApp, userDetails); const options = { hostname: HOST, port: PORT, method: "POST", path: `/crm/v2/Leads`, headers: { Authorization: `Zoho-oauthtoken ${accessToken}`, "Content-Type": "application/json", }, }; const request = http.request(options, function (response) { res.setHeader("content-type", "application/json"); response.pipe(res); }); request.write(JSON.stringify(data)); request.end(); } catch (err) { console.log(err); res .status(500) .send({ message: "Internal Server Error. Please try again after sometime.", }); } }); app.put("/crmData/:id", async (req, res) => { try { const catalystApp = catalyst.initialize(req); const updateData = req.body; const reqData = []; reqData.push(updateData); const data = { data: reqData, }; if (!updateData) { res.status(400).send({ message: "Update Data Not Found" }); } const userDetails = await getUserDetails(catalystApp); const accessToken = await getAccessToken(catalystApp, userDetails); const options = { hostname: HOST, port: PORT, method: "PUT", path: `/crm/v2/Leads/${req.params.id}`, headers: { Authorization: `Zoho-oauthtoken ${accessToken}`, "Content-Type": "application/json", }, }; const request = http.request(options, function (response) { res.setHeader("content-type", "application/json"); response.pipe(res); }); request.write(JSON.stringify(data)); request.end(); } catch (err) { console.log(err); res .status(500) .send({ message: "Internal Server Error. Please try again after sometime.", }); } }); app.delete("/crmData/:id", async (req, res) => { console.log(`/crm/v2/Leads/${req.params.id}`); try { const catalystApp = catalyst.initialize(req); const userDetails = await getUserDetails(catalystApp); const accessToken = await getAccessToken(catalystApp, userDetails); const options = { hostname: HOST, port: PORT, method: "DELETE", path: `/crm/v2/Leads/${req.params.id}`, headers: { Authorization: `Zoho-oauthtoken ${accessToken}`, "Content-Type": "application/json", }, }; const request = http.request(options, function (response) { res.setHeader("content-type", "application/json"); response.pipe(res); }); request.end(); } catch (err) { console.log(err); res.status(500).send({ message: "Internal Server Error. Please try again after sometime.", }); } }); async function getAccessToken(catalystApp, userDetails) { const refresh_token = userDetails[0].Token.refresh_token; const userId = userDetails[0].Token.userId; const credentials = { [userId]: { client_id: CLIENTID, client_secret: CLIENT_SECRET, auth_url: AUTH_HOST, refresh_url: AUTH_HOST, refresh_token, }, }; const accessToken = await catalystApp.connection(credentials).getConnector(userId).getAccessToken(); return accessToken; } async function getRefreshToken(code, res, domain) { try { const url = `${AUTH_HOST}?code=${code}&client_id=${CLIENTID}&client_secret=${CLIENT_SECRET}&grant_type=authorization_code&redirect_uri=${domain}/server/crm_crud/generateToken`; const response = await axios({ method: "POST", url }); return response.data.refresh_token; } catch (err) { console.log(err); res.status(500).send({ message: "Internal Server Error. Please try again after sometime.", error: err, }); } } async function getUserDetails(catalystApp) { let userDetails = await catalystApp.userManagement().getCurrentUser(); let userDetail = await catalystApp.zcql().executeZCQLQuery(`SELECT * FROM Token where UserId=${userDetails.user_id}`); return userDetail; } module.exports = app;
- Client ID in line 11
- Client Secret in line 12
The functions directory is now configured. We will discuss the function and client code, after you configure the client.