Dynamic Configuration of Options

The configuration field can derive values from:

  • Static values from an array of key pairs.
  • A function defined by the "execute" field.

    The function derives the field values.

The following code example shows how the flow "execute": "flow:spreadsheetIdFlow" populates the values of the "spreadsheetId" configuration field.

Sample code:

"insertRowAction": {
  "description": "Insert Row Into Sheet",
  "summary": "This action inserts a new row into sheet.",
  "execute": "flow:insertRowFlow",
  "input": {
    "execute": "flow:sheetSchemaFlow"
  },
  "output": {
    "$ref": "#/schemas/insertRowOutput"
  },
  "configuration": [
    {
      "name": "spreadsheetId",
      "displayName": "Spreadsheet Name",
      "description": "Please select a spreadsheet to insert a row",
      "type": "COMBO_BOX",
      "options": "flow:spreadsheetIdFlow",
      "required": true
    },
    {
      "name": "workSheetId",
      "displayName": "Worksheet Name",
      "description": "Please select a worksheet to insert a row",
      "type": "COMBO_BOX",
      "options": "flow:worksheetIdFlow",
      "required": true,
      "dependencies": {
        "spreadsheetId": {
          "values": []
        }
      }
    }
  ]
}

Configuration field flows can dynamically derive the values for the options keyName and displayName by reading the data from external applications by invoking APIs. The following code example shows how a dynamic call to Google Sheets retrieves a set of spreadsheets that appear for user selection.

Sample code:

"flows":
{
   "spreadsheetIdFlow": {
     "id": "spreadsheetIdFlow",
     "description": "spreadsheetIdFlow",
     "specVersion": "0.8",
     "version": "0.1",
     "start": "startState",
     "functions": [
       {
         "name": "generalRestFunc",
         "type": "custom",
         "operation": "connectivity::rest"
       }
     ],
     "states": [
       {
         "name": "startState",
         "type": "operation",
         "actions": [
           {
             "functionRef": {
               "refName": "generalRestFunc",
               "arguments": {
                 "uri": "https://www.googleapis.com/drive/v3/files",
                 "method": "GET",
                 "parameters": {
                   "q": "mimeType='application/vnd.google-apps.spreadsheet' and trashed=false",
                   "pageSize": 1000
                 }
               }
             },
             "actionDataFilter": {
               "results": "${ .body.files | map({keyName:.id, displayName:.name}) }",
               "toStateData": "${ .output }"
             }
           }
         ],
         "end": true
       }
     ]
   },
   "worksheetIdFlow": {
     "id": "sheetIdFlow",
     "description": "sheetIdFlow",
     "specVersion": "0.8",
     "version": "0.1",
     "start": "startState",
     "functions": [
       {
         "name": "generalRestFunc",
         "type": "custom",
         "operation": "connectivity::rest"
       }
     ],
     "states": [
       {
         "name": "startState",
         "type": "operation",
         "actions": [
           {
             "functionRef": {
               "refName": "generalRestFunc",
               "arguments": {
                 "uri": "https://sheets.googleapis.com/v4/spreadsheets/{spreadsheetId}",
                 "method": "GET",
                 "parameters": {
                   "spreadsheetId": "${ .configuration.spreadsheetId }"
                 }
               }
             },
             "actionDataFilter": {
               "results": "${ .body.sheets | map({keyName:.properties.title, displayName:.properties.title}) }",
               "toStateData": "${ .output }"
             }
           }
         ],
         "end": true
       }
     ]
   }
 }

The ".configuration" property structure allows selection of values from the configuration fields. For example, the worksheetflowID flow accesses the user selected "spreadsheetId" value from the ".configuration.spreadsheetId" list of values.

The "actionDataFilter" property extracts data from the body.sheets JSON object of the API response, and then formats the data into key value map of keyName and displayName.

Create a Configuration Field Flow

You can author this type of flow in the following ways:

  • Manually author the relevant JSON code in the adapter definition document based on an understanding of the Rapid Adapter Builder semantics and syntax.
  • Use the Rapid Adapter Builder extension in Visual Studio Code to author the flow.
  • Model a flow in a Postman collection and then use the Rapid Adapter Builder extension in Visual Studio Code to import the collection and then convert it into adapter definition document.

Use Postman Conversion of Flows to Design Configuration Fields

Configuration fields often derive their values by querying data from APIs of external applications and services. For example, an action that can create a resource may need to query the API of an external service and retrieve a set of available resources. The query results may also include custom objects.

In this scenario, the adapter developer can do the following:

  • Test the API to get the list of available resources and save the example in the Postman collection.

  • Filter the response to contain only the required fields.

    Typically, for a specific configuration field, the retrieved data is a set of names and values corresponding to the set of the resources as defined external application or service.

  • Populate the configuration field.

  • The adapter developer can use the Rapid Adapter Builder extension for Visual Studio Code to import only the flow corresponding to the request.

Configuration fields use flows to substitute values that are needed with the variables. The input data is imported as hardcoded values and can derive their values from other configuration field selection. If the format of the data from the API response does not comply with the requirement of the configuration field, the adapter developer can use jq expressions to restructure the data into the required format.

Note:

The format of the required data differs based on the widget used. For text boxes, the required data is a single object with a name and a value property. For a table widget, the required data to initialize the configuration field requires an array of rows of data. For more information, see Design User Interface Components of an Adapter.

The following sample code shows a flow that:

  • Retrieves the collection of spreadsheets for a particular Google Sheet user, formats the results into an array of name and values, and displays in a combo dropdown.
  • Retrieves the collection of sheets within a spreadsheet, formats the results into an array of name and values, and displays in a combo dropdown.
"spreadsheetIdFlow": {
  "id": "spreadsheetIdFlow",
  "description": "spreadsheetIdFlow",
  "specVersion": "0.8",
  "version": "0.1",
  "start": "startState",
  "functions": [
    {
      "name": "generalRestFunc",
      "type": "custom",
      "operation": "connectivity::rest"
    }
  ],
  "states": [
    {
      "name": "startState",
      "type": "operation",
      "actions": [
        {
          "functionRef": {
            "refName": "generalRestFunc",
            "arguments": {
              "uri": "https://www.googleapis.com/drive/v3/files",
              "method": "GET",
              "parameters": {
                "q": "mimeType='application/vnd.google-apps.spreadsheet' and trashed=false",
                "pageSize": 1000
              }
            }
          },
          "actionDataFilter": {
            "results": "${ .body.files | map({keyName:.id, displayName:.name}) }",
            "toStateData": "${ .output }"
          }
        }
      ],
      "end": true
    }
  ]
},
"sheetIdFlow": {
  "id": "sheetIdFlow",
  "description": "sheetIdFlow",
  "specVersion": "0.8",
  "version": "0.1",
  "start": "startState",
  "functions": [
    {
      "name": "generalRestFunc",
      "type": "custom",
      "operation": "connectivity::rest"
    }
  ],
  "states": [
    {
      "name": "startState",
      "type": "operation",
      "actions": [
        {
          "functionRef": {
            "refName": "generalRestFunc",
            "arguments": {
              "uri": "https://sheets.googleapis.com/v4/spreadsheets/{spreadsheetId}",
              "method": "GET",
              "parameters": {
                "spreadsheetId": "${ .configuration.spreadsheetId }"
              }
            }
          },
          "actionDataFilter": {
            "results": "${ .body.sheets | map({keyName:.properties.title, displayName:.properties.title}) }",
            "toStateData": "${ .output }"
          }
        }
      ],
      "end": true
    }
  ]
}

The following sample code retrieves the list of topics, formats the results into an array of name and values, and displays in a combo dropdown.

"ListTopicsUIFlow": {
  "id": "ListTopicsUIFlow",
  "version": "0.1",
  "start": "startState",
  "specVersion": "0.8",
  "functions": [
    {
      "name": "generalRestFunc",
      "operation": "connectivity::rest",
      "type": "custom"
    }
  ],
  "states": [
    {
      "actions": [
        {
          "name": "listTopics",
          "functionRef": {
            "refName": "generalRestFunc",
            "arguments": {
              "uri": "${\"https://\"+.connectionProperties.hostName+\"/v1/projects/{projectID}/topics\"}",
              "method": "GET",
              "parameters": {
                "projectID": "${.connectionProperties.projectID}",
                "pageSize": 1000
              }
            }
          },
          "actionDataFilter": {
            "results": "${ .body|if .topics!=null then(.topics | map({keyName:(.name|split(\"/\")|.[3]), displayName:(.name|split(\"/\")|.[3])})) else map({keyName:\"\", displayName:\"\"}) end}",
            "toStateData": "${ .output }"
          }
        }
      ],
      "name": "startState",
      "type": "operation",
      "end": true
    }
  ]
}

Use Postman Conversion of Flows to Model Dynamic Schema

Typically, schemas are statically defined for the input and output of an action. However, an enterprise adapter may need to support the ability to call the external application or service to query a particular metadata model of an object or a resource. The API response contains:

  • A list of properties.

  • The property name and datatype of each property.

Note:

This format may vary from service to service.

However, the configuration field requires an array of objects in name and value format. The response for the flow that can support dynamic schema requires the response of the flow to produce metadata corresponding to the supported schema type that defines the structure.

The Rapid Adapter Builder platform supports the following formats:

  • application/schema+json

    For JSON schema, set the schemaType to application/schema+json value.

  • avro/binary

    For arvo schema, set the schemaType to avro/binary value.

If the API response returns a format of the object definiton that is neither of the above formats, the adapter developer can use jq expressions to transform and format the data into a JSON schema form.

The following sample code shows how to query the first row of a sheet in Google Sheet by using the heading as metadata.

"sheetSchemaFlow": {
  "id": "sheetSchemaFlow",
  "description": "sheetSchemaFlow",
  "specVersion": "0.8",
  "version": "0.1",
  "start": "startState",
  "functions": [
    {
      "name": "generalRestFunc",
      "type": "custom",
      "operation": "connectivity::rest"
    },
    {
      "name": "constructReturnObject",
      "type": "expression",
      "operation": "{\"schemaType\": \"application/schema+json\", \"schema\": .schema}"
    }
  ],
  "states": [
    {
      "name": "startState",
      "type": "operation",
      "actions": [
        {
          "functionRef": {
            "refName": "generalRestFunc",
            "arguments": {
              "uri": "https://sheets.googleapis.com/v4/spreadsheets/{spreadsheetId}/values/{range}",
              "method": "GET",
              "parameters": {
                "spreadsheetId": "${ .configuration.spreadsheetId }",
                "range": "${ .configuration.sheetId + \"!A1:Z1\" }",
                "majorDimension": "ROWS"
              }
            }
          },
          "actionDataFilter": {
            "results": "${ .body.values[0] | map({key:., value: {type:\"string\"}}) | from_entries | {type: \"object\", properties: .} }",
            "toStateData": "${ .schema }"
          }
        },
        {
          "functionRef": "constructReturnObject",
          "actionDataFilter": {
            "toStateData": "${ .output }"
          }
        }
      ],
      "end": true
    }
  ]
}

The following sample code shows a flow that combines a static schema with a custom metadata to create a unified JSON schema.

"RetrieveAPaymentMethodCustomFieldFlow": {
    "id": "RetrieveAPaymentMethodCustomFieldFlow",
    "version": "0.1",
    "start": "startState",
    "specVersion": "0.8",
    "functions": [
        {
            "name": "staticFlow",
            "type": "expression",
            "operation": ".self.schemas.RetrieveAPaymentMethod0ResponseSchema"
        },
        {
            "name": "dynamicFlow",
            "operation": "connectivity::rest",
            "type": "custom"
        },
        {
            "name": "constructResult",
            "type": "expression",
            "operation": ".staticOutput"
        },
        {
            "name": "constructReturnObject",
            "type": "expression",
            "operation": "{\"schemaType\": \"application/schema+json\", \"schema\": .schema}"
        }
    ],
    "states": [
        {
            "name": "startState",
            "type": "operation",
            "actions": [
                {
                    "functionRef": "staticFlow",
                    "actionDataFilter": {
                        "toStateData": "${ .staticOutput }"
                    }
                },
                {
                    "functionRef": {
                        "refName": "dynamicFlow",
                        "arguments": {
                            "uri": "${\"https:/\"+\"/\"+.connectionProperties.invokeHostName+\"/settings/custom-fields/zuora/PaymentMethod\"}",
                            "method": "GET"
                        }
                    },
                    "actionDataFilter": {
                        "results": "${ .body | .schema.properties | with_entries(select(.value.type != null)) | map_values({type: .type})}",
                        "toStateData": "${ .staticOutput.properties.body.properties }"
                    }
                },
                {
                    "functionRef": "constructResult",
                    "actionDataFilter": {
                        "toStateData": "${ .schema }"
                    }
                },
                {
                    "functionRef": "constructReturnObject",
                    "actionDataFilter": {
                        "toStateData": "${ .output }"
                    }
                }
            ],
            "end": true
        }
    ]
}