Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Table of Contents

Problem Statement

English
In this article, we will

...

walkthrough the thought process

...

of designing a solution for the following business use case:-
  1. Visitor The visitor would request for a demo by submitting a form in the Joget app.
  2. Upon submission of the form, fields will be validated to make sure that all mandatory fields are filled up.
  3. Upon successful validation of data, the form data will be shared with an external system (i.e. CRM software) for further processing through the use of plugins (i.e. JSON Tool) or Bean Shell code. More on this later on.

  4. The main objective is to ensure the successful delivery of data with the external system.

This is an example on how of what the form would look like.

Image Modified

Figure 1

The only external factor that may be outside of the Joget platform's control would be the external integration with the CRM software. We will walkthrough a few scenarios on how best to design for this business use case with  UI/UX kept in mind.

...

Preparing the Form and Userview

  1. We will start with desiging designing the form itself. The form itself is quite simple, with just 3 fields and all of them made mandatroy.
    Image Modified
    Figure 2

  2. In the userview, we are making use of the Form Menu and link it to the form we have just designed.
    Image Modified
    Figure 3

    Configure "Action to Perform After Form Saved" to redirect to a "HTML Page" to show form submitted message (e.g. Form submitted. Thank you!).

  3. Do not forget to create a CRUD menu too so that we can browse through all the submissions easily using Generate CRUD.
    Image Modified
    Figure 4

  4. At this point of in time, there's no integration yet with the external CRM.

What Happens on Form Submission?

When end-user hits on the Submit button, the following will take place.

  1. Form Validation - Joget would iterate through each and every form element and invoking the validator (if configured).
    Image Modified
    Figure 5

    If all validations pass, then it will move to the next step, else, end user will be redirected back to the same form with validation errors displayed like what is shown in the screenshot below.
    Image Modified
    Figure 6

  2. Form Store - Since validations have passed, Joget will now proceed to the next step, form data will be passed to the store binder.
    Image Modified
    Figure 7

    By default, the load/store binder is Workflow Form Binder where it will load and store form data into the table name declared in the form properties. In this case, the table name is "demo_request".
  3. Since we are using Workflow Form Binder, this would also mean that we are saving the form data locally in Joget's database.

With what we have learned so far, this can be presented using the following diagram.

Image Modified
Figure 8

Invoke Restful API Call

...

The easiest, no-code approach is to make use of JSON Tool plugin itself. The JSON Tool itself is a Process Tool & Post Form Submission Processing Plugin. This means that we can invoke it from within a process flow or from the submission of the form.

Method 2 - Bean Shell Code

  1. We can also write Bean Shell code. Here's a quick sample code to make http HTTP get call.

    Code Block
    languagejava
    titleBean Shell code to make restful API calls sample
    linenumberstrue
    import org.apache.http.HttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.client.methods.HttpRequestBase;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import java.io.IOException;
    import org.joget.commons.util.LogUtil;
      
    	CloseableHttpClient client = null; 
    	HttpRequestBase request = null; 
    
     try{
    	String jsonUrl = "http://localhost:8080/jw/web/json/workflow/assignment/list/count?packageId=crm"; //sample url
    	String name = "header1";
    	String value = "value1";
    
    	CloseableHttpClient client = null;
    	CloseableHttpClient client = HttpClientsHttpClients.createDefault();
    
    	HttpRequestBase request = null;
    	request = new HttpGet(jsonUrl);
    	request.setHeader(name, value);
    
    	HttpResponse response = client.execute(request);
    
    } catch (Exception ex) {
        LogUtil.error(getClass().getName(), ex, "");
    } finally {
        try {
            if (request != null) {
                request.releaseConnection();
            }
            if (client != null) {
                client.close();
            }
        } catch (IOException ex) {
            LogUtil.error(getClass().getName(), ex, "");
        }
    }

    We can execute this piece of code from various plugin types giving us the flexibility on where/when we want to invoke it. The only disadvantage compared to the former is that we need to maintain the custom coding ourselves instead of configuring through a plugin. These are the plugin types relevant to our solution to call the code from:-

    1. Bean Shell for Process Tool
    2. Bean Shell Validator
    3. Bean Shell Form Binder 


...

Code Block
languagejava
titleCall JSON Tool plugin from Bean Shell code
linenumberstrue
import java.util.Map;
import java.util.HashMap;
import javax.servlet.http.HttpServletRequest;
import org.joget.apps.app.model.AppDefinition;
import org.joget.apps.app.service.AppPluginUtil;
import org.joget.apps.app.service.AppUtil;
import org.joget.plugin.base.ApplicationPlugin;
import org.joget.plugin.base.Plugin;
import org.joget.plugin.base.PluginManager;
import org.joget.plugin.property.model.PropertyEditable;
import org.joget.workflow.model.WorkflowAssignment;
  
public Object execute(AppDefinition appDef, HttpServletRequest request, WorkflowAssignment workflowAssignment) {
    String jsonUrl = "http://localhost:8080/jw/web/json/workflow/assignment/list/count?packageId=crm"; //sample url
     
    //Reuse Email Tool to send separated email to a list of users;
    Plugin plugin = pluginManager.getPlugin("org.joget.apps.app.lib.JsonTool");
    ApplicationPlugin jsonTool = (ApplicationPlugin) plugin;

    Map propertiesMap = new HashMap();
    propertiesMap.put("pluginManager", pluginManager);
    propertiesMap.put("appDef", appDef);
    propertiesMap.put("request", request);
    propertiesMap.put("workflowAssignment", workflowAssignment);
    
    //configure json tool plugin
    propertiesMap.put("jsonUrl", jsonUrl);
    propertiesMap.put("requestType", ""); //empty is for GET call
    propertiesMap.put("multirowBaseObject", "");
    propertiesMap.put("debugMode", "");
    propertiesMap.put("formDefId", "request");
    propertiesMap.put("headers", new Object[]{});

    List fieldMappings = new ArrayList();

    Map fieldMapping = new HashMap();
    fieldMapping.put("jsonObjectName", "total");
    fieldMapping.put("field", "day");
    fieldMappings.add(fieldMapping);

    //repeat this code to add more row
    // fieldMapping = new HashMap();
    // fieldMapping.put("jsonObjectName", "jsonAttrName");
    // fieldMapping.put("field", "formFieldId");
    // fieldMappings.add(fieldMapping);

    propertiesMap.put("fieldMapping", fieldMappings.toArray());
         
    //set properties and execute the tool
    ((PropertyEditable) jsonTool).setProperties(propertiesMap);
    jsonTool.execute(propertiesMap);
     
    return null;
}
  
//call execute method with injected variable
return execute(appDef, request, workflowAssignment);

...

Method 1 - Post Form Submission Processing and JSON Tool

By using Post Form Submission Processing in Form, and "Method 1 JSON Call" earlier, this is the easiest and quickest method. This allows us to invoke any Process Tool & Post Form Submission Processing PluginJSON Tool is one such candidate.

Image Modified

Figure 9

  • Upon form submission, form fields will be validated, with its form data stored, then, the "Post Form Submission Processing" will be triggered.
  • Response time of form submission will now include complete execution of the JSON Tool.
  • Imagine that the external JSON API takes longer than expected to respond, the end user will be kept waiting.
  • Depending on the feature of the API call, we would assume that it would return a response to indicate successful execution. For example:-

    Code Block
    languagejs
    titleSample JSON Call Response
    { "success" : "true" }
  • By using this integration point, there's no way to redirect the user to other place/menu when error occurs.

...

To avoid the waiting time for JSON Tool to finish executing, we can place it under Multi Tools instead.

Image Modified

Figure 10

Set the "Run Mode" such that it would execute the process tool (JSON Tool) in a new thread.

...

With what we have learned so far, there's still 1 con that we are trying to solve. Let's try to put the form within a process flow in a diagram as below.

Image Modified

Figure 11

By using a process flow, we can check the content of the returned JSON call to see if it matches the intended content. For example, we are expecting this reply and have it mapped to a workflow variable.

...

  1. Same form content may get submitted repetitively as other form fields' validators may kick in too (i.e. mandatory field checking). This results in many JSON calls which in most cases, not the behavior desired.
  2. If the external API system becomes unresponsive, the Joget app will stop working.
  3. Increased wait time. Response time includes JSON Tool's turnaround time.

...

By calling the JSON API within the Form Store Binder Plugin, we will need to explore on how to handle events such as when JSON API is not being responsive. In this type of plugin, it won't be expecting a true/false to be returned like the validator plugin though.

We can try to throw an exception instead in the Bean Shell code that we are writing.

Image Modified

Figure 12

This approach suffers from the following issues:-

  1. If the external API system becomes unresponsive, the Joget app will stop working.
  2. Error message is not friendly.
  3. Increased wait time. Response time includes JSON Tool's turnaround time.
  4.  If the intent is not to let user proceed until the JSON call is successful, then this is a viable choice to consider.
  5. Error message is not friendly. This is because the code written is wrapped within the Bean Shell plugin itself and the exception message from the code does not propagate to the form UI. To mitigate/improve on this, we will need to develop a custom store binder itself.
  6. Increased wait time. Response time includes JSON Tool's turnaround time.

Method 5 - Post Form Submission Processing and JSON Tool with Multi Method 5 - Post Form Submission Processing and JSON Tool with Multi Tools and Follow-Up on Failed API Calls

...

Taking cues from method 2 earlier, we will put the new plugin in Multi Tools and set the Run Mode to "Run tools sequentially in a new single thread". This is so that customer does not need to wait for JSON call to complete.

Image Removed

Figure 13

The following is a new section to configure to capture the JSON call's response status.

We created a new form to capture the JSON call log.

Image Removed

Figure 14

In this screenshot below, we are able to inspect each of the form submission made (left) and the result of the API call (right).

In the highlighted row, we can see that the API call failed with response status code of 524.

Image Removed

in a new single thread". This is so that customer does not need to wait for JSON call to complete.

Image Added

Figure 13

The following is a new section to configure to capture the JSON call's response status.

We created a new form to capture the JSON call log.

Image Added

Figure 14

In this screenshot below, we are able to inspect each of the form submission made (left) and the result of the API call (right).

In the highlighted row, we can see that the API call failed with response status code of 524.

Image Added

Figure 15

And in this screenshot below, there are 2 log records created. The JSON call is successful but the data response triggerred a casting exception.

Image Added

Figure 16

With the log data on hand, we can then create a scheduled task that picked up unsuccessful API calls and attempt to trigger them again later on by using the following SQL (MySQL).

Code Block
languagesql
SELECT req.*, log.c_status as `latest_status` FROM app_fd_demo_request req LEFT JOIN (SELECT MAX(dateCreated) as dateCreated, c_request_id FROM app_fd_demo_request_log log GROUP BY c_request_id) a ON req.id = a.c_request_id JOIN app_fd_demo_request_log log ON log.dateCreated = a.dateCreated WHERE log.c_status != '200' ORDER BY req.dateCreated DESC

This is what the list would look like in the screenshot below. Each of the request rows submitted will show the latest log.

Image Added

Figure 17

Since we are storing the exact form data in Joget's database, we can try to make the same JSON call again later on.

For example, we can make use of the Form Update Process Tool Datalist Action and map to the JSON Tool.

Image Added

Figure 18

Once it is tested working, we can consider automating it and set up a scheduler job - iterate through the same list and execute JSON Tool using Iterator Process Tool.Figure 15