Blackbox API Specification

Version 0.0.2

Introduction

Blackbox is an open standard that aims to unify communication between APIs and simplify API development. In the spirit of REST, the Blackbox API specification is built on two primary concepts: Services and Datatypes. Services provide the mechanisms for interacting with data, and data structure is defined by datatypes. Blackbox APIs follow a standard composition built around hypermedia enabling clients to easily find the services they require. As such, a client only needs to know the datatypes they are interested in in order to interact with the API.

Goals

The Blackbox API specification aims to achieve the following:

  • Provide a framework for human discoverable web services.
  • Provide a framework for machine discoverable web services.
  • Be easily applied retrospectively to existing web services.

Features

The Blackbox API specification provides the following features:

  • A hierarchy of services,
  • Support for the standardisation of services,
  • Hierarchical data linking,
  • Verbose mode for ease of interaction.

Terminology

The capitalised key words "MUST", "MUST NOT", "REQUIRED", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted according to BCP 14 RFC2119 RFC8174.

Blackbox API Structure

TODO: [BB-3] Add Content-Blackbox-Type to documentation. Reference x-content-blackbox-type.

Open API Document

A Blackbox API MUST be specified using the OpenAPI standard. The openapi-template.json file is a template for a Blackbox API and should be used as the starting point for a new API definition. The Blackbox command line interface (CLI) can be used to assist with building the OpenAPI document (see Blackbox Command Line Interface (CLI)).

The Hierarchy

TODO Update this section: Root node should use a service datastructure - remove the service map. Perhaps an OPTIONS request to a getServices operationId (i.e. to a child service) should return a service object rather than the standard GET response. OPTIONS response MUST include a header 'Content-Blackbox-Type' providing the full uri of the type (e.g. http://example.com/blackbox/value).

TODO Superceding the above TODO, due to issues in Chrome requesting OPTIONS with a body response, rather than use OPTIONS, add a 'service' query parameter for use with a GET request that will return a service object rather than the data object that would be received by default from a GET request.

TODO Add doco for how to link from a service node to a public deep child that is hidden behind intermediate private children; i.e. linking with parameters such as /books/{name}/likes/{username}.

The Blackbox specification aims to keep a relatively flat hierarchy for the sake of a simple to navigate API. A Blackbox API includes a single root node from which all services can be discovered by navigating the services’ links it specifies. The root node URL is the only address required for a client to navigate and use a Blackbox API. Every Blackbox API MUST have a single root node.

The root node includes links to the services offered. As such, the services themselves provide the second level of the Blackbox hierarchy. Services MAY be split into subservices grouped together by a single parent service, forming a three-layer hierarchy: Root > Service > Subservice. While the recommended structure includes only these two or three levels in the hierarchy, for the sake of easily adding an existing service into a Blackbox API the service MAY include subservices each with hierarchies of their own.

The Root Node

The root node MUST respond to a GET request with a service datastructure. The root node and service datatypes are specified in the openapi-template.json file.

Service Data Structure

The service data structure provides links and types of services. For example:

{
    "description": "A weather service.",
  "bbversion": "BB0.1",
  "links": {
    "self": {
      "href": "/weather"
    },
    "temperature": {
      "href": "/weather/temperature",
      "description": "The current temperature.",
      "types": [ {
        "uri": "http://example.com/datatypes",
        "name": "temperature",
        "format": "openDDL"
      } ]
    },
    "humidity": {
      "href": "/weather/humidity",
      "description": "The current humidity.",
      "types": [ { "uri": "/types.json", "name":"humidity" } ]
    }
  }
}

TODO The above is different to implementation. there should be a single weather link that lists temperature and humidity as the datatypes available.

In the above example the temperature type provides a URI.

The service data structure MUST conform to the "service" schema defined in the openapi-template.json specification and MAY include other fields, for example using the OpenAPI allOf property including both the "service" schema and other schemas. A link’s href MUST be a URI and MUST reference a REST API service, however the service MAY not be a Blackbox service.

Each link must contain a types array. See Service Types below details of the array’s content. If the referenced service is not a Blackbox service then the types array MUST be empty.

Service Types

Each service data structure includes an array of types. Each entry in the array MUST include a uri field and MAY include name, format and description fields. The uri MUST be a URI that references a datatype definition or a datatype repository that can be used to interpret the data served by the service. If the uri refers to a repository then the name field MUST be present and refer to a named datatype within the repositopry. The format field MUST be a string and SHOULD provide a hint as to the format of the datatype definition. Any service that includes a service type in the types array MUST provide services according to the datatype specified.

Blackbox Service

A Blackbox service is any restful API that adheres to the hierarchical data linking definitions below, and that offers verbose referencing as defined below.

Non-Blackbox Services

The Blackbox API supports linking to any service including non-Blackbox services. No restrictions are placed on such services.

Datatype Repositories

Datatypes SHOULD be stored in and loaded from repositories to encourage reuse. The format of the datatype repository is not mandated by the Blackbox specification, however it SHOULD allow a client developer to easily understand the data structures used by the service. Note that while the datatype repository format is not mandated, if the repository is to be used by the Blackbox CLI then the format will either need to be a map of named OpenAPI Schema Objects or a converter plugin to the CLI will be required (see Blackbox CLI).

Hierarchical Data Linking

Hierarchical entities SHOULD include only the root entity requested and SHOULD link to the child entities. In this context, an entity refers to an object that is available from a service within the API, a hierarchical entity has a data structure that includes a root entity and one or more levels of child data structures arranged in a hierarchy.

Linking

Links MUST be provided using the entity’s field name and a Link schema object as defined in the Blackbox Open API specification in place of the child entity. For example, consider the below entity, where the child object is available from a service within the API specification:

{
    "name": "John",
    "child": {
        "name": "Jane"
    }
}

Given that the child object is available from a service, say at path /children, then the child object does not need to be loaded and can be referenced via a link instead:

{
    "name": "John",
    "child": {
        "href": "/children/Jane",
        "description": "A link to John's child named Jane."
    }
}

If a request includes the depth query parameter, then the response MUST include all child entities within the hierarchy up to the depth specified. The depth query parameter has a type of integer and a value of zero indicates that the response MUST only include the root entity, this is the default behaviour. A value greater than zero indicates that the response MUST include all entities up to the specified depth within the hierarchy.

For example, consider the following recursive entity:

{
    "name": "John",
    "child": {
        "name": "Jane",
        "child": {
            "name": "Bob",
            "child": {
                "name": "Jill"
            }
        }
    }
}

Each child in the data structure is available from the /child service. A request for the entity with name "John" by default SHOULD return only the root entity with a link to the first child entity. Similarly, a request for the entity with name "John" with the depth query parameter set to 0 MUST include only the root entity and link to the entity with name "Jane". That is:

{
    "name": "John",
    "child": { "href": "/children/Jane" }
}

If the query includes parameter depth=1 then the response MUST include only the first level of the hierarchy beneath the root. That is:

{
    "name": "John",
    "child": {
        "name": "Jane",
        "child": { "href": "/children/Bob" }
    }
}

In order to determine the allowable methods for interacting with the linked to entity the link can be requested with the OPTIONS method and the verbose query parameter. See Verbose Mode, for the specified response.

Verbose Mode

When requesting a service, a query parameter verbose MUST be allowed. This is not applicable for requesting a parent service that only groups sub-services. For example, a request to /rulebase is not required to support the verbose query parameter, but a request to /rulebase/rules is. If the verbose query parameter is included in the request URI for a GET, POST, PUT, PATCH or OPTIONS request then, unless not applicable as described above, the response MUST provide a links field, or in the case of an array response, each item within the array MUST include a links field. The links field MUST include a self field containing a link object as defined by the Blackbox openapi-template.json schemas. All fields in the links object MUST also contain link objects, however which links are included beyond the self link are not mandated by the Blackbox specification.

For example, the response for a verbose GET request of a rule might be:

{
  ...
  "links": {
    "self": { "href": "/rulebase/rules/lightonwhendark" }
  }
}

TODO: Consider use of Location HTTP header for describing self links - e.g. after post.

TODO: Ref service datastructure links.