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