Input and Output Schemas of an Action Definition

The input and output properties model the contracts of an action definition. The property values can accept different type of values that determine the formulation of the schemas. The action definition's input and output properties can refer to a schema defined in the schema section of the adapter definition document. The action definition can also accept an inline schema expressed in JSON schema format. In the following code snippet, the input refers to a schema and the output references a flow. The schemaType property defines the type of schema.

"output": {
        "schemaType": "application/schema+json",
        "schema": {
          "$ref": "#/schemas/insertRowOutput"
        }

The Rapid Adapter Builder platform supports the following types of schemas:

  • application/schema+json

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

  • avro/binary

    For Avro schema, set the schemaType to avro/binary.

The following sections describe how to define a static schema, dynamic schema, and how to blend a static schema with a dynamic schema

Static Schema

You can build an adapter explicitly to support a particular customer's instance. In this business scenario, the adapter is used for internal consumption, and hence the adapter need not support dynamic behavior that is more suited for customization. For this requirement, the adapter developer can choose to define a JSON schema using name, data type, and other properties. The JSON schema is a static schema and is easy to define.

Some external applications and services may not provide the ability to define custom resources or objects, and the ability to extend existing resources or objects with user defined attributes. In this business scenario, the adapter developer can use the API schema that is defined and documented by the external application.

Sample code that describes the schema for the spreadsheet and sheet objects for Google Sheets service:

"schemas": {
  "Sheet": {
    "type": "object",
    "properties": {
      "properties": {
        "type": "object",
        "properties": {
          "sheetId": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "index": {
            "type": "integer"
          },
          "sheetType": {
            "type": "string"
          }
        }
      }
    }
  },
  "Spreadsheet": {
    "type": "object",
    "properties": {
      "spreadsheetId": {
        "type": "string"
      },
      "spreadsheetUrl": {
        "type": "string"
      },
      "properties": {
        "type": "object",
        "properties": {
          "title": {
            "type": "integer"
          },
          "locale": {
            "type": "string"
          },
          "autoRecalc": {
            "type": "integer"
          },
          "timeZone": {
            "type": "string"
          }
        }
      },
      "sheets": {
        "type": "array",
        "items": {
          "$ref": "#/schemas/Sheet"
        }
      }
    }
  }
}
Dynamic Schema Generation

In this code sample, the input property is set to flow:sheetSchemaFlow value. This denotes that the schema is dynamically formulated by a flow.

The flow calls a metadata API provided by the external application. The flow then uses a jq expression and a JSON schema to give a response. The flow logic and jq expression differ based on the response of the metadata API. Some APIs return metadata that is close to JSON schema, and the jq expression required to modify the response to JSON schema is minimal.

For the above example, Google Sheet does not provide a metadata API. Instead, the adapter interprets the metadata as the value of the first row. The flow assumes that all columns are string types and reads the first row. Then the flow creates a JSON schema.

Note:

The Rapid Adapter Builder extension does not support the automatic creation of a trigger. However, the extension can import the schemas and flows that drive the trigger logic.

The following sample code shows the dynamic generation of the JSON schema based on the first row:

"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
    }
  ]
}
Blending Static Schema with Dynamic Schema

In some business scenarios where the external application supports extension fields for existing business objects, the adapter developer can combine a static schema with a dynamic schema. To accomplish this, do the following:

  • Execute a flow that provides the JSON schema to the input or output.
  • Design the static part of the JSON schema in the schema section.

In the flow, the adapter developer can use the following syntax to reference the schema:

"functions": [
  {
    "name": "getStaticSchema",
    "type": "expression",
    "operation": ".self.schemas.OrderEventSchema"
  },

The various steps involved in blending a static schema with a dynamic schema are:

  • Declare a flow with a function that maps to the schema using the .self.schemas object.
  • When this function is called within the flow, the state logic returns the schema in the form of a string. The schema is defined in the schemas section of the adapter definition document.
  • To the static schema, append the schema information extended properties for an object. Use jq to perform this operation.

    Note:

    The adapter developer must design this considering the external application's API response.

Sample code that shows how to design a JSON schema for a Zuora order object with an extended field:

"OrderOutputSchemaFlow": {
    "id": "OrderOutputSchemaFlow",
    "version": "0.1",
    "start": "startState",
    "specVersion": "0.8",
    "functions": [
      {
        "name": "getStaticSchema",
        "type": "expression",
        "operation": ".self.schemas.OrderEventSchema"
      },
      {
        "name": "dynamicFlow1",
        "operation": "connectivity::rest",
        "type": "custom"
      },
      {
        "name": "dynamicFlow2",
        "operation": "connectivity::rest",
        "type": "custom"
      },
      {
        "name": "dynamicFlow3",
        "operation": "connectivity::rest",
        "type": "custom"
      },
      {
        "name": "dynamicFlow4",
        "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": "getStaticSchema",
            "actionDataFilter": {
              "toStateData": "${ .staticOutput }"
            }
          },
          {
            "functionRef": {
                "refName": "dynamicFlow1",
                "arguments": {
                    "uri": "${\"https:/\"+\"/\"+.connectionProperties.invokeHostName+\"/settings/custom-fields/zuora/Order\"}",
                    "method": "GET"
                }
            },
            "actionDataFilter": {
                "results": "${ .body | .schema.properties | with_entries(select(.value.type != null)) | map_values({type: .type})}",
                "toStateData": "${ .staticOutput.properties }"
            }
          },
          {
            "functionRef": {
                "refName": "dynamicFlow2",
                "arguments": {
                    "uri": "${\"https:/\"+\"/\"+.connectionProperties.invokeHostName+\"/settings/custom-fields/zuora/Account\"}",
                    "method": "GET"
                }
            },
            "actionDataFilter": {
                "results": "${ .body | .schema.properties | with_entries(select(.value.type != null)) | map_values({type: .type})}",
                "toStateData": "${ .staticOutput.properties }"
            }
          },
          {
            "functionRef": {
                "refName": "dynamicFlow3",
                "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 }"
            }
          },
          {
            "functionRef": {
                "refName": "dynamicFlow4",
                "arguments": {
                    "uri": "${\"https:/\"+\"/\"+.connectionProperties.invokeHostName+\"/settings/custom-fields/zuora/Contact\"}",
                    "method": "GET"
                }
            },
            "actionDataFilter": {
                "results": "${ .body | .schema.properties | with_entries(select(.value.type != null)) | map_values({type: .type})}",
                "toStateData": "${ .staticOutput.properties }"
            }
            },
          {
            "functionRef": "constructResult",
            "actionDataFilter": {
              "toStateData": "${ .schema }"
            }
          },
          {
            "functionRef": "constructReturnObject",
            "actionDataFilter": {
              "toStateData": "${ .output }"
            }
          }
        ],
        "end": true
      }
    ]
}

Sample code:

{
  "flows": {
    "insertRecordInputFlow": {
      "id": "insertRecordInputFlow",
      "specVersion": "0.8",
      "version": "0.1",
      "start": "startState",
      "functions": [
        {
          "name": "generateSchema",
          "type": "expression",
          "operation": "{\"schemaType\": \"application/schema+json\", \"schema\": {type:\"object\", properties:{firstName:{type:\"string\"},lastName:{type:\"string\"},address:{type:\"string\"}}}}"
        }
      ],
      "states":[
        {
          "name":"startState",
          "type":"operation",
          "actions":[
            {
              "functionRef": "generateSchema",
              "actionDataFilter": {
                "toStateData": "${ .output }"
              }
            }
          ],
          "end": true
        }
      ]
    }
  }
}