Configure the Functions Directory
Next, we’ll begin coding the Museum Finder application by configuring the functions.
All four Java functions contain the following files in their individual function directories:
- The main Java function file
- The catalyst-config.json configuration file
- The JAR library files
- .classpath and .project dependency files
The Node.js function contains the following files in its directory:
- The index.js main function file
- The catalyst-config.json configuration file
- Node modules
- package.json and package-lock.json dependency files
We will be adding code in the main Java files of each Java function and in the index.js file of the Node.js function.
You can use any IDE of your choice to configure the function.
Copy the code given below and paste it in RatingProcessor.java located in functions/RatingProcessor directory and save the file.
RatingProcessor.javacopyimport com.catalyst.Context; import com.catalyst.basic.BasicIO; import com.catalyst.basic.ZCFunction; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.logging.Logger; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import java.io.File; import java.io.FileReader; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.logging.Level; import com.zc.common.ZCProject; import com.zc.component.cache.ZCCache; import okhttp3.HttpUrl; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; public class RatingProcessor implements ZCFunction { private static final Logger LOGGER = Logger.getLogger(RatingProcessor.class.getName()); private static Map<String, Integer> map = new HashMap() { { put("Excellent",0); put("Very good",1); put("Average",2); put("Poor",3); put("Terrible",4); } }; @Override @SuppressWarnings("unchecked") public void runner(Context context, BasicIO basicIO) throws Exception { try { Integer topCount = 10; if(basicIO.getParameter("count") != null) { topCount = Integer.parseInt(basicIO.getParameter("count").toString()); } String travellerRating = (String) basicIO.getParameter("type"); Integer type = 0; if(travellerRating != null) { if(map.containsKey(travellerRating)) { type = map.get(travellerRating); } } String url = "https://www.zoho.com/catalyst/downloads/static/tutorial/dataset/traveller-rating.json"; ZCProject.initProject(); HttpUrl.Builder httpBuilder = HttpUrl.parse(url).newBuilder(); OkHttpClient client = new OkHttpClient().newBuilder().build(); Request.Builder requestsBuilder = new Request.Builder(); requestsBuilder.url(httpBuilder.build()); requestsBuilder.get(); Response responses = client.newCall(requestsBuilder.build()).execute(); String responsebody = responses.body().string(); JSONParser parser = new JSONParser(); JSONObject dataJSON = (JSONObject) parser.parse(responsebody); HashMap<String, Long> execellentRating = new HashMap<>(); Iterator<String> itrJson = dataJSON.keySet().iterator(); while(itrJson.hasNext()) { String museumName = itrJson.next(); List<String> ratingList = new ObjectMapper().convertValue(dataJSON.get(museumName), List.class); Long exellentRating = Long.parseLong(ratingList.get(type)); execellentRating.put(museumName, exellentRating); } execellentRating = RatingProcessor.sortByValue(execellentRating); List<String> execellentMuseum = new ArrayList<>(); itrJson = execellentRating.keySet().iterator(); Integer i= 0; while(i++ < topCount && itrJson.hasNext()) { execellentMuseum.add(itrJson.next()); } basicIO.write(new ObjectMapper().writeValueAsString(execellentMuseum)); } catch(Exception e) { LOGGER.log(Level.SEVERE,"Exception in RatingProcessor",e); basicIO.setStatus(500); } } public static HashMap<String, Long> sortByValue(HashMap<String, Long> hm) { // Create a list from elements of HashMap List<Map.Entry<String, Long> > list = new LinkedList<Map.Entry<String, Long> >(hm.entrySet()); // Sort the list Collections.sort(list, new Comparator<Map.Entry<String, Long> >() { public int compare(Map.Entry<String, Long> o1, Map.Entry<String, Long> o2) { return (o2.getValue()).compareTo(o1.getValue()); } }); // Put the data from the sorted list to the HashMap HashMap<String, Long> temp = new LinkedHashMap<String, Long>(); for (Map.Entry<String, Long> aa : list) { temp.put(aa.getKey(), aa.getValue()); } return temp; } }
Copy the code given below and paste it in TravellerTypeRatingProcessor.java located in functions/TravellerTypeRatingProcessor directory and save the file.
TravellerTypeRatingProcessor.javacopyimport java.io.File; import java.io.FileReader; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import com.catalyst.Context; import com.catalyst.basic.BasicIO; import com.catalyst.basic.ZCFunction; import com.fasterxml.jackson.databind.ObjectMapper; import com.zc.common.ZCProject; import okhttp3.HttpUrl; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; public class TravellerTypeRatingProcessor implements ZCFunction { private static final Logger LOGGER = Logger.getLogger(TravellerTypeRatingProcessor.class.getName()); private static Map<String, Integer> map = new HashMap() { { put("Families",0); put("Couples",1); put("Solo",2); put("Business",3); put("Friends",4); } }; @Override public void runner(Context context, BasicIO basicIO) throws Exception { try { Integer topCount = 10; String travellerType = (String) basicIO.getParameter("type"); Integer type = 0; if(travellerType != null) { if(map.containsKey(travellerType)) { type = map.get(travellerType); } } if(basicIO.getParameter("count") != null) { topCount = Integer.parseInt(basicIO.getParameter("count").toString()); } String url = "https://www.zoho.com/catalyst/downloads/static/tutorial/dataset/traveller-type-rating.json"; ZCProject.initProject(); HttpUrl.Builder httpBuilder = HttpUrl.parse(url).newBuilder(); OkHttpClient client = new OkHttpClient().newBuilder().build(); Request.Builder requestsBuilder = new Request.Builder(); requestsBuilder.url(httpBuilder.build()); requestsBuilder.get(); Response responses = client.newCall(requestsBuilder.build()).execute(); String responsebody = responses.body().string(); JSONParser parser = new JSONParser(); JSONObject dataJSON = (JSONObject) parser.parse(responsebody); HashMap<String, Long> execellentRating = new HashMap<>(); Iterator<String> itrJson = dataJSON.keySet().iterator(); while(itrJson.hasNext()) { String museumName = itrJson.next(); List<String> ratingList = new ObjectMapper().convertValue(dataJSON.get(museumName), List.class); Long rating = Long.parseLong(ratingList.get(type)); execellentRating.put(museumName, rating); } execellentRating = TravellerTypeRatingProcessor.sortByValue(execellentRating); List<String> execellentMuseum = new ArrayList<>(); itrJson = execellentRating.keySet().iterator(); Integer i= 0; while(i++ < topCount && itrJson.hasNext()) { execellentMuseum.add(itrJson.next()); } basicIO.write(new ObjectMapper().writeValueAsString(execellentMuseum)); } catch(Exception e) { LOGGER.log(Level.SEVERE,"Exception in TravellerTypeRatingProcessor",e); //You can view this log from Logs in the Catalyst console basicIO.setStatus(500); } } public static HashMap<String, Long> sortByValue(HashMap<String, Long> hm) { //Create a list from elements of HashMap List<Map.Entry<String, Long> > list = new LinkedList<Map.Entry<String, Long> >(hm.entrySet()); //Sort the list Collections.sort(list, new Comparator<Map.Entry<String, Long> >() { public int compare(Map.Entry<String, Long> o1, Map.Entry<String, Long> o2) { return (o2.getValue()).compareTo(o1.getValue()); } }); //Put the data from the sorted list to the HashMap HashMap<String, Long> temp = new LinkedHashMap<String, Long>(); for (Map.Entry<String, Long> aa : list) { temp.put(aa.getKey(), aa.getValue()); } return temp; } }
Copy the code given below and paste it in MergeDataSet.java located in functions/MergeDataSet directory and save the file.
MergeDataSet.javacopyimport com.catalyst.Context; import com.catalyst.basic.BasicIO; import com.catalyst.basic.ZCFunction; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.logging.Logger; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import com.zc.common.ZCProject; import com.zc.component.cache.ZCCache; import com.zc.component.object.ZCTable; public class MergeDataSet implements ZCFunction { private static final Logger LOGGER = Logger.getLogger(MergeDataSet.class.getName()); @Override public void runner(Context context, BasicIO basicIO) throws Exception { try { Object ratingList = basicIO.getArgument("rating_list"); Object typeRatingList = basicIO.getArgument("traveller_type_rating_list"); ObjectMapper mapper = new ObjectMapper(); List<String> travellerRatingList = mapper.readValue(ratingList.toString(), mapper.getTypeFactory().constructCollectionType(List.class, String.class));; List<String> travellerTypeRatingList = mapper.readValue(typeRatingList.toString(), mapper.getTypeFactory().constructCollectionType(List.class, String.class));; List<String> finalList = new ArrayList<>(); for(int i=0 ; i< travellerTypeRatingList.size() ; i++) { if(travellerRatingList.contains(travellerTypeRatingList.get(i))) { finalList.add(travellerTypeRatingList.get(i).toString()); } } JSONObject respJson = new JSONObject(); respJson.put("museum_list", finalList); LOGGER.log(Level.INFO, "Museum Count"+finalList.size()); //You can view this log from Logs in the Catalyst console basicIO.write(new ObjectMapper().writeValueAsString(respJson)); } catch(Exception e) { e.printStackTrace(); LOGGER.log(Level.SEVERE,"Exception in MergeDataSet",e); //You can view this log from Logs in the Catalyst console basicIO.setStatus(500); } } }
Copy the code given below and paste it in Mailer.java located in functions/Mailer directory and save the file.
Mailer.javacopyimport java.util.logging.Level; import java.util.logging.Logger; import org.json.simple.JSONObject; import com.catalyst.Context; import com.catalyst.basic.BasicIO; import com.catalyst.basic.ZCFunction; import com.zc.common.ZCProject; import com.zc.component.mail.ZCMail; import com.zc.component.mail.ZCMailContent; public class Mailer implements ZCFunction { private static final Logger LOGGER = Logger.getLogger(Mailer.class.getName()); private static final String SUBJECT = "Museum Search Results"; private static final String FROM_MAIL = "emma@zylker.com"; //Enter the sender's email address you configured earlier @Override public void runner(Context context, BasicIO basicIO) throws Exception { try { ZCProject.initProject(); Object content = basicIO.getArgument("content"); //Obtains the final list from the MergeDataSet function Object mailId = basicIO.getArgument("mail_id"); //Obtains the user's email address from the provided input if(content != null) { String mailContent = content.toString(); ZCMailContent mail = ZCMailContent.getInstance(); mail.setSubject(SUBJECT); mail.setFromEmail(FROM_MAIL); if(mailId != null) { mail.setToEmail(mailId.toString()); } else { basicIO.write(constructErrorMessage("mail_id not found")); basicIO.setStatus(400); return; } mail.setContent(mailContent); mail.setHtmlMode(false); ZCMail.getInstance().sendMail(mail); basicIO.setStatus(200); basicIO.write(constructSuccessMessage("Mail has been sent to "+mailId+" successfully")); } } catch(Exception e) { LOGGER.log(Level.SEVERE,"Exception in Mailer",e); //You can view this log from Logs in the Catalyst console basicIO.setStatus(500); basicIO.write(constructErrorMessage(e.getMessage())); } } private JSONObject constructErrorMessage(String message) { JSONObject errorJSON = new JSONObject(); errorJSON.put("status", "failure"); errorJSON.put("message", message); return errorJSON; } private JSONObject constructSuccessMessage(String message) { JSONObject errorJSON = new JSONObject(); errorJSON.put("status", "success"); errorJSON.put("message", message); return errorJSON; } }
Note: Provide the sender's email address you configured earlier, in line 19.
Install Express
Before we code the Node.js function, we must install the Express Node.js dependencies in the directory.
To install Express.js in your local machine, navigate to the function’s directory (functions/circuit) in your terminal and execute the following command:
copy$npm install express --saveThis will install the Express module and save the dependencies.
This information will also be updated in the package.json file.
Let’s code the index.js file now.
Copy the code given below and paste it in index.js located in functions/circuit directory and save the file.
index.jscopy'use strict'; var express = require('express'); var app = express(); var catalyst = require('zcatalyst-sdk-node'); app.use(express.json()); app.post('/triggerCircuit', (req, res) => { var CatalystApp = catalyst.initialize(req); var body = req.body; console.log(body); let execName = new Date().getTime().toString(); console.log(execName); let promiseResult = CatalystApp.circuit().execute(1028000000084051,execName, body); //Provide the circuit ID of the circuit you create in the console promiseResult.then((functionResponse) => { console.log(functionResponse); res.status(200).send({ message: "Please check your email for the list of museums." }); }).catch(err => { console.log(err); //You can view this log from Logs in the Catalyst console res.status(500).send({ message: "Please try again after sometime!" }); }) }); module.exports = app;
Note:After we create the circuit in the Catalyst console, ensure that you provide the circuit ID in this function in line 14.Let’s quickly go over the functions that we configured.
Circuit:
The Advanced I/O circuit function contains an API /triggerCircuit that triggers the execution of the circuit which is configured in the Catalyst console.
Note: We will configure the circuit after deploying the project resources to the remote console.The circuit function passes the input data obtained from the end-user in the client application to the circuit while triggering its execution. This data is passed to the circuit function by main.js in the client directory.
After the circuit has completed its execution, it will pass a message to be displayed in the client application requesting the user to check their email address for the list of museums that satisfy their criteria. This message is configured in this function.
RatingProcessor and TravellerTypeRatingProcessor:
- The RatingProcessor function stores the following ratings of the museums in a Java HashMap: Excellent, Very Good, Average, Poor, Terrible.
- The TravellerTypeRatingProcessor function stores the following traveller type suitability of the museums in a Java HashMap: Families, Couples, Solo, Business, Friends.
- In addition to the rating and traveller type, the user provides an input for the number of museums that they require to be listed in the results. This value is stored in the count variable and is processed by both the functions.
- The functions make calls to traveller-rating.json or traveller-type-rating.json datasets respectively, that are hosted by Catalyst. These datasets contain contain records of ratings and traveller type suitability provided by users in various websites.
- Records are fetched from these datasets by both functions, based on the input provided by the user. For example, if the user selected Excellent as the rating type, museums with that rating are identified in the traveller-rating.json dataset. The museums with the highest number of Excellent ratings, not exceeding the count value provided by the user, are then sorted in a descending order and fetched. The records matching the traveller type input provided by the user are also fetched similarly from traveller-type-rating.json dataset..
- The records that are fetched by both the functions are parsed as JSON objects and are written to ObjectMapper classes. They are then sorted in a list and added to new HashMaps in both the functions.
MergeDataSet:
- The MergeDataSet function obtains the records fetched by RatingProcessor and TravellerTypeRatingProcessor functions as arguments.
- The function then intersects both the lists and checks for museum results that are present in both the lists.
- It then adds the museums that are present in both the lists into a final list and returns the list as a JSON response.
Mailer:
- The Mailer function obtains the final list from the MergeDataSet function in the circuit that we will configure. This is set as the mail content. It also obtains the email address of the user from the input they provide.
- We can configure the email’s subject and sender’s address in the function. The Mailer function then sends an email to the user’s email address with the fetched content.
The functions directory is now configured.
Last Updated 2023-07-21 16:41:15 +0530 +0530