# Museum Finder -------------------------------------------------------------------------------- title: "Introduction" description: "Build a Museum Finder web application using Catalyst Functions that helps you find museums based on ratings and traveller type criteria, and automate its workflow using Catalyst Circuits." last_updated: "2026-03-18T07:41:08.684Z" source: "https://docs.catalyst.zoho.com/en/tutorials/museum-finder/introduction/" service: "All Services" related: - Project Directory Structure (/en/cli/v1/project-directory-structure/introduction/) -------------------------------------------------------------------------------- # Museum Finder ### Introduction This tutorial will help you build a museum finder application that lists museums in the United States of America, based on certain criteria that the users input. The application fetches information from public datasets hosted by Catalyst that contain records of ratings and traveller type suitability of museums from various websites. The application processes these in parallel and emails the results to the user. The application's workflow is automated using a {{%link href="/en/serverless/help/circuits/introduction/" %}}Catalyst Circuit{{%/link%}}, which orchestrates and executes each task to be carried out automatically. Refer to the {{%link href="/en/serverless/help/circuits/introduction" %}}Circuits help page{{%/link%}} for detailed help on Circuits. The client application will look like this: You can access a working application and test its functioning here: {{%link href="https://museumfinder.catalystserverlessapp.com/app/" %}}Try the App!{{%/link%}} The Museum Finder application utilizes components from the following Catalyst Services: 1. {{%link href="/en/serverless/" %}}{{%bold%}}Catalyst Serverless{{%/bold%}}{{%/link%}}: - {{%bold%}}{{%link href="/en/serverless/help/circuits/introduction" %}}Circuit:{{%/link%}}{{%/bold%}} Automates the entire workflow. <br/><br/> {{%note%}}{{%bold%}}Note:{{%/bold%}} Circuits is currently not available to Catalyst users accessing from the EU, AU, IN, JP, SA or CA data centers. You will not be able to execute Cron, Event, or Advanced I/O functions in a Catalyst circuit. {{%/note%}} - {{%bold%}}{{%link href="/en/serverless/help/functions/basic-io/" %}}Basic I/O Functions{{%/link%}}{{%/bold%}}: Contains the following Basic I/O functions in the **Java** runtime: - {{%badge%}}{{%bold%}}RatingProcessor{{%/bold%}}{{%/badge%}}: Processes the rating input provided by the user and selects museums that fulfil the criteria - {{%badge%}}{{%bold%}}TravellerTypeRatingProcessor{{%/bold%}}{{%/badge%}}: Processes the traveller type input provided by the user and selects museums that fulfil the criteria - {{%badge%}}{{%bold%}}MergeDataSet{{%/bold%}}{{%/badge%}}: Merges the results obtained from {{%badge%}}RatingProcessor{{%/badge%}} and {{%badge%}}TravellerTypeRatingProcessor{{%/badge%}} - {{%badge%}}{{%bold%}}Mailer{{%/bold%}}{{%/badge%}}: Emails the results to the user - {{%bold%}}{{%link href="/en/serverless/help/functions/advanced-io/" %}}Advanced I/O Function{{%/link%}}{{%/bold%}}: An Advanced I/O function in the **Node.js** runtime that automatically triggers the circuit to execute when the user submits the input form 2. {{%link href="/en/cloud-scale/" %}}{{%bold%}}Catalyst Cloud Scale{{%/bold%}}{{%/link%}}: - {{%bold%}}Web Client Hosting{{%/bold%}} The front end of the application will be hosted on Catalyst using the {{%link href="/en/cloud-scale/help/web-client-hosting/introduction" %}}Web Client Hosting{{%/link%}} component. You will be initializing the client as a Basic web app. #### Application Architecture * When the user gives an input choosing the museum rating and traveller type suitability they require, the {{%badge%}}RatingProcessor{{%/badge%}} and {{%badge%}}TravellerTypeRatingProcessor{{%/badge%}} functions fetch two independent public datasets that contain records of ratings and traveller type of museums. * Both functions are processed in parallel, and results matching the criteria requested by the user are selected from both datasets. * The results from {{%badge%}}RatingProcessor{{%/badge%}} and {{%badge%}}TravellerTypeRatingProcessor{{%/badge%}} functions are sent to the {{%badge%}}MergeDataSet{{%/badge%}} function which selects the museums that appear in both functions' results and merges them as one result. * This result is passed to the {{%badge%}}Mailer{{%/badge%}} function which sends the email to the user with the final museum list. * This entire workflow is automated by a circuit that we will configure in this application. After the user submits the input, the circuit automatically executes the functions, processes conditions, and generates results as depicted below:<br /> <br /> -------------------------------------------------------------------------------- title: "Prerequisites" description: "Build a Museum Finder web application using Catalyst Functions that helps you find museums based on ratings and traveller type criteria, and automate its workflow using Catalyst Circuits." last_updated: "2026-03-18T07:41:08.684Z" source: "https://docs.catalyst.zoho.com/en/tutorials/museum-finder/prerequisites/" service: "All Services" related: - CLI Command Reference (/en/cli/v1/cli-command-reference/) - Install CLI (/en/getting-started/installing-catalyst-cli/) - Login CLI (/en/cli/v1/login/login-from-cli/) -------------------------------------------------------------------------------- # Prerequisites Before you begin building the application, you must have the following prerequisites installed on your system: 1. {{%bold%}}Catalyst CLI{{%/bold%}}<br /><br /> Catalyst CLI contains a host of tools that enable you to initialize, develop, test, and deploy the components of your application from your local machine. We will be working with Catalyst CLI in this tutorial. You must perform these actions: 1. {{%bold%}}Install Catalyst CLI:{{%/bold%}} Catalyst CLI is installed through NPM. You must therefore have NPM and Node.js installed on your system before you install the CLI. Refer to the {{%link href="/en/getting-started/installing-catalyst-cli/" %}}**Install Catalyst CLI help page**{{%/link%}} for details on the pre-requisites and the steps to install it. 2. {{%bold%}}Login Catalyst CLI:{{%/bold%}} After you install Catalyst CLI, you must authenticate the CLI with your Catalyst account before using it. Refer to the {{%link href="/en/cli/v1/login/login-from-cli/" %}}**CLI Login help page**{{%/link%}} for the steps to login from Catalyst CLI and the various options available for it. 2. {{%bold%}}Any IDE tool for Function and client code development{{%/bold%}}<br /><br /> You can use any IDE to work with the function and the client code. Some popular choices include Visual Studio Code, IntelliJ IDEA, Eclipse, and Sublime Text. Download and install an IDE of your choice in your system. {{%info image="/images/tutorials/todo-list/vscode.png"%}}If you are a Visual Studio Code IDE user, you can install the {{%bold%}}Catalyst Tools{{%/bold%}} extension, and use your IDE itself in place of the CLI. You can find more details about the Catalyst VS Code extension from this {{%link href="/en/catalyst-extensions/vs-code-extension/introduction/" %}}help section{{%/link%}}.{{%/info%}} -------------------------------------------------------------------------------- title: "Create a project" description: "Build a Museum Finder web application using Catalyst Functions that helps you find museums based on ratings and traveller type criteria, and automate its workflow using Catalyst Circuits." last_updated: "2026-03-18T07:41:08.684Z" source: "https://docs.catalyst.zoho.com/en/tutorials/museum-finder/create-project/" service: "All Services" related: - Project Directory (/en/cli/v1/project-directory-structure/introduction) - Set up Catalyst Projects (/en/getting-started/catalyst-projects) -------------------------------------------------------------------------------- # Create a Project Let's {{%link href="/en/getting-started/catalyst-projects" %}}create a Catalyst project{{%/link%}} from the Catalyst console. 1. Log in to the {{%link href="https://console.catalyst.zoho.com/baas/index" %}}Catalyst console{{%/link%}} and click {{%bold%}}Create a new Project{{%/bold%}}.<br /> <br /> 2. Enter the project's name as '{{%bold%}}MuseumFinder{{%/bold%}}' in the pop-up window, and click **Create**. <br /> <br /> Your project will be created and opened. <br /> -------------------------------------------------------------------------------- title: "Configure the email address" description: "Build a Museum Finder web application using Catalyst Functions that helps you find museums based on ratings and traveller type criteria, and automate its workflow using Catalyst Circuits." last_updated: "2026-03-18T07:41:08.693Z" source: "https://docs.catalyst.zoho.com/en/tutorials/museum-finder/configure-email-address/" service: "All Services" related: - Catalyst Mail (/en/cloud-scale/help/mail/introduction) -------------------------------------------------------------------------------- # Configure Email Address Before we set up the project directory or configure the circuit, let's quickly configure the sender email address for sending emails to the users with the list of museums. We will configure the email address in {{%link href="/en/cloud-scale/help/mail/introduction" %}}Catalyst Mail{{%/link%}}. 1. Navigate to {{%bold%}}Cloud Scale{{%/bold%}}, then {{%bold%}}Mail{{%/bold%}}. Click {{%bold%}}Add Email{{%/bold%}}.<br /> <br /> 2. Enter the name and email address of the sender, then click {{%bold%}}Add Email{{%/bold%}}.<br /> <br /> 3. You must now verify the email address. Click {{%bold%}}Click to confirm{{%/bold%}}.<br /> <br /> An email with a verification code will be sent to your email address. 4. Copy the verification code and paste it into the text box and click {{%bold%}}Confirm{{%/bold%}}.<br /> <br /> Your email address is now verified. You can now use this email address to send emails from Catalyst. <br /> -------------------------------------------------------------------------------- title: "Initialize the project" description: "Build a Museum Finder web application using Catalyst Functions that helps you find museums based on ratings and traveller type criteria, and automate its workflow using Catalyst Circuits." last_updated: "2026-03-18T07:41:08.693Z" source: "https://docs.catalyst.zoho.com/en/tutorials/museum-finder/initialize-project/" service: "All Services" related: - Initialize CLI Resources (/en/cli/v1/initialize-resources/introduction) - Project Directory Structure (/en/cli/v1/project-directory-structure/introduction) -------------------------------------------------------------------------------- # Initialize the Project from the CLI You can now begin working on your Catalyst project from the CLI. The first step is to initialize the project in an empty directory. This will be the home directory of your project and all of the project files will be saved in it. You can learn more about this from the {{%link href="/en/cli/v1/project-directory-structure/introduction/" %}}Project Directory Structure help page{{%/link%}}. You can learn about initializing a project in detail from the {{%link href="/en/cli/v1/initialize-resources/introduction" %}}CLI help documentation{{%/link%}}. For the Museum Finder application, we will first initialize the client and the Basic I/O function components. We will then add the other functions to the project directory. 1. Create a folder for the project on your local machine and navigate to it from the terminal. 2. Initialize a project by executing the following command from that directory:<br /> {{%cli%}} catalyst init{{%/cli%}} 3. The CLI will now ask you to associate a Catalyst project with the directory. Associate it with the project that we created earlier from the console. Select **MuseumFinder** from the list and click Enter. <br /> 4. Select **Functions** and **Client** using the space bar. Press the Enter key to initialize. <br /> 5. The CLI will initiate the function setup. Select {{%bold%}}BasicIO{{%/bold%}} as the function type. <br /> 6. Select the latest runtime of **Java** as the function stack. <br /> 7. Enter the name of the Java function and the folder as "{{%bold%}}RatingProcessor{{%/bold%}}" and press {{%bold%}}Enter{{%/bold%}}. This is also its reference name. Enter the same name as its main class name as well, and press {{%bold%}}Enter{{%/bold%}}. The Catalyst Java SDK will now be downloaded to your system. <br /> {{%note%}}{{%bold%}}Note:{{%/bold%}} Ensure that you enter the main class name and the function's name exactly as instructed, because the application's code contains the same names.{{%/note%}} 8. The CLI will initiate the client set up next. Select **Basic web app** as your client type. <br /> 9. Enter "{{%bold%}}MuseumFinderClient{{%/bold%}}" as the name for the client package and press {{%bold%}}Enter{{%/bold%}}. You can enter any name you need. <br /> The function and client directories will be created in the standard structure. Catalyst initialization is now complete. ### Add Functions to the Project Let's now add the other functions to the project. 1. Execute the following command from the project directory to {{%link href="/en/cli/v1/working-with-functions/add-functions/" %}}add a new function{{%/link%}} to it: {{%cli%}} catalyst functions:add{{%/cli%}} 2. The CLI will initiate the function setup. Follow the same steps as function initialization discussed above. Select {{%bold%}}BasicIO{{%/bold%}} as the function type and the required version of Java as the function stack. Enter the function name and class name as "{{%bold%}}TravellerTypeRatingProcessor{{%/bold%}}". <br /> Now, add two other Basic I/O functions of the Java stack in the same way. Enter their function names and class names as "{{%bold%}}MergeDataSet{{%/bold%}}" and "{{%bold%}}Mailer{{%/bold%}}" respectively. {{%note%}}{{%bold%}}Note:{{%/bold%}} Ensure that you enter the main class name and the function's name of all the functions exactly as instructed, because the application's code contains the same names.{{%/note%}} The Java functions are now created. Let's now add the Node.js function to the project in the same way as above. 1. Execute the following command from the project directory again: {{%cli%}} catalyst functions:add{{%/cli%}} 2. The CLI will initiate the function setup. Select {{%bold%}}AdvancedIO{{%/bold%}} as the function type and latest runtime of {{%bold%}}nodeJS{{%/bold%}} as the function stack. Enter the package name as "{{%bold%}}circuit{{%/bold%}}", "{{%bold%}}index.js{{%/bold%}}" as the entry point, and your email address. You can press {{%bold%}}Enter{{%/bold%}} to fill the default values. The CLI will prompt the initialization of the Node dependencies. Press {{%bold%}}Y{{%/bold%}} to confirm the installation, and press {{%bold%}}Enter{{%/bold%}} to confirm your choice. The Node modules will be installed. <br /> {{%note%}}{{%bold%}}Note:{{%/bold%}} Ensure that you enter the function's package name exactly as instructed, because the application's code contains the same name.{{%/note%}} We have now initialized all the functions and client components in the project. The {{%link href="/en/cli/v1/project-directory-structure/client-directory" %}}client directory{{%/link%}} and the {{%link href="/en/cli/v1/project-directory-structure/functions-directory" %}}functions directory{{%/link%}} will be created in your project directory with the configuration files and dependencies, along with the {{%link href="/en/cli/v1/project-directory-structure/catalyst-json" %}}{{%badge%}}catalyst.json{{%/badge%}}{{%/link%}} and a hidden {{%badge%}}.catalystrc{{%/badge%}} file. This is the structure of your project directory. <br /> -------------------------------------------------------------------------------- title: "Configure functions" description: "Build a Museum Finder web application using Catalyst Functions that helps you find museums based on ratings and traveller type criteria, and automate its workflow using Catalyst Circuits." last_updated: "2026-03-18T07:41:08.693Z" source: "https://docs.catalyst.zoho.com/en/tutorials/museum-finder/configure-functions/" service: "All Services" related: - Basic I/O Functions (/en/serverless/help/functions/basic-io/) - Advanced I/O Functions (/en/serverless/help/functions/advanced-io/) - Functions Directory Structure (/en/cli/v1/project-directory-structure/functions-directory) -------------------------------------------------------------------------------- # 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 {{%link href="/en/cli/v1/project-directory-structure/functions-directory" %}}function directories{{%/link%}}: * The main Java function file * The {{%badge%}}catalyst-config.json{{%/badge%}} configuration file * The JAR library files * {{%badge%}}.classpath{{%/badge%}} and {{%badge%}}.project{{%/badge%}} dependency files The Node.js function contains the following files in its directory: * The {{%badge%}}index.js{{%/badge%}} main function file * The {{%badge%}}catalyst-config.json{{%/badge%}} configuration file * Node modules * {{%badge%}}{{%link href="https://docs.npmjs.com/files/package.json" %}}package.json{{%/link%}}{{%/badge%}} and {{%badge%}}{{%link href="https://docs.npmjs.com/configuring-npm/package-lock-json.html" %}}package-lock.json{{%/link%}}{{%/badge%}} dependency files We will be adding code in the main Java files of each Java function and in the {{%badge%}}index.js{{%/badge%}} file of the Node.js function. You can use any IDE of your choice to configure the function. {{%note%}}{{%bold%}}Note:{{%/bold%}} Please go through the code in this section to make sure you fully understand it. We will discuss the code of the functions at the end of the section.{{%/note%}} <br /> Copy the code below and paste it in {{%badge%}}RatingProcessor.java{{%/badge%}} located in {{%badge%}}functions/RatingProcessor{{%/badge%}} directory and save the file. {{% panel_with_adjustment header="RatingProcessor.java" footer="button" class="language-java line-numbers" scroll="set-scroll" %}}import 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&lt;String, Integer&gt; 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&lt;String, Long&gt; execellentRating = new HashMap&lt;&gt;(); Iterator&lt;String&gt; itrJson = dataJSON.keySet().iterator(); while(itrJson.hasNext()) { String museumName = itrJson.next(); List&lt;String&gt; 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&lt;String&gt; execellentMuseum = new ArrayList&lt;&gt;(); itrJson = execellentRating.keySet().iterator(); Integer i= 0; while(i++ &lt; 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&lt;String, Long&gt; sortByValue(HashMap&lt;String, Long&gt; hm) { // Create a list from elements of HashMap List&lt;Map.Entry&lt;String, Long&gt; &gt; list = new LinkedList&lt;Map.Entry&lt;String, Long&gt; &gt;(hm.entrySet()); // Sort the list Collections.sort(list, new Comparator&lt;Map.Entry&lt;String, Long&gt; &gt;() { public int compare(Map.Entry&lt;String, Long&gt; o1, Map.Entry&lt;String, Long&gt; o2) { return (o2.getValue()).compareTo(o1.getValue()); } }); // Put the data from the sorted list to the HashMap HashMap&lt;String, Long&gt; temp = new LinkedHashMap&lt;String, Long&gt;(); for (Map.Entry&lt;String, Long&gt; aa : list) { temp.put(aa.getKey(), aa.getValue()); } return temp; } } {{% /panel_with_adjustment %}} <br /> Copy the code below and paste it in {{%badge%}}TravellerTypeRatingProcessor.java{{%/badge%}} located in {{%badge%}}functions/TravellerTypeRatingProcessor{{%/badge%}} directory and save the file. {{% panel_with_adjustment header="TravellerTypeRatingProcessor.java" footer="button" class="language-java line-numbers" scroll="set-scroll" %}}import 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&lt;String, Integer&gt; 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&lt;String, Long&gt; execellentRating = new HashMap&lt;&gt;(); Iterator&lt;String&gt; itrJson = dataJSON.keySet().iterator(); while(itrJson.hasNext()) { String museumName = itrJson.next(); List&lt;String&gt; 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&lt;String&gt; execellentMuseum = new ArrayList&lt;&gt;(); itrJson = execellentRating.keySet().iterator(); Integer i= 0; while(i++ &lt; 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&lt;String, Long&gt; sortByValue(HashMap&lt;String, Long&gt; hm) { //Create a list from elements of HashMap List&lt;Map.Entry&lt;String, Long&gt; &gt; list = new LinkedList&lt;Map.Entry&lt;String, Long&gt; &gt;(hm.entrySet()); //Sort the list Collections.sort(list, new Comparator&lt;Map.Entry&lt;String, Long&gt; &gt;() { public int compare(Map.Entry&lt;String, Long&gt; o1, Map.Entry&lt;String, Long&gt; o2) { return (o2.getValue()).compareTo(o1.getValue()); } }); //Put the data from the sorted list to the HashMap HashMap&lt;String, Long&gt; temp = new LinkedHashMap&lt;String, Long&gt;(); for (Map.Entry&lt;String, Long&gt; aa : list) { temp.put(aa.getKey(), aa.getValue()); } return temp; } } {{% /panel_with_adjustment %}} <br /> Copy the code below and paste it in {{%badge%}}MergeDataSet.java{{%/badge%}} located in {{%badge%}}functions/MergeDataSet{{%/badge%}} directory and save the file. {{% panel_with_adjustment header="MergeDataSet.java" footer="button" class="language-java line-numbers" scroll="set-scroll" %}}import 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&lt;String&gt; travellerRatingList = mapper.readValue(ratingList.toString(), mapper.getTypeFactory().constructCollectionType(List.class, String.class));; List&lt;String&gt; travellerTypeRatingList = mapper.readValue(typeRatingList.toString(), mapper.getTypeFactory().constructCollectionType(List.class, String.class));; List&lt;String&gt; finalList = new ArrayList&lt;&gt;(); for(int i=0 ; i&lt; 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); } } } {{% /panel_with_adjustment %}} <br /> Copy the code below and paste it in {{%badge%}}Mailer.java{{%/badge%}} located in {{%badge%}}functions/Mailer{{%/badge%}} directory and save the file. {{% panel_with_adjustment header="Mailer.java" footer="button" class="language-java line-numbers" scroll="set-scroll" %}}import 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; } } {{% /panel_with_adjustment %}} {{%note%}}{{%bold%}}Note:{{%/bold%}} Provide the sender's email address you configured earlier, in line 19.{{%/note%}} <br /> #### Install Express Before we code the Node.js function, we must install the {{%link href="https://expressjs.com/"%}}Express Node.js{{%/link%}} dependencies in the directory. To install Express.js in your local machine, navigate to the function's directory ({{%badge%}}functions/circuit{{%/badge%}}) in your terminal and execute the following command: {{%cli%}}npm install express --save{{%/cli%}} This will install the Express module and save the dependencies. This information will also be updated in the {{%badge%}}package.json{{%/badge%}} file. <br /> Let's code the {{%badge%}}index.js{{%/badge%}} file now. Copy the code below and paste it in {{%badge%}}index.js{{%/badge%}} located in {{%badge%}}functions/circuit{{%/badge%}} directory and save the file. {{% panel_with_adjustment header="index.js" footer="button" class="language-javascript line-numbers" scroll="set-scroll" %}}'use strict'; var express = require('express'); var app = express(); var catalyst = require('zcatalyst-sdk-node'); app.use(express.json()); app.post('/triggerCircuit', (req, res) =&gt; { 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) =&gt; { console.log(functionResponse); res.status(200).send({ message: "Please check your email for the list of museums." }); }).catch(err =&gt; { 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; {{% /panel_with_adjustment %}} {{%note%}}{{%bold%}}Note:{{%/bold%}} After we create the circuit in the Catalyst console, ensure that you provide the circuit ID in this function in line 14.{{%/note%}} Let's quickly go over the functions that we configured. <br /> {{%badge%}}**Circuit**{{%/badge%}}: * The Advanced I/O {{%badge%}}circuit{{%/badge%}} function contains an API {{%badge%}}/triggerCircuit{{%/badge%}} that triggers the execution of the circuit which is configured in the Catalyst console. {{%note%}}{{%bold%}}Note:{{%/bold%}} We will configure the circuit after deploying the project resources to the remote console.{{%/note%}} * The {{%badge%}}circuit{{%/badge%}} 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 {{%badge%}}main.js{{%/badge%}} in the client directory. * After the circuit has completed its execution, it will pass a message to be displayed in the client application prompting the user to check their email address for the list of museums that satisfy their criteria. This message is configured in this function. <br /> {{%badge%}}{{%bold%}}RatingProcessor{{%/bold%}}{{%/badge%}} and {{%badge%}}{{%bold%}}TravellerTypeRatingProcessor{{%/bold%}}{{%/badge%}}: * The {{%badge%}}RatingProcessor{{%/badge%}} function stores the following ratings of the museums in a {{%link href="https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html" %}}Java HashMap{{%/link%}}: {{%badge%}}Excellent{{%/badge%}}, {{%badge%}}Very Good{{%/badge%}}, {{%badge%}}Average{{%/badge%}}, {{%badge%}}Poor{{%/badge%}}, {{%badge%}}Terrible{{%/badge%}}. * The {{%badge%}}TravellerTypeRatingProcessor{{%/badge%}} function stores the following traveller type suitability of the museums in a Java HashMap: {{%badge%}}Families{{%/badge%}}, {{%badge%}}Couples{{%/badge%}}, {{%badge%}}Solo{{%/badge%}}, {{%badge%}}Business{{%/badge%}}, {{%badge%}}Friends{{%/badge%}}. * In addition to the rating and traveller type, the user provides an input for the number of museums that they want to be listed in the results. This value is stored in the {{%badge%}}count{{%/badge%}} variable and is processed by both the functions. * The functions make calls to {{%badge%}}traveller-rating.json{{%/badge%}} or {{%badge%}}traveller-type-rating.json{{%/badge%}} datasets respectively, that are hosted by Catalyst. These datasets contain records of ratings and traveller type suitability provided by users on 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 {{%badge%}}Excellent{{%/badge%}} as the rating type, museums with that rating are identified in the {{%badge%}}traveller-rating.json{{%/badge%}} dataset. The museums with the highest number of {{%badge%}}Excellent{{%/badge%}} 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 the {{%badge%}}traveller-type-rating.json dataset.{{%/badge%}}. * The records that are fetched by both the functions are parsed as JSON objects and are written to {{%badge%}}ObjectMapper{{%/badge%}} classes. They are then sorted in a list and added to new HashMaps in both the functions. <br /> {{%badge%}}{{%bold%}}MergeDataSet{{%/bold%}}{{%/badge%}}: * The {{%badge%}}MergeDataSet{{%/badge%}} function obtains the records fetched by {{%badge%}}RatingProcessor{{%/badge%}} and {{%badge%}}TravellerTypeRatingProcessor{{%/badge%}} 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. <br /> {{%badge%}}{{%bold%}}Mailer{{%/bold%}}{{%/badge%}}: * The {{%badge%}}Mailer{{%/badge%}} function obtains the final list from the {{%badge%}}MergeDataSet{{%/badge%}} 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 {{%badge%}}Mailer{{%/badge%}} function then sends an email to the user's email address with the fetched content. The functions directory is now configured. -------------------------------------------------------------------------------- title: "Configure the client" description: "Build a Museum Finder web application using Catalyst Functions that helps you find museums based on ratings and traveller type criteria, and automate its workflow using Catalyst Circuits." last_updated: "2026-03-18T07:41:08.694Z" source: "https://docs.catalyst.zoho.com/en/tutorials/museum-finder/configure-client/" service: "All Services" related: - Client Directory Structure (/en/cli/v1/project-directory-structure/client-directory) -------------------------------------------------------------------------------- # Configure the Client Let's now configure the client component. The {{%link href="/en/cli/v1/project-directory-structure/client-directory" %}}client directory{{%/link%}} contains: * The {{%badge%}}index.html{{%/badge%}} file that contains the HTML code for the front-end application * The {{%badge%}}main.css{{%/badge%}} file that contains the CSS code for the front-end application * The {{%badge%}}main.js{{%/badge%}} file that contains the JavaScript code * The {{%badge%}}client-package.json{{%/badge%}} configuration file We will be coding {{%badge%}}index.html{{%/badge%}}, {{%badge%}}main.css{{%/badge%}}, and {{%badge%}}main.js{{%/badge%}}. {{%note%}}{{%bold%}}Note:{{%/bold%}} Please go through the code in this section to make sure you fully understand it. {{%/note%}} Copy the code below and paste it in the respective files located in the {{%badge%}}{{%bold%}}client/{{%/bold%}}{{%/badge%}} directory using an IDE and save the files. {{% panel_with_adjustment header="index.html" footer="button" class="language-xml line-numbers" scroll="set-scroll" %}}&lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;meta charset="utf-8" /&gt; &lt;meta http-equiv="X-UA-Compatible" content="IE=edge"&gt; &lt;title&gt;Museum Finder&lt;/title&gt; &lt;meta name="viewport" content="width=device-width, initial-scale=1"&gt; &lt;link rel="stylesheet" type="text/css" media="screen" href="main.css" /&gt; &lt;script src="main.js"&gt;&lt;/script&gt; &lt;!-- Font Awesome --&gt; &lt;link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css"&gt; &lt;!-- Google Fonts --&gt; &lt;link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"&gt; &lt;!-- Bootstrap core CSS --&gt; &lt;link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.min.css" rel="stylesheet"&gt; &lt;!-- Material Design Bootstrap --&gt; &lt;link href="https://cdnjs.cloudflare.com/ajax/libs/mdbootstrap/4.19.1/css/mdb.min.css" rel="stylesheet"&gt; &lt;script src="https://js.zohostatic.com/catalystclient/1.0.0/catalystWebSDK.js"&gt;&lt;/script&gt; &lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"&gt;&lt;/script&gt; &lt;!-- JQuery --&gt; &lt;!-- Bootstrap tooltips --&gt; &lt;script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.4/umd/popper.min.js"&gt;&lt;/script&gt; &lt;!-- Bootstrap core JavaScript --&gt; &lt;script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/js/bootstrap.min.js"&gt;&lt;/script&gt; &lt;!-- MDB core JavaScript --&gt; &lt;script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mdbootstrap/4.19.1/js/mdb.min.js"&gt;&lt;/script&gt; &lt;/head&gt; &lt;body&gt; &lt;div id="maindiv"&gt; &lt;nav class="navbar navbar-expand-lg navbar-dark brown"&gt; &lt;a class="navbar-brand" href=""&gt;Best Museum Finder ®&lt;/a&gt; &lt;/nav&gt; &lt;br&gt;&lt;br&gt;&lt;br&gt; &lt;center&gt; &lt;h2&gt;Get the list of best museums in the US based on your Choice and Rating !&lt;/h2&gt; &lt;br&gt;&lt;br&gt;&lt;br&gt; &lt;form id="myForm"&gt; &lt;label for="Type"&gt;Traveller Type: &lt;/label&gt; &lt;select name="Type" id="Type"&gt; &lt;option disabled selected value&gt; -- select an option -- &lt;/option&gt; &lt;option value="Families"&gt;Families&lt;/option&gt; &lt;option value="Couples"&gt;Couples&lt;/option&gt; &lt;option value="Solo"&gt;Solo&lt;/option&gt; &lt;option value="Business"&gt;Business&lt;/option&gt; &lt;option value="Friends"&gt;Friends&lt;/option&gt; &lt;option&gt;----------------------&lt;/option&gt; &lt;/select&gt;                  &lt;label for="Rating"&gt;Select the rating : &lt;/label&gt; &lt;select name="Rating" id="Rating"&gt; &lt;option disabled selected value&gt; -- select an option -- &lt;/option&gt; &lt;option value="Excellent"&gt;Excellent&lt;/option&gt; &lt;option value="Very good"&gt;Very good&lt;/option&gt; &lt;option value="Average"&gt;Average&lt;/option&gt; &lt;option value="Poor"&gt;Poor&lt;/option&gt; &lt;option value="Terrible"&gt;Terrible&lt;/option&gt; &lt;option&gt;----------------------&lt;/option&gt; &lt;/select&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt; Enter the number of Museums you want : &lt;input type="text" id="count" required&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt; Enter your Mail ID : &lt;input type="email" id="mailid" required&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt; &lt;button type="button" id="button" class="btn btn-brown" data-toggle="modal" data-target="#basicExampleModal" onclick="triggerCircuit()"&gt;Submit details&lt;/button&gt;&lt;/form&gt; &lt;/center&gt; &lt;/div&gt; &lt;div class="modal fade" id="basicExampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true"&gt; &lt;div class="modal-dialog" role="document"&gt; &lt;div class="modal-content"&gt; &lt;div class="modal-body"&gt; &lt;center&gt;&lt;br&gt;&lt;p id="popuptext"&gt;&lt;/p&gt;&lt;br&gt; &lt;button type="button" class="btn btn-brown" data-dismiss="modal"&gt;Close&lt;/button&gt; &lt;/center&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/body&gt; &lt;/html&gt; {{% /panel_with_adjustment %}} {{% panel_with_adjustment header="main.css" footer="button" class="language-css line-numbers" scroll="set-scroll" %}}.modal { padding-top: 350px; } {{% /panel_with_adjustment %}} {{% panel_with_adjustment header="main.js" footer="button" class="language-javascript line-numbers" scroll="set-scroll" %}}function triggerCircuit() { var count = document.getElementById("count").value; var Rating = document.getElementById("Rating").value; var Type = document.getElementById("Type").value; var MailID = document.getElementById("mailid").value; document.getElementById("myForm").reset(); if (count || Rating || Type || MailID) { console.log(count, Rating, Type, MailID); $.ajax({ url: "/server/circuit/triggerCircuit", type: "post", contentType: "application/json", data: JSON.stringify({ "count": count, "rating": Rating, "traveller": Type, "mail_id": MailID }), success: function (data) { console.log(data); //You can view these logs from Logs in the Catalyst console document.getElementById("popuptext").innerHTML = data.message; }, error: function (error) { console.log(error); document.getElementById("popuptext").innerHTML = "Please try again after sometime"; } }); } else { document.getElementById("popuptext").innerHTML = "Please provide data in the all the fields"; } } {{% /panel_with_adjustment %}} <br /> As discussed in the previous section, the {{%badge%}}main.js{{%/badge%}} function obtains the input values provided by the user in the client application and passes it to the circuit function by triggering its API. The HTTP {{%badge%}}POST{{%/badge%}} method is used to post the data to the circuit function. The data passed includes the values for the number of museums required by the user, the preferred rating and traveller type, and the user's email address. The client directory is now configured. We will now deploy the function and client components to the remote console. -------------------------------------------------------------------------------- title: "Deploy the project" description: "Build a Museum Finder web application using Catalyst Functions that helps you find museums based on ratings and traveller type criteria, and automate its workflow using Catalyst Circuits." last_updated: "2026-03-18T07:41:08.694Z" source: "https://docs.catalyst.zoho.com/en/tutorials/museum-finder/deploy-project/" service: "All Services" related: - Deploy CLI Resources (/en/cli/v1/deploy-resources/introduction/) - Web Client Hosting (/en/cloud-scale/help/web-client-hosting/introduction) -------------------------------------------------------------------------------- # Deploy The Project To {{%link href="/en/cli/v1/deploy-resources/introduction" %}}deploy your Catalyst project{{%/link%}} from the CLI, run the following command in your terminal from your project directory: {{%cli%}}catalyst deploy{{%/cli%}} The functions are deployed first, followed by the client component. The production URLs of the components are displayed. <br /> You can now open the Catalyst console and access the _MuseumFinder_ project. Click {{%bold%}}Functions{{%/bold%}} from **Serverless** to check if all five functions were deployed properly. <br /> {{%note%}}{{%bold%}}Note:{{%/bold%}} The console editor is currently not available for Java functions in the Catalyst console. Therefore, you will not be able to view the code of the Java functions.{{%/note%}} Similarly, click {{%bold%}}Web Client Hosting{{%/bold%}} from **Cloud Scale** to check if the web client has been deployed and {{%link href="/en/cloud-scale/help/web-client-hosting/introduction" %}}hosted{{%/link%}}. <br /> Before we access the web client, let's create and configure the circuit. -------------------------------------------------------------------------------- title: "Create and configure a Circuit" description: "Build a Museum Finder web application using Catalyst Functions that helps you find museums based on ratings and traveller type criteria, and automate its workflow using Catalyst Circuits." last_updated: "2026-03-18T07:41:08.695Z" source: "https://docs.catalyst.zoho.com/en/tutorials/museum-finder/create-and-configure-circuit/" service: "All Services" related: - Circuits (/en/serverless/help/circuits/introduction/) -------------------------------------------------------------------------------- # Create and Configure the Circuit The circuit automates the executions of all the functions based on the workflow we design. Therefore, after the user provides the input in the client application, the functions we configured will execute in a specific order and the email will be sent to the user with the results they require. To create a circuit from the console: 1. Navigate to {{%bold%}}Circuits{{%/bold%}} from {{%bold%}}Serverless{{%/bold%}} and click {{%bold%}}Create Circuit{{%/bold%}}.<br /> <br /> 2. Enter a name for the circuit and click {{%bold%}}Create{{%/bold%}}. You can enter any name.<br /> <br /> The circuit will be created and the _Builder View_ will open. You can learn about Circuits in detail from the {{%link href="/en/serverless/help/circuits/introduction" %}}Circuits help page{{%/link%}}. <br /> Before we configure the circuit, perform the following action: 1. Click on the {{%bold%}}arrow{{%/bold%}} next to the circuit name on the top-left corner to navigate to the _Circuits_ page. Copy the {{%bold%}}Circuit ID{{%/bold%}} displayed in the page. <br /> 2. Now, navigate to {{%bold%}}Functions{{%/bold%}} and click the {{%badge%}}{{%bold%}}circuit{{%/bold%}}{{%/badge%}} function. <br /> 3. Click the {{%bold%}}Code{{%/bold%}} tab and open {{%badge%}}{{%bold%}}index.js{{%/bold%}}{{%/badge%}} by clicking it. Paste the Circuit ID that you copied on line 14 and click {{%bold%}}Save{{%/bold%}}. <br /> <br /> #### Configure the Circuit Now let's go back to configuring the circuit. 1. Navigate to {{%bold%}}Circuits{{%/bold%}} and open the circuit that you created. We will first create a {{%link href="/en/serverless/help/circuits/key-concepts/#parallel" %}}parallel state{{%/link%}} to execute the {{%badge%}}RatingProcessor{{%/badge%}} and {{%badge%}}TravellerTypeRatingProcessor{{%/badge%}} functions in parallel. {{%note%}}{{%bold%}}Note:{{%/bold%}} Ensure that you enter all the values for the circuit's states and configurations as described in this section.{{%/note%}} <br /> 2. Drag the {{%bold%}}Parallel{{%/bold%}} state from the left panel onto the builder view and drop it anywhere. This will open a pop-up box. <br /> 3. Enter the parallel state's name as "{{%bold%}}Dataset Processing{{%/bold%}}". Set its previous state to "{{%bold%}}Start{{%/bold%}}" and next state to the default "{{%bold%}}State1{{%/bold%}}". Click {{%bold%}}Create{{%/bold%}}. <br /> 4. Now in the Dataset Processing state's configuration, click {{%bold%}}Add Path{{%/bold%}} and enter path's name as "{{%bold%}}rating\_list{{%/bold%}}". This will automatically assign the path to a new state called _New State 1_. Similarly, add another path and enter its name as "{{%bold%}}traveller\_type\_rating\_list{{%/bold%}}". This will assign the path to _New State 2_. <br /> {{%note%}}{{%bold%}}Note:{{%/bold%}} We will configure the Input/Output of all the states in the end.{{%/note%}} <br /> 5. Now click on {{%bold%}}New State 1{{%/bold%}} in the builder. Enter the state's name as "{{%bold%}}Rating Processor{{%/bold%}}". Assign its type as a {{%link href="/en/serverless/help/circuits/key-concepts/#function" %}}Function state{{%/link%}} and associate it with the {{%badge%}}RatingProcessor{{%/badge%}} function from the drop down-lists. <br /> 6. Similarly, click on {{%bold%}}New State 2{{%/bold%}} in the builder. Enter the state's name as "{{%bold%}}Traveller Type Rating Processor{{%/bold%}}". Assign its type as {{%bold%}}Function{{%/bold%}} and associate it with the {{%badge%}}TravellerTypeRatingProcessor{{%/badge%}} function. Both these functions will now execute in parallel every time the circuit is triggered. <br /> 7. Now click on {{%bold%}}State1{{%/bold%}} in the builder. Enter the state's name as "{{%bold%}}Merge Dataset{{%/bold%}}" and associate it with the {{%badge%}}MergeDataSet{{%/badge%}} function. <br /> 8. Let's add another function state. Drag and drop a function state from the left panel, and provide the following values. Click {{%bold%}}Create{{%/bold%}}. Associate this state with the {{%badge%}}Mailer{{%/badge%}} function in the configuration. <br /> 9. Let's now add a {{%link href="/en/serverless/help/circuits/key-concepts/#branch" %}}branch state{{%/link%}} before the Mailer state to branch the circuit's processing. Drag and drop a branch state and provide the following values. Click {{%bold%}}Create{{%/bold%}}. {{%note%}}{{%bold%}}Note:{{%/bold%}} We are adding the branch state after the Mailer state has been created to avoid creating a default branch.{{%/note%}} <br /> 10. In the Check Result state's configuration, click {{%bold%}}Add Condition{{%/bold%}} and enter the condition as "{{%bold%}}$.result.museum\_list.size() == 0{{%/bold%}}". This will automatically assign it to a New State 1. <br /> 11. Now click on {{%bold%}}New State 1{{%/bold%}} from the builder and associate it with a {{%link href="/en/serverless/help/circuits/key-concepts/#failure" %}}Failure state{{%/link%}}. Name the state as "{{%bold%}}Failure{{%/bold%}}". Now if there are no results matching the criteria of the user's input, the circuit will traverse this branch and end in the failure state. Since a failure state is an end state, there will be no next state associated with it. If there are results matching the user's criteria, the circuit will traverse the other branch and execute the {{%badge%}}Mailer{{%/badge%}} function that emails the results to the user. <br /> Your final circuit should look like this. <br /> Let's now configure the {{%link href="/en/serverless/help/circuits/key-concepts/#input-output-and-result" %}}input paths, result paths, and parameters{{%/link%}} of some states. * {{%bold%}}Dataset Processing:{{%/bold%}} Click on the {{%bold%}}Dataset Processing{{%/bold%}} state from the builder and click the {{%bold%}}Input/Output{{%/bold%}} tab for it. Enter the value for its result path as "{{%bold%}}$.dataset\_results{{%/bold%}}". This will assign the results obtained from processing the {{%badge%}}RatingProcessor{{%/badge%}} and {{%badge%}}TravellerTypeRatingProcessor{{%/badge%}} functions to the variable dataset\_results.<br /> <br /> * {{%bold%}}Rating Processor:{{%/bold%}} Enter the following values for the Input/Output of the {{%bold%}}Rating Processor{{%/bold%}} state. This selects the results obtained by the {{%badge%}}RatingProcessor{{%/badge%}} function's execution and assigns it to the variable {{%badge%}}result{{%/badge%}}. That value is then added to the {{%link href="/en/serverless/help/circuits/key-concepts/#input-output-and-result" %}}input JSON of the circuit{{%/link%}}. Add two parameters named "{{%bold%}}type{{%/bold%}}" and "{{%bold%}}count{{%/bold%}}" to pass the {{%badge%}}rating{{%/badge%}} and {{%badge%}}count{{%/badge%}} input values from the user to the function respectively.<br /> <br /> * {{%bold%}}Traveller Type Rating Processor:{{%/bold%}} Enter the following values for the Input/Output of the {{%bold%}}Traveller Type Rating Processor{{%/bold%}} state. These are the same as the previous one, except that the parameter "type" is assigned to the {{%badge%}}traveller{{%/badge%}} input value.<br /> <br /> * {{%bold%}}Merge Dataset:{{%/bold%}} Enter the following values for the Input/Output of the {{%bold%}}Merge Dataset{{%/bold%}} state. The result previously assigned as {{%badge%}}dataset\_results{{%/badge%}} by the parallel processing, is passed as the input to this function, and the function's output is assigned to the {{%badge%}}result{{%/badge%}} variable again. We will not be passing any parameters here.<br /> <br /> * {{%bold%}}Mailer:{{%/bold%}} Enter the following values for the Input/Output of the {{%bold%}}Mailer{{%/bold%}} state. The final list ({{%badge%}}result.museum\_list{{%/badge%}}) obtained from the {{%badge%}}MergeDataSet{{%/badge%}} function is passed as the {{%badge%}}content{{%/badge%}} parameter and the user's email address is passed as {{%badge%}}mail\_id{{%/badge%}}. The museum list is also passed as the circuit's output by adding it as the result path in this state.<br /> <br /> The circuit is now completely configured. Click {{%bold%}}Save{{%/bold%}} to save it. {{%note%}}{{%bold%}}Note:{{%/bold%}} You can also view the JSON code of the circuit to understand it better by clicking the {{%bold%}}Code View{{%/bold%}} tab in the builder page.{{%/note%}} -------------------------------------------------------------------------------- title: "Test the application" description: "Build a Museum Finder web application using Catalyst Functions that helps you find museums based on ratings and traveller type criteria, and automate its workflow using Catalyst Circuits." last_updated: "2026-03-18T07:41:08.696Z" source: "https://docs.catalyst.zoho.com/en/tutorials/museum-finder/test-application/" service: "All Services" related: - Web Client Hosting (/en/cloud-scale/help/web-client-hosting/introduction/) - Circuits (/en/serverless/help/circuits/introduction/) -------------------------------------------------------------------------------- # Test the Application Let's now test the client application, and verify if the circuit is triggered automatically and the entire workflow executes as intended. Navigate to {{%bold%}}Web Client Hosting{{%/bold%}} from {{%bold%}}Cloud Scale{{%/bold%}}. Click the {{%link href="/en/cloud-scale/help/web-client-hosting/introduction" %}}web app URL{{%/link%}}. <br /> This will open the front-end of the Museum Finder application. <br /> Select values for {{%bold%}}traveller type{{%/bold%}} and {{%bold%}}rating{{%/bold%}} from the drop-down lists. Enter the value for museum count and an email address to receive the email. Click {{%bold%}}Submit Details{{%/bold%}}. <br /> The application will display a pop-up box to let you know the results were emailed, or if no results were found. <br /> You can check your email to view the list of museums matching the criteria you selected. <br /> You can check the circuit's execution by navigating to the _Circuits_ page in the Catalyst console and clicking {{%bold%}}Execution History{{%/bold%}}. <br /> Open the latest execution from the _Execution History_ page. <br /> You can view the circuit's output under _Execution Details_, along with other details in the {{%bold%}}View Graph{{%/bold%}} section. The graph will display the path that was traversed during the circuit's processing. <br /> You can access a {{%link href="/en/serverless/help/circuits/implementation/#execution-history-of-a-circuit" %}}detailed execution log{{%/link%}} by clicking {{%bold%}}View Logs{{%/bold%}}. This displays every event that occurred in each state, the payload and parameters passed, exceptions generated, and more. <br /> The Museum Finder application is now configured and will work without any errors.