Defining Data to and from an Adapter

Schema is used to define the data that an adapter sends to and receives from the system it is integrating with. It does not define the type of data (see datatype in action.json). It is only utilized when the datatype is something that can be translated; for example, JSON.

Multiple schema files can be utilized by an adapter. Every action within the adapter can have a:

  • Request schema which defines the data the adapter will send to the other system on the API request.
  • Response schema which will define the data that the other system will send to the adapter on the response.

The request and response do not have to be separate schemas; on CRUD operations they will often be the same schema. When building an adapter from Swagger, each action within the Swagger will have query or body parameters defined. These are inserted into the schema files.

There is a good reason to define your schema files. Multiple systems often have a different way of defining entities. Itential Automation Platform (IAP) will have its way and often the other systems will not conform to that. Thus, you need the ability to translate data between them.

Take the following example:

  • If System A defines a device IP address as ip_addr and System B defines it as ipaddress and IAP defines it as ip_address then something needs to map the fields so that when we get a device from System A it takes ip_addr and puts it into ip_address for IAP.
  • Similarly, when IAP wants to add a new device to System B we take ip_address and put it in ipaddress. This translation happens automatically in the adapter libraries based on the information that you put in the schemas.

Schema files are based on a JSON schema with some added fields utilized by the adapter libraries. So if it works in the JSON schema, it should work in the schema file.

  • $id (required): This is the id/name of the schema and should be unique.
  • type (optional): Used in several places, it is the data type associated with a specific schema item. If you want type validation, then this field should be provided.
  • schema (required): This is the JSON schema specification used by the adapter’s schema file. The adapter uses Ajv to check the data against the schema so this specification should be one that is supported by Ajv.
  • description (optional): Used in many places, it is the description associated with a specific schema item.
  • translate (optional): This field is added by the adapter. It is available on any object type field. It tells the adapter library whether to run translation (or not) on the schema or object. Adapters will often be generated with an empty schema file and translation set to false as that is a simpler schema file. It defaults to true on the schema and is inherited from parents on objects within the schema.
  • dynamicfields (optional): This field is also added by the adapter. It is available on any object type field. It tells the adapter library whether to include any undefined fields in the data that it returns from the translator. It defaults to false on the schema and is inherited from parents on objects within the schema.
  • properties (required): This field contains all the data fields within the schema.
  • ph_request_type (required): This property should be set in every adapter schema file. It is never included with the data but used internally by the adapter library. It should have an enum that includes every action that uses this schema file. The default should be set to one of those actions.
  • default (optional but required with ph_request_type): Defines the default value for the data field. In the case of ph_request_type it must be one of the values in the enum. In general, be careful using default as it is possible to get data that you do not want inside your request/response. It is also best to be careful when using certain data rules (e.g., min/max). Make sure they are appropriate for the request/response.
  • enum (optional but required with ph_request_type): Defines the values that a data field of type string can have. In the case of ph_request_type, it is important that enum include every action that uses this schema.
  • Property names: These refer to how the data should be presented to IAP.
    • In the earlier example, IAP referred to IP addresses as ip_address. Accordingly, that means the value in this field would be ip_address.
    • In the case of token requests there should be a username property and a password property.
  • external_name: This field is added by the adapter. It is how the data should be presented to the system we are integrating with. In the prior example, System A referred to IP address as ip_addr. So for this field, the value would be set to ip_addr.

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"
    }
  },
  "definitions": {}
}

  • parse (optional): This field is added by the adapter. It tells the adapter if this field needs to be independently parsed (maybe it was stringified within the object that was sent). The default is false.
  • encode (optional): This field is added by the adapter. It tells the adapter whether to endode/decode the data contained within this field. Encoding is base 64. The default is not to encode.
  • encrypt (optional): This field is added by the adapter. It tells the adapter whether to encrypt/decrypt the data contained within this individual field.
    • Different encryption types are supported in the type field, but at the current time only AES encryption is used.
  • The key field contains the key used to encrypt/decrypt the data in the field.
    The default is not to encrypt.

SCHEMA

{
  "$id": "reqTokenSchema.json",
  "properties": {
    ….
    "username": {
      "type": "string",
      "description": "username to log in with",
      "parse": true,
      "encode": false,
      "encrypt": {
        "type": "AES",
        "key": "sfhgjhajlgsfhjlaghlshdg"
      },
      "external_name": "user_name"
    },
     ….
  },
  "definitions": {}
}

  • Required fields: You can have required fields within the schema using standard JSON schema techniques like having a list of “required” properties using a required value. You can also use more complex techniques for “required” properties based on the action.
  • One advantage of using required properties is that invalid requests will not be sent to the other system, thereby causing the system to error. Instead, the error will be returned by the adapter. This technique is not only efficient but saves time!

SCHEMA

{
  "$id": "sevone_alert",
  "type": "object",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "properties": {
    "ph_request_type": {
      "type": "string",
      "description": "type of request (internal to adapter)",
      "default": "getAlerts",
      "enum": [
        "getAlerts", "getAlertsFiltered", "getAlertsForDevice",
        "getAlertsForMapConnection", "getAlertsForMapNode",
        "createAlert", "updateAlert", "assignAlert", "ignoreAlert",
        "clearAlert", "deleteAlert"
      ],
      "external_name": "ph_request_type"
    },
    "id": {
      "type": "integer",
      "description": "id of the alert in sevone",
      "minimum": 0,
      "maximum": 999999999999,
      "external_name": “sys_id"
    },
    "origin": {
      "type": "string",
      "description": "where this alert was originated from",
      "external_name": "origin"
    }
  },
  "allOf": [
    {
      "if" : { "properties" : { "ph_request_type": { "enum": ["createAlert"] } } },
      "then" : { "required" : ["origin"] }
    }
  ],
  "definitions": {}
}

  • For help with schema definitions:
    • Create a ticket (ISD, IPSO or ADAPT).
    • You can work the ticket or allow the Itential Adapter Team to verify if the defined schema can be supported.
    • Once all changes are made, update the adapter-utils dependency in your adapter or run the adapterMigrator.
  • To update your adapter-utils dependency:
    • Change the version in the package.json dependencies.
    • rm –rf node modules
    • rm package-lock.json
    • npm install