/ OPENAPIRESTXQXQUERY
 / 10.59350/pbvs9-p0443

OpenAPI Code Generator for RESTXQ

CC BY 3.0, Acarpentier from Wikimedia Commons

This article introduces an XQuery library that allows code generation according to the OpenAPI 3.0.2 standard based on RESTXQ. It uses xqDoc as well as XQuery function annotation with %rest and %test prefix, combined with type declaration and also searches for general information in package description resources (expath-pkg.xml and repo.xml). It ships with the “swagger-ui-dist” package grabbed from npm, but can be built without by ant. You can find the package in our eXist-repo.

OpenAPI

OpenAPI is the de-facto standard for documenting a REST API. Formerly known as “swagger” the OpenAPI Initative (OAI, now a part of the Linux Foundation) is the organization backing the specifications. They offer a well-structured format to describe RESTful APIs on top of HTTP. Following these structure, a project benefit from ideas and recommendations (as to provide an appropriate license for an API).

While the work of the OpenAPI Initiative continues at a github repo a rendering is offered at swagger.io.

You have to follow the specifications and prepare a file named openapi.json containing metadata for a specific REST API. Simplified the core structure looks like the following.

{
  "openapi": "3.0.2",
  "info": "basic information like contact and licencse",
  "servers": "URLs to endpoints",
  "paths": "all the paths offered by the API",
  "tags": "description of tags used to organize endpoints (paths)",
  "externalDocs": "where to find the complete documentation or more to read."
}

Of course one can start and write about an API, but chances are good that a code generator is able to process the source code an creates the description file based on Java (e.g. Spring) or Python (e.g. Django REST framework) function annotations.

Code generators parse the code, collect the information and prepare the description. This article introduces an implementation for RESTXQ.

RESTXQ

RESTXQ is a proposal by Adam Retter and Christian Grün to provide a

W3C XQuery compliant approach to delivering RESTful Web Services from XQuery[.]

It allows to place an annotation in a function`s header to describe a resource path with well-known path templates: %rest:path("/openapi-test/{$param}/get"). Here we link a part of the path to a variable available to the function.

RESTXQ documentation with OpenAPI

A minimal paths object in openapi.json can describe this endpoint.

{
  "paths": {
    "/openapi-test/{param}/get": {
      "get": {
        "description": "foobar",
        "parameters": [
          {
            "in": "path",
            "name": "param",
            "description": "barfoo",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Default response"
          }
        }
      }
    }
  }
}

Like for other programming languages, we can also parse the code to generate the descriptor, but we need to stick to some conventions.

OpenAPI for RESTXQ

It takes advantage of two kinds of annotations: xqDoc and XQuery function annotations.

xqDocs

Describing a function in-place is a good practice. We use the function documentation to pass these description to the API documentation. This means the description of the function itself as well as the description of parameters passed to the function will become available to the documentation. So we have to extent our simple example from above by adding the documentation surrounded by (:~ and :)

(:~
 : Parses the input and prepares an output.
 : @param $param A string to include in the output.
 : @return The parsed input included in an xml element named test.
 :)
declare
  %rest:GET
  %rest:path("/openapi-test/{$param}/get")
function openapi-test-simple:get($param) {
    <test>
        <parameters n="1">{$param}</parameters>
        <response n="1" type="application/xml"/>
    </test>
};

We can include the information in our json file and add "description": "Parses the input and prepares an output.", but also adding description values to the path parameter and response.

In addition to @param and @return the @see annotation is mapped to an OpenAPI externalDocs object.

XQSuite test annotations

By using test annotation like %test:arg("param", "myPath") we can provide example values for the openAPI file.

{   "schema" :
      { "x-xml-type" : "xs:string",
       "type" : "string"
      },
    "in" : "path",
    "example" : "myPath",
    "name" : "param",
    "description" : "A string to include in the output.",
    "required" : true
}

It is always the first %test:arg annotation per parameter that is evaluated. By convention – or at least by assumption of this package – the first test should lead to a successful test result.

But what about these schema definition?

XQuery style

Of course we have to add type declarations to the parameter ($param as xs:string) and the return (as element(test)). They will become part of the API description.

Package metadata

EXpath packages provide a metadata file expath-pkg.xml that offers a minimal set of metadata:

  • version string,
  • a title,
  • name (URI) and
  • an abbreviation.

In addition eXist-db is aware of a resource named repo.xml that contains additional metadata. Both are evaluated to provide generic information about an API (mainly in the info object). It is recommended to fill as much fields as possible.

SPDX

Those who are using a license abbreviation according to the SPDX list will find a URL to the named license in the resulting OpenAPI file and an additional key x-is-spdx set to true.

Download

The package is available in a release and a develop flavor (using git flow) in the DARIAH-DE eXist-db repository.

Usage

The package is configured as a library and registers a namespace on a global level.

import module namespace openapi="https://lab.sub.uni-goettingen.de/restxqopenapi";

It exposes two functions:

  • openapi:main#1
  • openapi:json#1

Both receive a single parameter ($target) containing a path to a collection where to create the documentation for. It is aware of sub-collections.

To add more concrete information to the contact and servers objects, a file named openapi-config.xml stored within the target collection is evaluated. If this file is not present, the fallback is the configuration file provided with this package which might point to the example configuration provided with this package.

The documentation will be created for all functions with %rest: annotations no matter if they are registered or not. The demo implementation will register all provided functions. If this package is on a production server, it is recommended to remove the registration of the following modules:

  • content/openapi-tests-simple.xqm
  • content/openapi-tests-simple.xqm

If the openapi.json is all you need, just call openapi:json#1.

Swagger UI

This package provides a minimal installation of swagger ui. By default it shows the demo API. To look at the documentation for other packages, use the input field in the top bar and ask for something like openapi.json?target=/db/apps/SADE. On a local eXist-db installation the ui is available at localhost:8080/exist/apps/openapi/.

To include this view to a foreign application, simply add the following lines to the controller.xql.

else if (starts-with($exist:path, "/openapi/")) then
  <dispatch xmlns="http://exist.sourceforge.net/NS/exist">
    <forward
      url="/openapi/{ $exist:path => substring-after("/openapi/") => replace("json", "xq") }"
      method="get">
      <add-parameter name="target" value="{ substring-after($exist:root, "://") || $exist:controller }"/>
      <add-parameter name="register" value="false"/>
    </forward>
  </dispatch>

Limitations

Parameter name vs. variable name

There are still some conventions to follow to get the best out of the package. Especially the usage of the %test: annotation is done in a way to encourage developer to write at least a single test for the function as it provides the example values to the documentation. One consideration might be to establish an addition %openapi: annotation to get more precise information. When combining path and query parameters the path parameter variable name MUST be different from the query parameter`s name.

Example values

Example values are taken from the %test:arg() annotation and the usage of %test:args() is not supported.

Development

Find the git repo at gitlab.gwdg.de with the corresponding issue tracker.

To start developing this package use ant test to prepare a complete test env.

# load swagger-ui-dist
npm install
# prepare the xar package and a complete eXist database with all packages needed placed in autodeploy
ant test
# start exist
bash test/eXist-db-*/bin/startup.sh

or combined

npm install && ant test && bash test/eXist-db-*/bin/startup.sh