- 05 Dec 2024
-
DarkLight
-
PDF
Using Dynamic Dependency in JSON Forms
- Updated on 05 Dec 2024
-
DarkLight
-
PDF
This simple use case explains how to use a dynamic device name as a dependency to dynamically provide a list of available interfaces on a Cisco Router.
See JSON Forms for details on creating JSON Forms and working with Form Builder to create the form elements referenced throughout this guide.
How to Build Dynamic Dependencies in JSON Forms
Follow these steps to build and configure dynamic dependencies in the JSON form. As the form is built, it can be previewed and the generated form schemas can be inspected.
Build JSON Form Structure
First, create the structure of the JSON form:
-
Create a JSON Form using the Automation Studio application.
-
Add two dropdown elements to the JSON form canvas using the Form Elements bin.
-
Hover over the menu button (…) at the far-right of each dropdown element and click the grid icon that appears. The Configure dialog opens.
-
Use the Label field to name the first dropdown element "Device" and the second "Interface" (Figure 1).
Figure 1
-
Click Save () to retain the naming inputs.
Add Dynamic Data Binding (Devices)
Next, add the logic that is required to dynamically populate the fields. This is done via API calls.
-
Hover over the menu button (…) and click the gear icon (⚙) to open the Configure dialog.
-
Click the +Options button located at the bottom of the dialog and select the Dynamic configuration type. The Dynamic Data Binding editor displays.
-
For the Request Configuration, use the following parameters.
Method: POST Base URL: /configuration_manager API Route: /devices
-
In the Request Body editor, add options to help sort the list as well as limit the amount of data that is pulled. At a minimum, the suggested options are:
{ "options": { "limit": 10000, "start": 0, "sort": [ { "name": 1, "address": -1, "port": 1 } ] } }
-
Once the Request Body is added, click the Make API Call button to generate the Response Body (Figure 2).
Figure 2
-
For Data Mapping, use
Source-To-Target
and the field inputs below.Source Property: /list Property Key: /name
-
Click the Query Data button. The Data Mapping properties will generate a preview list of devices that are available and discoverable by Configuration Manager (Figure 3).
Figure 3
-
Click the Back button once all configurations are set and then click Save to retain all changes.
Configure Field Dependency (Interfaces)
Once the "Device" selections are set, you can then configure the dynamic options for "Interfaces".
-
For the Request Configuration, use the following inputs.
Method: GET Base URL: /configuration_manager API Route: /devices/:name/configuration
-
Populate the Define Parameters name field with an active device name found within Configuration Manager.
-
Click the Make API Call button to return the device configuration (Response Body).
-
For Data Mapping, select
JSON Transformation
and input the "Transform Device Config" JST file provided at the end of this guide. -
Click the Run Transformation button. The Target Data Preview editor displays the interface name.
-
For the Variables (Field Dependency), select
Form Reference
and input the Device field configured in the previous section./device
Figure 4
-
Click the Back button once all interface mappings are set and then click Save to retain all configuration changes.
Validate the JSON Form
To test the logic of the JSON form:
- Navigate to IAP → Automation Studio → JSON Forms. JSON Forms is listed in the left navbar as its own subcategory in Automation Studio.
- Expand the JSON Forms menu in the left navbar and select the JSON Form.
- Click the eye icon at the top to open the Preview Form dialog.
- Select a device from the Device dropdown. Likewise, select the interface captured from the device configuration in the Interface dropdown.
- Click Show Form Data to display the JSON schema. Click Close to return to the preview modal.
Figure 5
The information presented within Preview Form is from a live device and may take a few seconds to display based on the size of the device configuration as well as network congestion.
JSON Transformation File for Device Config
To import, copy the JSON into a .txt
file and save it as “Transform Device Config.jst.json”. Then import the file into your transformation library under Automation Studio.
Click here for Transform Device Config JST JSON
{
"name": "Transform Device Config",
"incoming": [
{
"$id": "config",
"type": "string"
}
],
"outgoing": [
{
"$id": "out",
"type": "array"
}
],
"steps": [
{
"id": 2,
"type": "assign",
"from": {
"location": "incoming",
"name": "config",
"ptr": ""
},
"to": {
"location": "method",
"name": 1,
"ptr": "/args/0/value"
},
"context": "#"
},
{
"id": 1,
"type": "method",
"library": "String",
"method": "split",
"args": [
null,
"\n",
null
],
"view": {
"row": 1,
"col": 1
},
"context": "#"
},
{
"id": 4,
"type": "assign",
"from": {
"location": "method",
"name": 1,
"ptr": "/return"
},
"to": {
"location": "method",
"name": 3,
"ptr": "/args/0/value"
},
"context": "#"
},
{
"id": 3,
"type": "method",
"library": "Array",
"method": "filter",
"args": [
null,
"ƒ_query_1"
],
"view": {
"row": 2,
"col": 2
},
"context": "#"
},
{
"id": 6,
"type": "method",
"library": "Array",
"method": "map",
"args": [
null,
"ƒ_map_1"
],
"view": {
"row": 1,
"col": 3
},
"context": "#"
},
{
"id": 7,
"type": "assign",
"from": {
"location": "method",
"name": 3,
"ptr": "/return"
},
"to": {
"location": "method",
"name": 6,
"ptr": "/args/0/value"
},
"context": "#"
},
{
"id": 8,
"type": "assign",
"from": {
"location": "method",
"name": 6,
"ptr": "/return"
},
"to": {
"location": "outgoing",
"name": "out",
"ptr": ""
},
"context": "#"
}
],
"functions": [
{
"name": "ƒ_query_1",
"incoming": [
{
"type": [
"array",
"boolean",
"number",
"integer",
"string",
"object",
"null"
],
"$id": "element"
},
{
"title": "index",
"type": "number",
"optional": true,
"$id": "index"
},
{
"type": "array",
"$id": "array",
"optional": true
},
{
"$id": "thisArg",
"type": "object",
"properties": {},
"isContext": true,
"isThis": true,
"isIndexed": true
}
],
"outgoing": [
{
"title": "return",
"type": "boolean",
"$id": "return"
}
],
"steps": [
{
"id": 5,
"type": "assign",
"from": {
"location": "incoming",
"name": "element",
"ptr": ""
},
"to": {
"location": "method",
"name": 4,
"ptr": "/args/0/value"
},
"context": "#"
},
{
"id": 4,
"type": "method",
"library": "String",
"method": "trim",
"args": [
null
],
"view": {
"row": 2,
"col": 1
},
"context": "#"
},
{
"id": 6,
"type": "assign",
"from": {
"location": "method",
"name": 4,
"ptr": "/return"
},
"to": {
"location": "method",
"name": 1,
"ptr": "/args/0/value"
},
"context": "#"
},
{
"id": 1,
"type": "method",
"library": "String",
"method": "startsWith",
"args": [
null,
"interface",
null
],
"view": {
"row": 1,
"col": 1
},
"context": "#"
},
{
"id": 3,
"type": "assign",
"from": {
"location": "method",
"name": 1,
"ptr": "/return"
},
"to": {
"location": "outgoing",
"name": "return",
"ptr": ""
},
"context": "#"
}
],
"functions": [],
"view": {
"col": 1,
"row": 3
},
"id": "ƒ_query_1",
"comments": []
},
{
"name": "ƒ_map_1",
"incoming": [
{
"type": [
"array",
"boolean",
"number",
"integer",
"string",
"object",
"null"
],
"$id": "currentValue"
},
{
"title": "index",
"type": "number",
"optional": true,
"$id": "index"
},
{
"type": "array",
"$id": "array",
"optional": true
},
{
"$id": "thisArg",
"type": "object",
"properties": {},
"isContext": true,
"isThis": true,
"isIndexed": true
}
],
"outgoing": [
{
"title": "newValue",
"type": [
"array",
"boolean",
"number",
"integer",
"string",
"object",
"null"
],
"editable": true,
"$id": "newValue"
}
],
"steps": [
{
"id": 1,
"type": "method",
"library": "String",
"method": "trim",
"args": [
null
],
"view": {
"row": 1,
"col": 1
},
"context": "#"
},
{
"id": 2,
"type": "assign",
"from": {
"location": "incoming",
"name": "currentValue",
"ptr": ""
},
"to": {
"location": "method",
"name": 1,
"ptr": "/args/0/value"
},
"context": "#"
},
{
"id": 3,
"type": "assign",
"from": {
"location": "method",
"name": 1,
"ptr": "/return"
},
"to": {
"location": "outgoing",
"name": "newValue",
"ptr": ""
},
"context": "#"
}
],
"functions": [],
"view": {
"col": 1,
"row": 3
},
"id": "ƒ_map_1",
"comments": []
}
],
"view": {
"col": 3,
"row": 5
},
"_id": "6472c5cb363dd75d098b8d0d",
"description": "",
"comments": [],
"version": "3.23.3-2022.1.14"
}
Working with API Calls in JSON Forms with Dynamic Data Binding
When working with Dynamic Data Binding in JSON Forms, it is important to know the adapter calls do not always translate directly to the system-level API call the adapter is translating. This mainly applies to system-level GET
calls.
GET/POST API Calls
As a criterion, all system-level GET
calls with defined query parameters (optional or mandatory) in the adapter's OpenAPI/Swagger specification document will be treated (translated) as adapter POST
calls.
To illustrate, let's refer to the F5 BIG-IP API depicted in the figure below.
Figure 6: Retrieve BIG-IP Devices
At the system-level, this is a GET
API call. However, in order to make it an adapter call, the API has to be a POST
request that requires an object with the object keys listed. Without those parameters in the request body, the adapter call will not work nor provide any output. In this instance, the object keys can be provided as empty values:
{ "select":"", "filter":""}
Use OpenAPI Help to Access Parameters
For all system-level GET
calls that need to be translated to POST
calls, the required parameters that need to be provided can be found through:
https://IAP_IP-LINK_Address:PORT/rest-api
If an adapter GET
call is not listed, two items need to be reviewed:
-
Validate that an adapter
POST
call associated with the system does not exist. -
Validate the developer building the JSON form has authorization to make the adapter call that is being attempted.
Related Reading