Skip to content

Annotating API to make it queryable

Daniel Bachler edited this page May 13, 2019 · 7 revisions

This page describes how you annotate your OpenAPI/Swagger definition to be queryable by the OpenRiskNet SPARQL service.

Purpose

An important concept of OpenRiskNet is the idea that we should try to make REST or similar HTTP based APIs semantically understandable for machines and humans. To fulfill this purpose, we combine several technologies:

  • OpenAPI for documenting the technical API structure (HTTP endpoints, json structure of responses and requests, ...). This can be parsed by various tools to e.g. generate client libraries automatically for specific APIs but also to generate interactive documentation for human developers (e.g. you can follow the View in SwaggerUI link on any service in the ORN registry.
  • JsonLD in the OpenAPI json document to be able to transform the entire OpenAPI document into the RDF data model and use ontology annotations. This can be done on a high level of operations as well as on a fine-grained level of individual json keys in the request and response descriptions by mapping json keys to ontology terms.

A brief intro to JsonLD

The basic premise of JsonLd is that it allows you to map a tree like Json data model to a triple based RDF data model. This mapping basically works by starting with a root node and using this as the initial subject, then mapping every json key to a predicate to arrive at the next logical nesting which is either a value or again a node (if the json entity is an object). From here, nested json keys are mapped in a similar fasion. Arrays are resolved simply as repeated uses of triples with the same predicate. Nodes (like the root node) can have a URI if one was assigned using the @id syntax (or rather x-orn-@id in OpenRiskNet as described below), but are otherwise assigned anonymous node ids.

Here is a simple plain JsonLD example that should hopefully clarify the concept:

{
  "@context": {"@vocab":"http://openrisknet.org/schema#"} ,
  "name": "Jane Doe",
  "address": {
    "street": "Wall street"
  }
  
}

This will be transcribed to the following triples:

subject predicate object
_:b0 http://openrisknet.org/schema#address _:b1
_:b0 http://openrisknet.org/schema#name Jane Doe
_:b1 http://openrisknet.org/schema#street Wall street

The @vocab definition in the @context header helps us here because it gives a default mapping of json keys to URIs if no explicit mapping has been given (like a concrete ontology term for a json key - this is described in more detail in the fine grained annotation section below).

There are no node ids given so anonymous ids are used in the form _:b0, _:b1 etc. From the root node we map with predicate "http://openrisknet.org/schema#name" to the value "Jane Doe" and with the predicate "http://openrisknet.org/schema#address" to another node, _:b1. From here another triple links with predicate http://openrisknet.org/schema#street to the value "Wall street".

Special considerations for JsonLD in the context of OpenRiskNet

One important requirement is that the combined OpenAPI/JsonLD document is a valid OpenAPI document. Unfortunately, JsonLD requires certain annotations that are not allowed under the OpenAPI spec (e.g. a top level "@context":{...} key). To reconcile these two worlds, we prefix such invalid JsonLD keys with x-orn- as most entities in the OpenAPI spec can be extended with arbitrary json if they use the x- prefix. The minimum recommended JsonLD context looks like this and should be included in every combined OpenAPI/JsonLD document at the root level:

"x-orn-@context": {
    "@vocab": "http://openrisknet.org/schema#",
    "x-orn": "http://openrisknet.org/schema#",
    "x-orn-@id": "@id",
    "x-orn-@type":"@type"
}

The x-orn-@context definition has to be renamed to @context manually if you want to parse the document as valid JsonLD (e.g. in the JsonLD playground). The registry does this step as part of the automated preprocessing.

Here again we use the @vocab definition to give a default mapping of Json keys to URIs. We then alias x-orn-@id to be functionally equivalent to the JsonLD construct @id and the same with x-orn-@type to @type. Finally, we define a convenience URI shortcut x-orn to mean "http://openrisknet.org/schema#" so we can map write shorter mappings to the default URIs.

High level annotation

The high level annotations are concerned with the basic building blocks of the API: The id of the service and the inputs and outputs of the endpoints (on a coarse level). Here we don't use fine-grained mappings inside the top level x-orn-@context section but instead use explicit annotations like x-orn-@id to specify ids for nodes like the top level or x-orn:returns to describe the high level output of an endpoint.

Below we give a top level x-orn-@id definition so that triples on the top level will have an explicit URI instead of using an anonymous node identifier. We also define the RDF type of the top level identity to be a "http://openrisknet.org/schema#Service"

openapi: 3.0.0
x-orn-@id: 'https://lazar.prod.openrisknet.org'
x-orn-@type: 'x-orn:Service'

and will be translated into:

subject predicate object
https://lazar.prod.openrisknet.org/ http://www.w3.org/1999/02/22-rdf-syntax-ns#type http://openrisknet.org/schema#Service

The following code shows the annotation of a single route: (The whole API can be found here)

'/compound/{InChI}':
  get:
    x-orn-@type: 'x-orn:Compound'
    'x-orn:path': 'https://lazar.prod.openrisknet.org/compound/{InChI}'
    'x-orn:method': Get
    tags:
      - compound
    description: Get compound representation
    parameters:
      - $ref: '#/components/parameters/inchi'
      - $ref: '#/components/parameters/subjectid'
    responses:
      '200':
        description: OK
        content:
          application/json:
            'x-orn:returns': application/json
            schema:
              'x-orn:property': InChI
              type: string
              example: InChI=1S/C6H6/c1-2-4-6-5-3-1/h1-6H

Each orn annotations will be used to translate lines of code into RDF triples. For parameters a reference was used. This reference fully described is:

parameters:
    inchi:
      name: InChI
      in: path
      description: InChI String
      required: true
      schema:
        type: string

Fine grained annotations

Every json key can be mapped to an ontology term that will then be used as a predicate in the rdf data model. This allows developers of APIs to annotate complex json response/return types on the level of individual keys and thus at a much finer granularity onr:expects and orn:returns structures above.

Here inchi is a json key and is described in the JSON-LD definition with an ontology URI it will be automatically converted.

{"x-orn-@context": {
    "@vocab": "http://openrisknet.org/schema#",
    "x-orn": "http://openrisknet.org/schema#",
    "x-orn-@id": "@id",
    "x-orn-@type":"@type",

    "smiles": "http://semanticscience.org/resource/CHEMINF_000018",
    "inchi": "http://semanticscience.org/resource/CHEMINF_000113",
    "inchikey": "http://semanticscience.org/resource/CHEMINF_000059",
    "cas": "http://semanticscience.org/resource/CHEMINF_000446"
  }
}
subject predicate object
_:b1 http://semanticscience.org/resource/CHEMINF_000113 _:b10
_:b10 http://openrisknet.org/schema#description InChI String^^http://www.w3.org/2001/XMLSchema#string
_:b10 http://openrisknet.org/schema#in path^^http://www.w3.org/2001/XMLSchema#string
_:b10 http://openrisknet.org/schema#name InChI^^http://www.w3.org/2001/XMLSchema#string
_:b10 http://openrisknet.org/schema#required true^^http://www.w3.org/2001/XMLSchema#boolean
Clone this wiki locally