• The action.json file defines the actions that an adapter can take with the system it is integrating with. Actions are oftentimes referred to as integrations, APIs, endpoints, etc.
  • When building an adapter from Swagger, each action within Swagger will result in an action somewhere within the action.json of an adapter.
  • Actions are grouped together within an entity. In other words, each entity that is defined within the adapter will contain a single action.json file that contains all the possible actions you can take on the entity.
  • Some examples of actions include CRUD operations on an entity. For example, if you have a device entity, typical actions would be getDevice, createDevice, updateDevice, and deleteDevice. Other actions can also be defined for an entity. For example, getDeviceConfig and setDeviceConfig.
    • There are no limitations to the number of actions an entity can have.
  • Changes to the action.json do not require an Itential Automation Platform (IAP) or adapter restart.
  • name (required): This is the name of the action. When the method in adapter.js wants to take this action in the other system, it calls identifyRequest and provides the entity (first parameter) that this action is in and the name (second parameter) to identify the particular action.
  • protocol (required): REST is currently the only protocol supported but this field can support others. SOAP would still use REST as it works via HTTP/HTTPS via a POST call.
  • method (required): This is the REST method (sometimes referred to as a verb) the call uses. The most common are GET, POST, PUT, PATCH and DELETE but others are supported as well.
  • entitypath (required): This is the path that is added to the API call – https://host:port/entitypath. It is the added part of the call. You will notice it has variables in it {base_path}. These variables are replaced by the adapter library when the call is made.
  • schema: Default schema used for both the request and response, unless overridden by a specific schema.
    • This field is used to define the data that is sent between the adapter and the other system. You can have a different schema definition for every call in an action file. You may also have different schemas for the request and the response. Consequently you may see various schema files within an entity.
  • requestSchema: Defines a schema specific to the request.
  • responseSchema: Defines a schema specific to the response.
  • timeout (optional): While the adapter provides a property to set a global timeout, sometimes a particular action may take a long time. This property provides the ability to set a timeout on the action which overrides the global timeout.
    • If no value is provided or it has a value <= 0, the global request timeout will be used.
  • sendEmpty (optional): This flag tells the adapter there is no information to send and to still send an empty object (e.g.{}). The default is to not send any data.
  • sendGetBody (optional): This flag tells the adapter whether to send a body on a GET request. The default is no body on GET.
  • datatype (optional): This required field is used to define the type of data that is sent between the adapter and the other system. Can be used for the request or response, or both. Similarly to schemas, you may have different datatypes for the request and the response. Some example datatypes include PLAIN, XML, XML2JSON, URLENCODE, FORM and JSON.
    • If no datatype is provided, JSON is used as the default.
  • requestDatatype: Defines the datatype of the request.
  • responseDatatype: Defines the datatype of the response.
  • headers (optional): Many systems may require additional headers, and there are several ways to specify headers on the request. If the headers are always the same for a particular action, specifying them on the action is the best method. Other ways to specify headers include:
    • Global request properties – These headers are consistent on all requests.
    • Additional headers – These headers can be static for a call or dynamic by allowing them to be set based on user input.
    • Some headers are set by the adapter libraries based on the datatype, but those can be overridden if you specify them elsewhere.
  • responseObjects (required): Tells the adapter how to handle the response. Some actions may have many responses, so this field is often used for different purposes.
    • First, it provides a way to tell the adapter where in the response to find the data that IAP cares about.
    • Second, this field is crucial for standalone testing because it tells the adapter libraries where to find mock data that it should return when not integrated with the other system.
    • type: Used by the adapter libraries to identify which response object to use. The adapter libraries will go through a hierarchical process to match the best response type for a specific request. This field is required.
    • key: Used by the adapter libraries to tell the adapter where in the response to find the data that IAP is concerned with. It supports JSONquery. It allows you to remove metadata, etc. and only return the data that you really care about.
    • mockFile: This is used by the adapter libraries when running in stub mode (not integrated) to tell the adapter where the mock data that should be returned is located.

ACTION.JSON

    {
      "name": "getIP",
      "protocol": "REST",
      "method": "GET",
      "entitypath": "{base_path}/{version}/addresses/{pathv1}",
      "schema": "schema.json",
      "timeout": 3000,
      "sendEmpty": true,
      "sendGetBody": false,
      "datatype": "PLAIN",
      "headers": {},
      "responseObjects": [
        {
          "type": "default",
          "key": "",
          "mockFile": ""
        }
      ]
    },


ACTION.JSON

    {
      "name": "getDeviceInterface",
      "protocol": "REST",
      "method": "GET",
      "entitypath": "{base_path}/{version}/device/{pathv1}/interface/{pathv2}?{query}",
      "schema": "schema.json",
      "timeout": 3000,
      "sendEmpty": true,
      "sendGetBody": false,
      "datatype": "PLAIN",
      "responseObjects": [
        {
          "type": "default",
          "key": "",
          "mockFile": ""
        }
      ]
    },


ACTION.JSON

"/api/v1/devices"

"/api/v1/devices/{pathv1}"

"/api/v1/devices/{pathv1}?{query}"

"/api/v1/devices/{pathv1}/interface/{pathv2}?{query}"

  • You can hard code the version in a specific action call and in all action calls. However, it is easier to maintain/upgrade to a new version if this is set in a global property. Then the entity path can use the global property (first path).
  • You can still support unique actions that use a different version by hard coding that version in the second path. This allows the adapter to support integration to many different versions of APIs by simply using a property for the predominant version and then hardcoding other versions.

ACTION.JSON

"/api/{version}/devices/{pathv1}?{query}"

"/api/v2.0/devices/{pathv1}?{query}"

  • Base Path works like version; you can hard code the base_path in any and all action calls. However, it is easier to maintain/upgrade to a new base_path if this is set in a global property. Then the entity path can use the global property (first and second paths). You can still support unique action calls that use a different base_path by hard coding that base_path (third and fourth paths).
  • Note: Remember the entitypath is set up to support a great deal of flexibility in the API calls. This ranges from easy maintenance to providing dynamic information on each call.

ACTION.JSON

"/{base_path}/{version}/devices/{pathv1}?{query}"

"/{base_path}/v2.0/devices/{pathv1}?{query}"

"/api/{version}/devices/{pathv1}?{query}"

"/api/v2.0/devices/{pathv1}?{query}"


ACTION.JSON

    {
      "name": "getToken",
      …
      "requestSchema": "reqTokenSchema.json",
      "responseSchema": "respTokenSchema.json",
      …
    }


REQUEST SCHEMA

{
  "$id": "reqTokenSchema.json",
  "type": "object",
  "schema": "http://json-schema.org/draft-07/schema#",
  "translate": true,
  "dynamicfields": true,
  "properties": {
    "ph_request_type": {
      "type": "string",
      "description": "type of request (internal to adapter)",
      "default": "getToken",
      "enum": [
        "getToken",
        "healthcheck"
      ],
      "external_name": "ph_request_type"
    },
    "username": {
      "type": "string",
      "description": "username to log in with",
      "external_name": "user_name"
    },
    "password": {
      "type": "string",
      "description": "password to log in with",
      "external_name": "passwd"
    }
  },
  "required": ["username", "password"],
  "definitions": {}
}



RESPONSE SCHEMA

{
  "$id": "respTokenSchema.json",
  "type": "object",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "translate": true,
  "properties": {
    "ph_request_type": {
      "type": "string",
      "description": "type of request (internal to adapter)",
      "default": "getToken",
      "enum": [
        "getToken"
      ],
      "external_name": "ph_request_type"
    },
    "token": {
      "type": "string",
      "description": "the token returned from system",
      "external_name": "access_token"
    }
  },
  "definitions": {}
}


ACTION.JSON

    {
      "name": "getIP",
      "protocol": "REST",
      "method": "GET",
      "entitypath": "{base_path}/{version}/addresses/{pathv1}",
      "schema": "schema.json",
      "timeout": 3000,
      "sendEmpty": true,
      "sendGetBody": false,
      "datatype": "PLAIN",
      “requestDatatype”: “PLAIN”,
      “responseDatatype”: “XML”,
      "responseObjects": [
        {
          "type": "default",
          "key": "",
          "mockFile": ""
        }
      ]
    },


ACTION.JSON

    {
      "name": "getIP",
      "protocol": "REST",
      "method": "GET",
      "entitypath": "{base_path}/{version}/addresses/{pathv1}",
      "schema": "schema.json",
      "timeout": 3000,
      "datatype": "PLAIN",
      "sendEmpty": true,
      "sendGetBody": false,
      "responseObjects": [
        {
          "type": "default",
          "key": "",
          "mockFile": ""
        }
      ]
    },


ACTION.JSON

    {
      "name": "getIP",
      "entitypath": "{base_path}/{version}/addresses/{pathv1}?{query}",
	….
      "responseObjects": [
        {
          "type": ”anykey",
          "key": "result[id=88911]",
          "mockFile": ”a.json"
        },
        {
          "type": ” anykey",
          "key": "result[4]",
          "mockFile": ”b.json"
        },
        {
          "type": ” anykey",
          "key": "result.goodData",
          "mockFile": ”c.json"
        }
      ]
    },


ACTION.JSON

    {
      "name": "getIP",
      "entitypath": "{base_path}/{version}/addresses/{pathv1}?{query}",
	….
      "responseObjects": [
        {
          "type": "name-abc123",
          "key": "",
          "mockFile": "a.json"
        },
        {
          "type": "error",
          "key": "",
          "mockFile": "b.json"
        },
        {
          "type": "name=happy",
          "key": "",
          "mockFile": "c.json"
        }
      ]
    },

  • Can still support different mock data based on the URI structure:
    • path variables (withPathv#)
    • query (withQuery)
  • On a call with a path variable (http://system:80/path/ver/addresses/abc123) the y.json is returned
  • On a call with a query variable (http://system:80/path/ver/addresses?name=abc123) the z.json is returned.
  • Otherwise the x.json is returned.

ACTION.JSON

    {
      "name": "getIP",
      "entitypath": "{base_path}/{version}/addresses/{pathv1}?{query}",
	….
      "responseObjects": [
        {
          "type": "default",
          "key": "",
          "mockFile": "x.json"
        },
        {
          "type": "withPathv1",
          "key": "",
          "mockFile": "y.json"
        },
        {
          "type": "withQuery",
          "key": "",
          "mockFile": "z.json"
        }
      ]
    },