NoCode Template Builder 🆕
In the following documentation, we will explore the utilization of StackGuardian's NoCode Template Builder for creating and configuring templates. Additionally, we will delve into the various custom UI widgets introduced, understand the SG noCode tab, and provide examples for using these widgets in your templates.
The NoCode Template Builder allows users to build and configure templates using JSONSchema Form representation of input JSON data, providing a streamlined experience for creating Orchestrator Workflows without needing extensive coding knowledge.
The NoCode interface enables flexible UI structuring for a seamless user experience. Explore its capabilities at React JSONSchema Form.
Navigating the NoCode Template Builder
- Navigate to Orchestrator > Library > select one of the subscribed templates.
- SG noCode: This tab contains the JSONSchema Form representation of the input JSON data, used to provide the SG noCode experience for template users.
- Click on “Show Schema” to enable the Form JSON Schema.
JSON Schema Example
Below is an example of a JSON Schema defining properties, types, descriptions, and default values. Accepted types include "array", "boolean", "integer", "null", "number", "object", and "string".
{
"$schema": "http://json-schema.org/schema#",
"type": "object",
"properties": {
"enable_autoscaling": {
"type": "boolean",
"description": "Controls if autoscaling should be enabled for the deployment",
"default": true
},
"max_replicas": {
"type": "integer",
"description": "Maximum number of replicas for the autoscaling group",
"default": 10
},
"log_level": {
"type": "string",
"description": "Specifies the log level for the application (e.g., DEBUG, INFO, WARN, ERROR)",
"default": "INFO"
}
},
"required": ["enable_autoscaling", "max_replicas"]
}
Custom UI Widgets
Apart from the types mentioned above, we introduced custom UI widgets. The following custom UI widgets enhance the user experience by providing more flexibility and functionality in form creation.
1. Textarea Widget
Allows users to input multiline text, making it suitable for fields requiring extensive text entries, such as JSON objects.
- Form GUI
- Form JSON Schema
- UI Schema
{
"$schema": "http://json-schema.org/schema#",
"type": "object",
"properties": {
"tags": {
"type": "string",
"default": "Some default text"
}
}
}
{
"tags": {
"ui:widget": "textarea"
}
}
2. MultiSelectWidget
Enables users to select multiple options from a predefined list. This is useful for scenarios where multiple selections are needed.
- Form GUI
- Form JSON Schema
- UI Schema
{
"$schema": "http://json-schema.org/schema#",
"type": "object",
"properties": {
"mime_types": {
"type": "array",
"title": "A multiple-choice list",
"minItems": 2,
"items": {
"type": "string",
"enum": [
"foo",
"bar",
"fuzz",
"fooing"
]
},
"default": [
"foo"
]
}
}
}
{
"mime_types": {
"ui:widget": "MultiSelectWidget"
}
}
3. SelectWidget
Allows users to select a single option from a dropdown list. It is similar to the default Select field in RJSF but offers additional customization.
- Form GUI
- Form JSON Schema
- UI Schema
{
"$schema": "http://json-schema.org/schema#",
"type": "object",
"properties": {
"mime_types": {
"type": "string",
"title": "Where are you headed?",
"enum": [
"canada",
"usa",
"international"
],
"enumNames": [
"Canada",
"USA"
]
}
},
"required": [
"bucket_region",
"mime_types"
]
}
{
"mime_types": {
"ui:widget": "SelectWidget"
}
}
4. Password Widget
The password widget renders a string field with password type functionality, masking the input.
- Form GUI
- Form JSON Schema
- UI Schema
{
"password": {
"type": "string",
"description": "Enter your password",
"default": ""
}
}
{
"password": {
"ui:widget": "password",
"ui:options": {
"placeholder": "Enter your password"
}
}
}
5. AsyncSelectWidget
The AsyncSelectWidget is a dynamic select component that makes an API call to fetch options. This is useful for scenarios where the options are not static and need to be retrieved from a server.
- Form GUI
- Form JSON Schema
- UI Schema
{
"$schema": "http://json-schema.org/schema#",
"type": "object",
"properties": {
"products": {
"type": "string",
"description": "This is an example of an async select widget",
"default": "Samsung Universe 9"
}
}
}
{
"products": {
"ui:widget": "AsyncSelectWidget",
"apiSchema": {
"endpoint": "https://dummyjson.com/products",
"limit": 10,
"dataKey": "products",
"labelKey": "title",
"valueKey": "title"
}
}
}
6. CustomCodeWidget
The CustomCodeWidget
enables dynamic and context-aware customization of form fields in real-time. By executing custom JavaScript (JS) code in a Node.js v20 runtime environment, it allows users to fetch, process, and populate form data dynamically.
Key Features
- Dynamic Data Fetching: Fetch data from APIs or other sources in real-time.
- Custom JavaScript Support: Users can write custom JS code to handle data processing and form updates.
- Runtime Environment: Executes in a Node.js v20 environment with pre-configured capabilities.
- Contextual Awareness: Leverages variables like
sg_context
for details such asorg
,wfgrp
,stack
, andwf
.
How It Works
Users provide custom JavaScript code in the apiSchema.handler
configuration. This code is executed to process data and dynamically update the jsonSchema
and uiSchema
of the form.
Accessible Variables in JS Code
sg_context
:viewContext
: Contains contextual keys likeorg
,wfgrp
,stack
,wf
.token
: User's authentication token for making secure API calls.user
: Provides user details, such as email and role.
deploymentPlatformConfig
: Contains configurations for the selected connector, if applicable.
- Form GUI
- Form JSON Schema
- UI Schema
{
"$schema": "http://json-schema.org/schema#",
"type": "object",
"properties": {
"workflow_output": {
"title": "Workflow Outputs",
"type": "string"
}
}
}
{
"workflow_output": {
"ui:widget": "CustomCodeWidget",
"ui:title": "Workflow Outputs",
"apiSchema": {
"triggerType": ["onLoadItems"],
"handler": "// Base API URL\n" +
"const baseUrl = 'https://testapi.qa.stackguardian.io';\n" +
"\n" +
"// Extract params from sg_context\n" +
"let orgName = sg_context?.viewContext?.org || 'demo-org';\n" +
"let wfGrpName = sg_context?.viewContext?.wfgrp || 'tf-init-richard-test-cases';\n" +
"let stackName = sg_context?.viewContext?.stack || '';\n" +
"let stackId = stackName ? `/stacks/${stackName}` : '';\n" +
"let token = sg_context?.token;\n" +
"\n" +
"let finalOptions = [];\n" +
"\n" +
"// API URL to fetch workflows\n" +
"let apiUrl = `${baseUrl}/api/v1/orgs/${orgName}/wfgrps/${wfGrpName}${stackId}/wfs/listall/`;\n" +
"const api_response = await fetch(apiUrl, {\n" +
" headers: {\n" +
" Authorization: `${token}`\n" +
" }\n" +
"});\n" +
"\n" +
"// Parse JSON response\n" +
"let listallWfs = await api_response.json();\n" +
"\n" +
"// Iterate over workflows to fetch outputs\n" +
"for (let i = 0; i < listallWfs.msg.length; i++) {\n" +
" try {\n" +
" let item = listallWfs.msg[i];\n" +
" let wfName = item.ResourceName;\n" +
" let wfOutputUrl = `${baseUrl}/api/v1/orgs/${orgName}/wfgrps/${wfGrpName}${stackId}/wfs/${wfName}/outputs/`;\n" +
" let outputResponse = await fetch(wfOutputUrl, {\n" +
" headers: {\n" +
" Authorization: `${token}`\n" +
" }\n" +
" });\n" +
" let wfData = await outputResponse.json();\n" +
" let wfOutputs = wfData?.data?.outputs || {};\n" +
"\n" +
" Object.keys(wfOutputs).forEach(key => {\n" +
" finalOptions.push({\n" +
" label: `${wfGrpName}.${stackName}.${wfName}.${key}.value`,\n" +
" value: `${wfGrpName}.${stackName}.${wfName}.${key}.value`\n" +
" });\n" +
" });\n" +
" } catch (e) {\n" +
" console.log(e);\n" +
" }\n" +
"}\n" +
"\n" +
"// Update jsonSchema and uiSchema\n" +
"let modifiedUISchema = { workflow_output: { 'ui:widget': 'select' } };\n" +
"let modifiedJsonSchema = { type: 'object', properties: {} };\n" +
"modifiedJsonSchema.properties['workflow_output'] = {\n" +
" type: 'string',\n" +
" title: 'Workflow Outputs',\n" +
" enum: finalOptions.map(item => item.value)\n" +
"};\n" +
"return { jsonSchema: modifiedJsonSchema, uiSchema: modifiedUISchema };"
}
}
}
The code dynamically fetches workflow outputs based on the context (org, workflow group, and stack) and populates a dropdown field in the form with those outputs.
// Base API URL
const baseUrl = 'https://testapi.qa.stackguardian.io';
// Extract params from sg_context
let orgName = sg_context?.viewContext?.org || 'demo-org';
let wfGrpName = sg_context?.viewContext?.wfgrp || 'tf-init-richard-test-cases';
let stackName = sg_context?.viewContext?.stack || '';
let stackId = stackName ? `/stacks/${stackName}` : '';
let token = sg_context?.token;
let finalOptions = [];
// API URL to fetch workflows
let apiUrl = `${baseUrl}/api/v1/orgs/${orgName}/wfgrps/${wfGrpName}${stackId}/wfs/listall/`;
const api_response = await fetch(apiUrl, {
headers: {
Authorization: `${token}`,
},
});
// Parse JSON response
let listallWfs = await api_response.json();
// Iterate over workflows to fetch outputs
for (let i = 0; i < listallWfs.msg.length; i++) {
try {
let item = listallWfs.msg[i];
let wfName = item.ResourceName;
let wfOutputUrl = `${baseUrl}/api/v1/orgs/${orgName}/wfgrps/${wfGrpName}${stackId}/wfs/${wfName}/outputs/`;
// Fetch output
let outputResponse = await fetch(wfOutputUrl, {
headers: {
Authorization: `${token}`,
},
});
let wfData = await outputResponse.json();
let wfOutputs = wfData?.data?.outputs || {};
// Populate dropdown options
Object.keys(wfOutputs).forEach((key) => {
finalOptions.push({
label: `${wfGrpName}.${stackName}.${wfName}.${key}.value`,
value: `${wfGrpName}.${stackName}.${wfName}.${key}.value`,
});
});
} catch (e) {
console.error(e);
}
}
// Update jsonSchema and uiSchema
let modifiedUISchema = { workflow_output: { 'ui:widget': 'select' } };
let modifiedJsonSchema = { type: 'object', properties: {} };
modifiedJsonSchema.properties['workflow_output'] = {
type: 'string',
title: 'Workflow Outputs',
enum: finalOptions.map((item) => item.value),
};
return { jsonSchema: modifiedJsonSchema, uiSchema: modifiedUISchema };
These custom UI widgets enhance the user experience by providing more flexibility and functionality in form creation. By leveraging these widgets, users can create more interactive and dynamic forms tailored to their specific needs.