Plugin Design
Note:
1. The process for publishing custom plugins is still in testing, so currently it is not possible to publish custom plugins to the market.
2. This feature requires coding skills. If you do not know how to code, you can install plugins from the market or send requests to us.
Introduction
In Jodoo open platform, you can design plugins with custom code logic to meet your individual business needs in different scenarios. Once a plugin is published to the market, other users can access the plugin without having to understand the technical details.
When you click New Plugin and enter the Plugin Design page, a function is automatically created for you within the plugin. As a plugin can contain one or more functions, you can add other functions according to your needs. Each function contains three main properties: code, request parameter declaration, and return parameter declaration.
The request parameter declaration defines what parameters the user needs to pass to the function. The return parameter declaration defines what parameters the function will output. Code defines the custom logic that the function executes, it can process the input parameters and return output parameters.
Take the translation plugin as an example, users can input a text that they want to translate, and this text is passed as an input parameter to the function. Then the code is executed. It sends the text to a third-party translation API, along with information like the target language. Once the translation API has generated the translated text, the function returns the text as an output parameter to the plugin, which can then display the text to the users. Users can fill in the returned results in other form fields.
Here is the Plugin Design page and the features available on the page.
Number | Feature | Description |
1 | Plugin Name | / |
2 | Common Parameters | You can add parameters shared by all functions, such as the secret key required by some platforms. |
3 | Front-End Extension/Back-End Function | Both consist of request parameters, return parameters, and code.
|
4 | Fields | When setting the parameter, you can drag fields from the panel and add them to the parameter. Different types of fields have different features when configured or used by users. |
5 | Request Parameters/Return Parameters | For the request parameters, users need to specify values for each parameter when using the plugin. These values can be filled in manually by the users or configured to reference data from other features. Jodoo will link the data for you, so you do not need to worry about the source of the parameters. For the return parameters, users can configure whether they are used or saved to the specified fields when using the plugin. |
6 | Configuration of The Parameter | Define IDs for variables so that you can reference and identify the variable easily within the code. Set the prompt and initial value for a parameter so that you can better understand the configurations. |
When setting Triggered Action in Front-End Event, the settings correspond to the configuration on the Plugin Design page as follows:
Adding Function(s)
After creating a plugin, you need to add functions. On the Plugin Design page, you can click New Function in the bottom left corner to add functions according to your needs. There are two types of functions available.
Type | Description | Location to Set |
Front-End Extension | If the action is set as a front-end extension, the plugin will be executed on the user-side system and can therefore provide additional functionality for the user. For example, you can set it to pop up an iframe window. In business scenarios, you can use this feature to pop up a map page or record audio. | You can only set the action in Front-End Event. You can only set it as the Triggered Action for Button fields. |
Back-End Function | If the action is set as a back-end function, the plugin will be executed on the server side. In business scenarios, you can use this feature to extract data from Excel files and apply it in Jodoo. | You can set the action in all locations where the plugin can be called. |
Note:
1. Both Front-End Extension and Back-End Function include Request Parameters, Return Parameters, and Code. The rules for request parameters and return parameters are the same. The supported methods for Code of Front-End Extension and Back-End Function are listed in "Coding" below.
2. A maximum of 30 functions can be added for one custom plugin.
Designing Parameter(s)
There are four types of parameters available when designing the plugin. You can add parameters according to your needs.
Type | Description | Field Limit |
Authentication | It refers to the information that users need to fill in when configuring plugins, in order to verify the identity of the plugin users and ensure the security of plugin calls. | / |
Common Parameters | You can use common parameters to configure settings that apply to the plugin as a whole. Common parameters should be configured before the plugin is enabled. For example, you need to set the API Key when calling Jodoo APIs. | 1. A maximum of 128 fields can be added for Common Parameters, Request Parameters, and Return Parameters. 2. For Request Parameters, a maximum of 4.096 characters can be entered in a prompt. |
Request Parameters | The request parameters are what the plugin user needs to provide when using the plugin in different locations (such as Front-End Events and Automations). For example: when calling department APIs, the user needs to provide department information. | |
Return Parameters | Return parameters refer to the content that is returned to the plugin user when the plugin is executed. Users can then select the desired content and fill it into the form. |
Authentication
1. Authentication refers to the information that users need to fill in when configuring plugins, in order to verify the identity of the plugin users and ensure the security of plugin calls. During the plugin design process, you can choose None for no authentication, or perform authentication based on different integration modes. Jodoo supports authentication in the following two ways:
- Baisc Auth
- OAuth2.0 - Client Credentials
2. When choosing Basic Auth as the authentication method, you need to enter the corresponding Username and Password for authentication during plugin configuration. Users can also click Add Parameter to set more authentication options, and can also set parameter properties in the right panel.
Taking the authorization parameter only as Username and Password as an example:
Note:
The maximum number of authorization parameters is 5.
3. When OAuth2.0 - Client Credentials is selected as the authentication method, it supports setting the following contents:
- Authorization Parameters: By default, ClientID and ClientSecret are used as authorization parameters for authentication. You can click Add Parameters to set more authentication options, and can also set parameter properties in the right panel.
- API Parameters: Supports setting Request Type, URL, and Header/Body;
- Return Parameters: Supports setting accessToken and Token Lifetime(s).
Taking the authorization parameter only as ClientID and ClientSecret as an example:
Note:
The maximum number of authorization parameters is 5.
Common Parameters
You can use common parameters to configure settings that apply to the plugin as a whole. Common parameters should be configured before the plugin is enabled.
For example, you can configure the API Key when integrating with a third-party platform.
Note:
1. You can add fields according to what needs to be configured in the parameters. When setting Common Parameters, you can add the following fields: Text, Number, Date&Time, and Single Select.
2. A maximum of 128 fields can be added for Common Parameters.
Request Parameters
The request parameters are what the plugin user needs to provide when using the plugin in different locations (such as Front-End Events and Automations). For example: when calling department APIs, the user needs to provide department information.
When designing request parameters, you can add different kinds of fields. This relates to the input method of the plugin user when configuring the plugin, as well as the type of value that you can get in the code.
For example, if you add a Select Department parameter in the request parameter declaration, users can only select the department type fields when configuring the plugin in Front-End Event. Besides, users can also reference fields in the form that can be converted to this type, such as Department and Departments.
Note:
1. For the field types supported to store different request parameters in Front-End Event, refer to "Related Terminology and Settings" below.
2. A maximum of 128 fields can be added for Request Parameters.
3. A maximum of 4.096 characters can be entered in a prompt for Request Parameters.
Return Parameters
Return parameters refer to the content that is returned to the plugin user when the plugin is executed. Plugin users can choose the desired content to fill in the form or transfer it to functions of other plugins. For example: after calling a department API, the plugin will return content like member information. Then you can define member information as return parameters.
Note:
A maximum of 128 fields can be added for Return Parameters.
There are two types of parameters that can be returned, any and object[].
The default parameter type is any. This means the function can return a value of any type.
Object[] is used to represent an array of objects. This is often used when returning a list of data, where the item in the list may have different values. For example, when querying the members of a department, there may be more than one member.
If the parameter type is set to object[], you can add a new row as a child item by clicking the plus (+) button in front of the parameter. Each child item corresponds to a specific element in the returned array. For example, in the returned member information, each piece of information contains the member's name, member number, and so on.
You can set return parameters when configuring the plugin in Front-End Event:
Error Message for Form Validation
On the Plugin Design page, the system can validate your entries based on the format rules when you configure common parameters, request parameters, and response parameters in the form view. This feature helps reduce input errors, ensuring a smoother and more reliable configuration of the form view.
Coding
After designing the parameters, you need to write code to run the function.
Your code will be wrapped in a function of the programming language. In the code, you need to use the global variable "triggerConf" to retrieve request parameter values. After writing the function code, you have to use the "return" keyword to return the output value.
Select a programming language (icon one), and write the function code in the code editor (icon two). You can reference the parameters defined in the request parameter declaration. The list of all parameters and their value types is shown in the right panel.
Example of Back-End Function Code
Python 3.6
# You can reference third-party libraries.
import json
import requests
# You can obtain the parameters you defined by reading the properties in the predefined global variables.
# agentConf is a common parameter, and it is a dictionary with key-value pairs. The key is the ID you defined for the control in the request parameter declaration, and the value is the configuration value specified by the user.
# triggerConf is a request parameter, and it is a dictionary with key-value pairs. The key is the ID you defined for the control in the request parameter declaration, and the value is the configuration value or the value of the form field specified by the user.
api_key = str(agentConf.get('apiKey'))
dept_no = triggerConf.get('deptNo')
# You need to validate the type and format of the input parameters to enhance the robustness of the function logic.
# The following code processes different value formats that may be obtained to ensure the accuracy of dept_no, see "Structure of triggerConf (request parameters)" for specific value formats.
try:
if isinstance(dept_no, str):
dept_no = json.loads(dept_no)
if isinstance(dept_no, list):
dept_no = dept_no[0] if 0 < len(dept_no) else None
if isinstance(dept_no, dict):
dept_no = dept_no.get('dept_no')
if dept_no is None:
dept_no = 1
dept_no = int(dept_no)
except:
raise ValueError('Department number format error')
# You can use third-party libraries and request parameters to call related APIs in the server environment and obtain results.
url = ' https://api.jodoo.com/api/v5/corp/department/user/list'
payload = {'dept_no': dept_no}
headers = {'Authorization': 'Bearer ' + api_key}
response = requests.post(url, json=payload, headers=headers)
if 300 <= response.status_code < 500:
# For specific results, throw an error and define the error message.
body = response.json()
code = body.get('code', -1)
message = body.get('message', 'Unknown error')
message = 'Parameter error(%s): %s' % (code, message)
raise ValueError(message)
# Process the result and define the return parameter value.
# You need to return a dictionary, and the key of the dictionary corresponds to the return parameter ID. If the return is an array, the corresponding output parameter needs to be set as "object[]" type.
body = response.json()
users = [{'name': user.get('name'), 'username': user.get(
'username')} for user in body.get('users', [])]
count = len(users)
return {
"users": users,
"count": count
}
# The global variables that can be referenced, the third-party libraries supported, and the data storage format of request parameters are described in "Related Terminology and Settings"
Node.js 12
// You can reference third-party libraries.
const _ = require('lodash');
const axios = require('axios');
// You can obtain the parameters you defined by reading the properties in the predefined global variables.
// agentConf is a common parameter, and it is an object with a key-value structure. The key is the ID you defined for the control in the request parameter declaration, and the value is the configuration value specified by the user.
// agentConf is a common parameter, and it is an object with a key-value structure. The key is the ID you defined for the control in the request parameter declaration, and the value is the configuration value specified by the user.
const apiKey = _.chain(agentConf).get(['apiKey']).trim().value();
let deptNo = _.get(triggerConf, ['deptNo']);
// You need to validate the type and format of the input parameters to enhance the robustness of the function logic.
// The following code processes different value formats that may be obtained to ensure the accuracy of dept_no, see "Structure of triggerConf (request parameters)" for specific value formats.
try {
if (_.isString(deptNo)) deptNo = JSON.parse(deptNo);
if (_.isArray(deptNo)) deptNo = _.first(deptNo);
if (_.has(deptNo, ['dept_no'])) deptNo = _.get(deptNo, ['dept_no']);
deptNo = _.toInteger(deptNo);
} catch (e) {
throw new Error('Please enter a correct department number.');
}
// You can use third-party libraries and request parameters to call related APIs in the server environment and obtain results.
try {
const response = await axios({
method: 'post',
url: ' https://api.jodoo.com/api/v5/corp/department/user/list',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
data: { dept_no: deptNo }
});
const { data } = response;
// Process the result and define the return parameter value.
// You need to return an object, and the key of the object corresponds to the return parameter ID. If the return is an array, the corresponding output parameter needs to be set as "object[]" type.
const users = _.chain(data)
.get(['users'])
.map((it) => ({ name: it.name, username: it.username }))
.value();
return { users, count: users.length };
} catch (e) {
// For specific results, throw an error and define the error message.
const code =
_.get(e, ['response', 'data', 'code']) || _.get(e, ['code']) || -1;
let message =
_.get(e, ['response', 'data', 'msg']) ||
_.get(e, ['message']) ||
'Unknown error';
message = `Parameter error(${code}): ${message}`;
throw new Error(message);
}
// The global variables that can be referenced, the third-party libraries supported, and the data storage format of request parameters are described in "Related Terminology and Settings"
Example of Front-End Extension Code
// closeModal
const close = () => {
closeModal();
console.log('Modal closed after 4 seconds');
};
const reportUrl = 'https://t6ixa9nyl6.jodoo.com/f/6005518bc9f3660006e9d764';
setTimeout(() => close(), 4000);
// GET
let response2 = await fetch(reportUrl, {method: 'GET'});
console.log(await response2.text());
// openModal
// openModal:continue code execution after the modal is displayed, Worker ends after execution.
// await openModal:block code execution after the modal is displayed, follow the developer's code rules for subsequent actions.
await openModal({
title: 'If you have any requirements for the plugin, welcome to fill in this form.',
url: reportUrl
});
// Call back-end function.
await callFunction({
name: 'func.ID',
data: {}
})
// Syntax for throwing errors is the same as for the back-end function.
Using Parameter Hints
You can view the parameters you have set on the right panel. If you click the parameter or function, the corresponding invocation syntax will be added to the code editor.
Parameter Validation Prompt
Switch to Code View when editing common parameters, request parameters, and return parameters. If there is any error in the code, the corresponding code will be highlighted in red. When you hover the mouse over the code, a detailed error message will be displayed. Therefore, developers can test their code easily.
Syntax Error Prompt
For codes under Front-End Extension/Back-End Function (Python view), if an error occurred in your code, hover your cursor over the wrong statement underlined with a red wavy line. Then a detailed error message will be displayed. This helps developers easily spot any syntax errors in their code and rectify them promptly, providing a more intuitive and efficient development experience.
Demonstration
This is how to configure and execute the back-end function:
This is how to configure and execute the front-end extension:
Related Terminology and Settings
Global Variables
You can directly reference the global variables triggerConf, agentConf, and triggerConf in the code.
1. Structure of agentConf (common parameters).
The agentConf is the common parameter for the plugin.
Its structure is a key-value data structure in the corresponding programming language, such as a dict in Python or an object in Node.js. In this structure, the key is the ID you specify for the variable in the request parameter declaration, and the value is the data configured or referenced by the user.
2. Structure of triggerConf (request parameters).
The triggerConf is the request parameter for the plugin function.
Its structure is a key-value data structure in the corresponding programming language, such as a dict in Python or an object in Node.js. In this structure, the key is the ID you specify for the variable in the request parameter declaration, and the value is the data configured or referenced by the user (see the specific structure below).
Jodoo will help perform type conversions at runtime based on the control type you selected in the request parameter declaration and the source of the parameter value configured by the user so that you can get properly formatted values in your code. However, this conversion is not completely ensured. In more complex scenarios, you may encounter all kinds of parameters, including those unexpected ones. Considering this situation, you can throw errors to guide the user to adjust their configuration when they cannot handle certain types of parameters.
For example, if you are designing a plugin for image recognition and you add a Text field in the plugin's request parameters to accept image attachments from the user, you may receive different values based on different user configurations.
- The user may directly enter a file URL, and you will get a string with the content "https://...".
- The user may associate the value of the image/attachment control in Jodoo form data, and you will get a JSON string with the content '[{"url":"https://...","name":"..."},{"url":"https://...","name":"..."}]'.
- The user may associate a property of the return value of another plugin, and you may get a value of uncertain type.
You should handle the above situations in the code based on the plugin's functionality and its application scenarios. You also need to indicate to the user why this type is not supported by throwing errors and attaching messages.
3. Value storage format of request parameters.
a. Custom
If the user chooses to set values as Custom when configuring the plugin, the storage format of different controls in the request parameters is as follows:
Control | Storage Format |
Text | "hello, world" |
Single Select | "Option 1" |
Number | 3.14 |
Date&Time | "2022-09-09T08:00:00.000Z" |
Select Member | [{ "_id": "5b433bf80118dc44bcb9183b", "name": "david", "username": "david", "status": 1, "type": 0 }] |
Select Department | [{ "_id": "602f77c86aee9d2dd4c04bfc", "name": "first-level department", "dept_no": 3, "type": 0 }] |
b. Field Value
When the user configures the plugin in Jodoo forms, they can save values to fields. The storage format of the different controls in the request parameter declaration is as follows:
Form Control/Request Parameter Declaration Control | Field Value | String Example | |||||
Text | Single Select | Number | Date&Time | Select Member | Select Department | ||
Single Line | "hello, world" | ❌ | ❌ | ❌ | ❌ | ❌ | Value: "hello, world" ID: "_widget_1653375890324" |
Multi Line | "hello, world \n i am david" | ❌ | ❌ | ❌ | ❌ | ❌ | Value: "hello, world \n i am david" ID: "_widget_1653375890324" |
Number | 3.14 | ❌ | 3.14 | ❌ | ❌ | ❌ | Value: 3.14 ID: "_widget_1653375890324" |
Date&Time | "2022-09-09T08:00:00.000Z" | ❌ | ❌ | "2022-09-09T08:00:00.000Z" | ❌ | ❌ | Value: "2022-09-09 16:00:00" ID: "_widget_1653375890324" |
Radio | "Option 1" | ❌ | ❌ | ❌ | ❌ | ❌ | Value: "Option 1" ID: "_widget_1653375890324" |
Checkbox | '["Option 1","Option 3"]' | ❌ | ❌ | ❌ | ❌ | ❌ | Value: "Option 1, Option 3" ID: "_widget_1653375890324" |
Single Select | "Option 1" | ❌ | ❌ | ❌ | ❌ | ❌ | Value: "Option 1" ID: "_widget_1653375890324" |
Multi Select | '["Option 1","Option 3"]' | ❌ | ❌ | ❌ | ❌ | ❌ | Value: "Option 1, Option 3" ID: "_widget_1653375890324" |
Image | '[{"name":"emoji.gif","size":10168,"mime":"image/gif","url":"https://..."}]' | ❌ | ❌ | ❌ | ❌ | ❌ | Value: "https://..., https://..." ID: "_widget_1653375890324" |
Attachment | '[{"name":"emoji.jpg","size":10168,"mime":"image/jpeg","url":"https://..."}]' | ❌ | ❌ | ❌ | ❌ | ❌ | Value: "https://..., https://..." ID: "_widget_1653375890324" |
Signature | {"name":"signature_1663590240652.png","size":8800,"mime":"image/png","url":"https://..."} | ❌ | ❌ | ❌ | ❌ | ❌ | Value: "https://..." ID: "_widget_1653375890324" |
Serial No. | "00005" | ❌ | ❌ | ❌ | ❌ | ❌ | Value: "00005" ID: "_widget_1653375890324" |
Member | '[{"_id":"5b433bf80118dc44bcb9183b","name":"david","username":"david","status":1,"type":0}]' | ❌ | ❌ | ❌ | '[{"_id":"5b433bf80118dc44bcb9183b","name":"david","username":"david","status":1,"type":0}]' | ❌ | Value (name): "david" ID: "_widget_1653375890324" |
Members | '[{"_id":"5b433bf80118dc44bcb9183b","name":"david","username":"charles","status":1,"type":0}]' | ❌ | ❌ | ❌ | '[{"_id":"5b433bf80118dc44bcb9183b","name":"david","username":"charles","status":1,"type":0}]' | ❌ | Value (name): "david, charles" ID: "_widget_1653375890324" |
Department | '[{"_id":"602f77c86aee9d2dd4c04bfc","name":"first-level department","dept_no":3,"type":0}]' | ❌ | ❌ | ❌ | ❌ | '[{"_id":"602f77c86aee9d2dd4c04bfc","name":"first-level department","dept_no":3,"type":0}]' | Value: "standard chartered bank" ID: "_widget_1653375890324" |
Departments | '[{"_id":"602f77c86aee9d2dd4c04bfc","name":"first-level department","dept_no":3,"type":0}]' | ❌ | ❌ | ❌ | ❌ | '[{"_id":"602f77c86aee9d2dd4c04bfc","name":"first-level department","dept_no":3,"type":0}]' | Value: "standard chartered bank, johnson&johnson" ID: "_widget_1653375890324" |
4. Structure of triggerContext
The triggerContext structure stores basic information about the runtime environment, such as the tenant ID that triggered the plugin and Jodoo's open API domain name.
Supported Built-in Libraries
1. Node.js
- axios (0.21.0);
- form-data (2.3.3);
- lodash (4.17.20);
- moment-timezone (0.5.32), mongodb (3.6.3), mssql (8.1.2), mysql (2.2.5);
- pg (8.5.1), pg-hstore (2.3.3);
- sequelize (6.3.5).
2. Python
The following list includes callable built-in and third-party libraries.
- abc, argparse, array, asynchat, asyncio, asyncore, audioop;
- base64, binascii, binhex, bisect, bz2;
- calendar, cgi, cgitb, chunk, cmath, cmd, codecs, collections, colorsys, configparser, contextlib, contextvars, copy, copyreg, crypt, csv, ctypes;
- dataclasses, datetime, dbm, decimal, difflib, doctest;
- email, encodings, enum, errno;
- fnmatch, formatter, fractions, ftplib, functools;
- getopt, gettext, glob, graphlib, grp, gzip;
- hashlib, heapq, hmac, html, http;
- imaplib, imghdr, ipaddress, itertools;
- json;
- keyword;
- linecache, locale, logging, lzma;
- mailbox, mailcap, math, mimetypes, mmap;
- netrc, nis, nntplib, numbers;
- operator, optparse;
- parser, poplib, pprint;
- quopri;
- random, re, reprlib;
- secrets, select, selectors, shlex, smtpd, smtplib, sndhdr, socket, socketserver, sqlite3, ssl, statistics, string, stringprep, struct, sunau;
- tarfile, telnetlib, test, textwrap, time, timeit, types, typing;
- unicodedata, unittest, urllib, uu, uuid;
- warnings, wave, weakref, wsgiref;
- xml, xmlrpc;
- zlib, zoneinfo;
- requests (2.26.0), pymysql (1.0.2), pymssql (2.2.5), pymongo (3.12.1), psycopg2_binary (2.9.3);
- paramiko (2.8.0), pyjwt (2.3.0), lxml (4.6.3), xmltodict(0.12.0), jinja2 (2.11.3).
Available Front-End Extension API(s)
API Name | Description | Example |
$g.utils.openModal | Open a popup window | $g.utils.openModal({ title: 'Title of the popup window', url: 'URL for opening the popup window' }) |
$g.utils.closeModal | Close a popup window | $g.utils.closeModal() |
$g.utils.callFunction | Call back-end functions | $g.utils.callFunction({ name: 'func.ID', data: {} }) |
$g.utils.openUrl | Access a URL in a new tab | $g.utils.openUrl({ url: 'URL' }) |
$g.ui.onmessage | Receive messages sent from a popup window within an iframe | // Plugin $g.ui.onmessage = (message) => { // The message parameter corresponds to the value of the "pluginMessage" key in the "postMessage" method. console.log(message) } //Popup window iframe parent.postMessage({pluginMessage: 'any'}, '*')
|
Note:
Despite the new APIs above, you can still use openModal, closeModal, callFunction and so on.
Note(s)
1. When running plugins, there are some restrictions. The plugin has a timeout of 60 seconds. However, different features have their own timeout limits, for example, the timeout for front-end events is 20 seconds. This means if you use a plugin in Front-End Event, the timeout limit is 20 seconds.
2. Under Form View, the maximum length of Prompt Content is limited to 4,096 characters.