Refer to the section of the following article for the advantages of using plug-ins to handle sensitive information and how they work:
Get Started with Developing Plug-ins
The Outline of the Plug-in for this Tutorial
In this sample, data within the Text area field will be translated to Spanish when the Translate button is clicked on the edit record page. The plug-in runs the Web API provided by DeepL, a machine learning translation service. This sample uses the free version of the service. For more information, refer to the official website:
DeepL API
An API key is required to run this API. By securely storing the authentication information within the plug-in settings, the Web API can be called without the API key being leaked to end users.
STEP 1: Create a Sample App
First, create a new Kintone App.
Set the fields in the sample App as follows.
Field Type
Field Code / Element ID
Field Name
Notes
Text area
source
Enter text
The text to translate.
Text area
translation
Translation
The translation result.
Blank space
button-space
none
The space to place the Translation button.
STEP 2: Generate an API Key from DeepL
Follow these steps to create a DeepL account and generate an API key to call the DeepL API.
Click the profile icon on the top right of the page, and then click Account.
Open the API Keys tab and click the copy icon to the right of the API key to copy it.
Note down the API key as it is needed for the integration in the later steps.
STEP 3: Prepare the Plug-in Files
Next, prepare the necessary files for the Kintone plug-in.
Create a directory with the following structure.
1
2
3
4
5
sample-plugin
├── css
├── html
├── image
└── js
Prepare a Plug-in Icon
Prepare an icon file for the plug-in.
Set the image under the "image" directory with the file name "icon.png".
Create a CSS file for the Plug-in Settings Page
Save the contents of the 51-modern-default.css file and name it "51-modern-default.css". Save the file under the "css" directory.
51-modern-default.css
Create an HTML file for the Plug-in Settings Page
Create an HTML file "config.html" and save it under the "html" directory. This HTML file will be used for creating the plug-in settings page.
Set up the HTML to display drop-downs and text boxes for the plug-in settings page.
Name
Type
Description
Space field
Drop-down
The Space field to place the Translate button. Only space fields can be selected.
Source field
Drop-down
The source field that will be translated. Only Text areas can be selected.
Translation field
Drop-down
The translation field to save the translation results. Only Text areas can be selected.
DeepL Key
Textbox
The API key from the DeepL account.
In this sample code, the class name is set so that the 51-modern-default style is applied.
STEP 4: Save the Sensitive Information in the Plug-in Settings
On the plug-in settings page, kintone.plugin.app.setConfig() can be used to save plug-in configuration settings. The information saved by this API can be retrieved using kintone.plugin.app.getConfig(), which can be called on both the Plug-in settings page and the record pages. This means that the saved settings can be viewed by both App Admins and non-admin users of the App. Therefore, kintone.plugin.app.setConfig() should not be used to save sensitive information.
Instead of using kintone.plugin.app.setConfig() to save the sensitive data, use the following Kintone JavaScript API:
Proxy Set Config
: kintone.plugin.app.setProxyConfig()
kintone.plugin.app.setProxyConfig() saves the plug-in configuration settings including the request URL, method, and headers. Information saved with this API can be retrieved only on the plug-in settings page, using the kintone.plugin.app.getProxyConfig() API. App users who do not have access to the plug-in's settings cannot retrieve the saved information.
The saved data though can be used outside of the plug-in's settings page to make external API requests, introduced in the later steps.
The function to be called after the settings have been successfully saved
Next, prepare the information required to run the API.
The contents of the request and the method of authentication differ depending on the Web API.
According to the DeepL API document, the following information is required to run this API:
Translate text
Item
Value
URL
https://api-free.deepl.com/v2/translate
Method
POST
Request Header
Authorization: DeepL-Auth-Key [yourAuthKey]
Content-Type: application/json
Request Body
text: Translation source in array format
target_lang: Translation target language
Set these values as the parameters for the kintone.plugin.app.setProxyConfig() API.
Since the actual request is made from the customization file, specify an empty object for the request body.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Specify external API
const apiUrl = 'https://api-free.deepl.com/v2/translate';
const method = 'POST';
// Get text box
const apiKeyField = document.getElementById('token');
const auth = escapeHtml(apiKeyField.value);
const apiHeader = {
Authorization: `DeepL-Auth-Key ${auth}`,
'Content-Type': 'application/json'};
const apiBody = {};
const successCallback = () => {};
if (apiKeyField.value === '') {
alert('Required value is missing.');
return;
}
kintone.plugin.app.setProxyConfig(apiUrl, method, apiHeader, apiBody, successCallback);
Next, add the code to the "config.js" file made in the previous step.
The process of saving information that is not sensitive needs to be run after the execution of the kintone.plugin.app.setProxyConfig() API. Therefore, kintone.plugin.app.setConfig() is run in the successCallback() function.
STEP 5: Retrieve the Saved Sensitive Information From the Plug-in Settings
When users navigate to the plug-in settings page, initial values need to be set in the fields. These initial values will come from the values saved with the kintone.plugin.app.setProxyConfig() API.
Use the following API to retrieve the saved sensitive information.
Proxy Get Config
: kintone.plugin.app.getProxyConfig()
(async (PLUGIN_ID) => {
'use strict';
// Escape values
const escapeHtml = (str) => {
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/\n/g, '
');
};
// Drop-down list for the translate button
const createOptionsForSpace = async () => {
let options = [];
// Get space fields
const spaceFields = await KintoneConfigHelper.getFields('SPACER');
if (spaceFields) {
spaceFields.forEach(field => {
const option = document.createElement('option');
option.value = field.elementId;
option.textContent = field.elementId;
options = options.concat(option);
});
}
return options;
};
// Create the drop-down list
const spaceField = document.getElementById('space-field');
const spaceOptins = await createOptionsForSpace();
spaceOptins.forEach(option => {
spaceField.appendChild(option);
});
// Drop-down list for the source and translation
const getKintoneFiled = async () => {
let options = [];
// Get text area fields
const textFields = await KintoneConfigHelper.getFields('MULTI_LINE_TEXT');
if (textFields) {
textFields.forEach(field => {
const option = document.createElement('option');
option.value = field.code;
option.textContent = field.label;
options = options.concat(option);
});
}
return options;
};
// Create the drop-down list
const textOptions = await getKintoneFiled();
const sourceField = document.getElementById('source-field');
const targetField = document.getElementById('target-field');
textOptions.forEach(option => {
const sourceFieldOption = option.cloneNode(true);
const targetFieldOptine = option.cloneNode(true);
sourceField.appendChild(sourceFieldOption);
targetField.appendChild(targetFieldOptine);
});
// Set initial value
const config = kintone.plugin.app.getConfig(PLUGIN_ID);
const setConfigValue = (field, options, element) => {
const selectedOption = options.find(
(option) => option.value === config[field]
);
if (selectedOption) {
element.value = config[field];
}
};
setConfigValue('sourceFieldValue', textOptions, sourceField);
setConfigValue('targetFieldValue', textOptions, targetField);
setConfigValue('spaceFieldID', spaceOptins, spaceField);
// Get text box
const apiKeyField = document.getElementById('token');
// Specify external API
const apiUrl = 'https://api-free.deepl.com/v2/translate';
const method = 'POST';
// Get settings information from the API request and set it as initial value
const proxyConfig = kintone.plugin.app.getProxyConfig(apiUrl, method);
const deepLApiToken = proxyConfig ? proxyConfig.headers.Authorization.split(' ')[1] : '';
if (deepLApiToken) {
apiKeyField.value = deepLApiToken;
}
// Save and Cancel button
const appId = kintone.app.getId();
const form = document.getElementById('submit-settings');
const cancelButton = document.getElementById('cancel-button');
form.addEventListener('submit', (e) => {
e.preventDefault();
const auth = escapeHtml(apiKeyField.value);
const apiHeader = {
Authorization: `DeepL-Auth-Key ${auth}`,
'Content-Type': 'application/json' };
const apiBody = {};
const successCallback = () => {
const newConfig = {
sourceFieldValue: escapeHtml(sourceField.value),
targetFieldValue: escapeHtml(targetField.value),
spaceFieldID: escapeHtml(spaceField.value)
};
if (spaceField.value === '' || sourceField.value === '' || targetField.value === '') {
alert('Required value is missing.');
} else {
kintone.plugin.app.setConfig(newConfig, () => {
window.location.href = `/k/admin/app/flow?app=${appId}`;
});
}
};
if (apiKeyField.value === '') {
alert('Required value is missing.');
return;
}
kintone.plugin.app.setProxyConfig(apiUrl, method, apiHeader, apiBody, successCallback);
});
cancelButton.addEventListener('click', () => {
window.location.href = `../../${appId}/plugin/`;
});
})(kintone.$PLUGIN_ID);
STEP 6: Send an External API Request
Update the "desktop.js" file in the "js" directory to implement the following features:
Use the plug-in's settings to retrieve the field information.
When the Translate button is clicked, run the DeepL API to translate the contents of the Enter text field and set the result into the Translation field.
Retrieve the Field Information
Use the Get Config API to retrieve the plug-in's configuration settings.
Get Config
: kintone.plugin.app.getConfig()
In the space field, display the Translate button and register an event handler for the click event.
Implement the function to run the DeepL API to translate the contents of the Enter text field.
To run an external Web API, use the following Kintone JavaScript API.
Plug-in Proxy Request
: kintone.plugin.app.proxy()
Specify the following information as the arguments:
pluginId: ID of this plug-in
url: URL of the Web API. Specify https://api-free.deepl.com/v2/translate.
method: HTTP method. Specify POST.
data: Request body. Specify data of the DeepL API request. For more information, refer to the following link:
Translate Text
When the API is run with the above arguments, the proxy server makes the request to the DeepL API.
The contents saved using kintone.plugin.app.setProxyConfig() and the contents specified in kintone.plugin.app.proxy() will be compared. If the following information match, the saved information will be added to the request header and request body.
Plug-in ID
URL
HTTP method
After the request to the DeepL API, set the returned translation result into the Translation field.
The final code of the "desktop.js" file is as follows:
Navigate to Kintone on the browser and check if the plug-in has been uploaded successfully.
STEP 8: Test the Plug-in
Test the Translate Button
When the Translate button is clicked on the edit record page, the Enter text field will be translated into Spanish and the result will appear in the Translation field.
Check that the sensitive information is not visible
Go to the Plug-in Settings page and confirm the following:
The drop-down list is displayed and can be selected
The information of the saved plug-in settings is set as the initial value.
Check using kintone.plugin.app.getProxyConfig()
Go to the edit record page and open the Console tab of the browser.
Run the following code.
As shown below, the execution result is null, confirming that the sensitive information is not visible to the App user.
Check using network tab request
Open the browser's Network tab on the edit record page.
Click the Translate button and call.json?... will be displayed. Click it, and the contents of the request sent by the DeepL API will be displayed. Confirm that the API key in the header is not displayed.
Conclusion
The past few tutorials have covered how to build out plug-ins, with this article focusing on how to deal with sensitive information. To provide more secure services for end-users of Kintone, always keep in mind how to handle sensitive information inside Kintone plug-ins.