Gitlab Community Edition Instance

Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • subugoe/openapi4restxq
  • mark.rainbird/openapi4restxq
2 results
Show changes
Commits on Source (63)
# tempaltes
.install_template: &installation
script:
# wait for service container
- while [ $(curl --head --silent http://$TARGET | grep -c "200 OK") == 0 ]; do sleep 2s; done
# upload package
- 'curl -X PUT -H "Content-Type: application/zip" --data-binary @$(ls build/*.xar) "http://admin:@$TARGET/exist/rest/db/openapi.xar"'
# install package
- 'curl -X GET "http://admin:@$TARGET/exist/rest/db/?_query=repo:install-and-deploy-from-db(''/db/openapi.xar'')"'
# check availabilty
# for debug use - 'curl --silent "http://$TARGET/exist/apps/openapi/openapi.json"'
- 'curl --silent "http://$TARGET/exist/apps/openapi/index.html" | grep "Swagger UI for OpenAPI for RESTXQ"' # swagger available
- 'curl --silent "http://$TARGET/exist/apps/openapi/openapi.json" | grep "OpenAPI for RESTXQ"' # openapi.json available
timeout: 5 minutes
# main CI instructions
image: docker.gitlab.gwdg.de/subugoe/openapi4restxq:latest
stages:
......@@ -7,23 +23,22 @@ stages:
build-develop:
except:
- master
- main
- tags
stage: build
script:
- npm install
- ant test
- ant
artifacts:
paths:
- build/*.xar
- test/
build-master:
build-main:
only:
- master
- main
stage: build
script:
- cp master.build.properties local.build.properties
- cp main.build.properties local.build.properties
- npm install
- ant test
artifacts:
......@@ -31,28 +46,33 @@ build-master:
- build/*.xar
- test/
installation:
installation-exist:
except:
- tags
stage: test
script:
- bash test/eXist-db-*/bin/startup.sh | tee output.log &
# wait for eXist
- while [ $(curl --head --silent http://localhost:8080 | grep -c "200 OK") == 0 ]; do sleep 2s; done
# shutdown eXist
- bash test/eXist-db-*/bin/shutdown.sh
- ls -al /tmp; mv /tmp/tests-* . || true
artifacts:
paths:
- output.log
- test/tests-*.xml
- test/eXist-db-*/webapp/WEB-INF/logs/expath-repo.log
reports:
junit: test/tests-*.xml
services:
- name: existdb/existdb:latest
alias: existdb
variables:
TARGET: "existdb:8080"
<<: *installation
installation-fusion:
stage: test
except:
- tags
services:
- name: repo.evolvedbinary.com:9443/evolvedbinary/fusiondb-server:latest
alias: fusiondb
variables:
TARGET: "fusiondb:4059"
<<: *installation
upload:
only:
- master
- main
- develop
except:
- tags
......
......@@ -13,7 +13,8 @@ OpenAPI conform documentation. It covers the RESTXQ annotations as well as the
[xqDocs](http://xqdoc.org/xqdoc_comments_doc.html) for describing the API.
It is meant to be used for a single [expath package](http://expath.org/spec/pkg)
and written for [eXist-db](http://exist-db.org).
and written for [eXist-db](http://exist-db.org), but supports [fusiondb](http://fusiondb.com)
as well.
## Build
For preparing the `openapi.json` the application is build with
......@@ -33,9 +34,37 @@ the file in the `autodeploy` directory or using the package manager application
installed by default at a running database.
## Use
The package provides a
### Integrated in own applications
By adding the following lines to the `controller.xql` a path like
[/myApplication/openapi/index.html](http://localhost:8080/exist/apps/myApplication/openapi/index.html)
will become available. (The usage of the pipe operator requires XQuery version 3.1.)
```xq
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>
```
### Standalone
By default the application provides a preview and a few simple REST paths as test
interfaces. Open the application via the Dashboard or browse to [http://localhost:8080/exist/apps/openapi/index.html](http://localhost:8080/exist/apps/openapi/index.html).
To view the documentation for other packages, use the input filed in the top bar
to ask for a description file at `openapi.json?target=/db/apps/myApplication`.
Optional the re-registration of functions to the RESTXQ engine is possible via
an additional parameter: `&register=true`.
## Configure
To include information not present in one of the parsed documents, the library
checks the availability of a resources named `openapi-config.xml` in the
root collection of the application where to create the description file for.
It is recommended to place a customized copy of the file provided with this
package.
## Develop
To start developing or testing the package a ant target is available that sets
......@@ -49,7 +78,6 @@ Behind the curtain the information will be collected by calling
inspect:inspect-module(xs:anyURI("/db/apps/openapi/content/openapi-tests-full.xqm"))
```
## Limitations
### combining path and query parameters
Query parameters passed by `%rest:query-param()` MUST use a name different from
......
project.name=https://lab.sub.uni-goettingen.de/openapi4restxq-develop
project.version=1.1.0
project.version=1.7.0
project.title=OpenAPI for RESTXQ
project.abbrev=openapi-develop
project.processorversion=4.6.0
project.processorversion=5.2.0
destfile=${build.dir}/${project.abbrev}-${project.version}.xar
test.dir=test
......@@ -57,13 +57,19 @@ Run `npm install` first to build a package that includes swagger-ui.</echo>
</target>
<target name="test" depends="antversion-test, xar">
<!-- task setpermissions requries at least ant 1.10.0 -->
<get src="https://bintray.com/existdb/releases/download_file?file_path=eXist-db-${project.processorversion}.tar.bz2" dest="${build.dir}/eXist-db-${project.processorversion}.tar.bz2" skipexisting="true" />
<untar src="${build.dir}/eXist-db-${project.processorversion}.tar.bz2" dest="${test.dir}" compression="bzip2" />
<setpermissions mode="755">
<file file="${test.dir}/eXist-db-${project.processorversion}/bin/startup.sh"/>
</setpermissions>
<copy file="${destfile}" todir="${test.dir}/eXist-db-${project.processorversion}/autodeploy" />
</target>
<delete dir="${test.dir}"/>
<get src="https://bintray.com/existdb/releases/download_file?file_path=exist-distribution-${project.processorversion}-unix.tar.bz2" dest="${build.dir}/eXist-${project.processorversion}.tar.bz2" skipexisting="true" quiet="true" />
<untar src="${build.dir}/eXist-${project.processorversion}.tar.bz2" dest="${test.dir}" compression="bzip2">
<cutdirsmapper dirs="1" />
</untar>
<setpermissions mode="755">
<fileset dir="${test.dir}">
<filename name="bin/*.sh"/>
</fileset>
</setpermissions>
<copy file="${destfile}" todir="${test.dir}/autodeploy" />
</target>
</project>
......@@ -23,20 +23,20 @@ declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";
: @see http://example.com/documentation/about/this
:)
declare
%rest:POST("{$body}")
%rest:consumes("application/xml", "text/xml")
%rest:path("/openapi-test/full/post/{$paramPath}/{$int}.{$format}")
%rest:query-param("getParam", "{$getParam}", "2019")
%rest:produces("application/json")
%output:method("json")
%test:arg("paramPath", "here")
%test:arg("int", "123")
%test:arg("format", "xquery")
%test:arg("getParam", "and-get")
%test:arg("body", "<incomming><node/></incomming>")
%rest:POST("{$body}")
%rest:consumes("application/xml", "text/xml")
%rest:path("/openapi-test/full/post/{$paramPath}/{$int}.{$format}")
%rest:query-param("getParam", "{$getParam}", "2019")
%rest:produces("application/xml" ,"text/xml")
%output:method("json")
%test:arg("paramPath", "here")
%test:arg("int", "123")
%test:arg("format", "xquery")
%test:arg("getParam", "and-get")
%test:arg("body", "<incoming><node/></incoming>")
function openapi-test-full:post(
$paramPath as xs:string,
$int as xs:int,
......@@ -55,3 +55,19 @@ as element(test) {
<response n="1" type="application/json"/>
</test>
};
(:~
: GET Method with a path defined also for a POST in a different module
: @return xml fragment that describes request and response
: @see http://example.com/documentation/about/this
:)
declare
%rest:GET
%rest:path("/openapi-test/simple/multi-module")
function openapi-test-full:multi-post()
as element(test) {
<test>
<parameters n="0"/>
<response n="1" type="application/xml"/>
</test>
};
......@@ -168,3 +168,68 @@ declare
%rest:path("/openapi-test/simple/head")
function openapi-test-simple:head()
as empty-sequence() {()};
(:~
: All Methods Test for OpenAPI
: @return empty
: @see http://example.com/documentation/about/this
:)
declare
%rest:GET
%rest:HEAD
%rest:POST
%rest:PUT
%rest:DELETE
%rest:path("/openapi-test/simple/multi")
function openapi-test-simple:multi-methods(){
()
};
(:~
: GET Method with a path defined also for a POST in a different function
: @return xml fragment that describes request and response
: @see http://example.com/documentation/about/this
:)
declare
%rest:GET
%rest:path("/openapi-test/simple/multi-function")
function openapi-test-simple:multi-get()
as element(test) {
<test>
<parameters n="0"/>
<response n="1" type="application/xml"/>
</test>
};
(:~
: POST Method with a path defined also for a POST in a different function
: @return xml fragment that describes request and response
: @see http://example.com/documentation/about/this
:)
declare
%rest:POST("{$body}")
%rest:path("/openapi-test/simple/multi-function")
function openapi-test-simple:multi-post-function($body)
as element(test) {
<test>
<parameters n="0"/>
<response n="1" type="application/xml"/>
</test>
};
(:~
: POST Method with a path defined also for a POST in a different module
: @return xml fragment that describes request and response
: @see http://example.com/documentation/about/this
:)
declare
%rest:POST("{$body}")
%rest:path("/openapi-test/simple/multi-module")
function openapi-test-simple:multi-post-module($body)
as element(test) {
<test>
<parameters n="0"/>
<response n="1" type="application/xml"/>
</test>
};
......@@ -17,7 +17,7 @@ declare namespace repo="http://exist-db.org/xquery/repo";
declare %private variable $openapi:supported-methods := ("rest:GET", "rest:HEAD", "rest:POST", "rest:PUT", "rest:DELETE");
(:~
: Prepares a JSON document usually to be stored as "openapi.json".
: Prepares a JSON document conform to OpenAPI 3.0.2, usually to be stored as "openapi.json".
: @param $target the collection to prepare the descriptor for, e.g. “/db/apps/application”
: :)
declare function openapi:json($target as xs:string)
......@@ -34,27 +34,27 @@ as xs:string {
:)
declare function openapi:main($target as xs:string)
as map(*) {
let $modules-uris := collection($target)[ends-with(base-uri(), ".xqm")]/base-uri()
let $modules-uris := openapi:resources($target, ())[openapi:xquery-resource(.)]
let $module :=
for $module in $modules-uris
let $inspect := inspect:inspect-module($module)
where $inspect/function/annotation/string(@name) = $openapi:supported-methods
let $test4rest := contains(util:binary-doc($module) => util:base64-decode(), "%rest:")
where $test4rest
return
$inspect
inspect:inspect-module($module)[.//annotation[@name = $openapi:supported-methods]]
let $config-uri := $target || "/openapi-config.xml"
let $config := if(doc-available($config-uri))
then doc($config-uri)/*
else doc( replace(system:get-module-load-path(), '^(xmldb:exist://)?(embedded-eXist-server)?(.+)$', '$3') || "/openapi-config.xml" )/*
else doc( replace(system:get-module-load-path(), '^(xmldb:exist://)?(embedded-eXist-server)?(.+)$', '$3') || "/../openapi-config.xml" )/*
let $expath := doc($target || "/expath-pkg.xml")/*
let $repo := doc($target || "/repo.xml")/*
return
map:merge((
map{"openapi": "3.0.2"},
openapi:paths-object($module),
openapi:paths-object($module, $config),
openapi:servers-object($config/openapi:servers),
openapi:info-object($expath, $repo, $config/openapi:info),
openapi:tags-object($module)
openapi:tags-object($module, $config)
))
};
......@@ -127,12 +127,20 @@ as map(*) {
: Prepare OAS3 Paths Object.
: @see https://swagger.io/specification/#pathsObject
:)
declare %private function openapi:paths-object($module as element(module)+)
declare %private function openapi:paths-object($module as element(module)+, $config as element(openapi:config))
as map(*) {
let $paths := $module/function/annotation[@name = "rest:path"]/value => distinct-values()
return
map{
"paths":
map:merge((
$module/function[annotation/@name = "rest:path"] ! openapi:operation-object(.)
for $path in $paths
let $functions := $module/function[annotation[@name = "rest:path"]/value = $path]
return
map{
$path => replace("\{\$", "{"):
map:merge(($functions ! openapi:operation-object(., $config)))
}
))
}
};
......@@ -141,21 +149,32 @@ as map(*) {
: Prepare OAS3 Operation Object.
: @see https://swagger.io/specification/#operationObject
:)
declare %private function openapi:operation-object($function as element(function))
declare %private function openapi:operation-object($function as element(function), $config as element(openapi:config))
as map(*) {
let $desc := normalize-space($function/description)
let $name := $function/@name
let $desc := tokenize($function/description, "\n\s\n\s") ! normalize-space(.)
let $see := normalize-space($function/see)
let $deprecated := $function/deprecated
let $tags := array { $function/@name => substring-before(":") }
let $tags := array {
if($config/openapi:tags/openapi:tag/openapi:function[@name = $name])
then
if($config/openapi:tags/openapi:tag/openapi:function[@name = $name]/parent::openapi:tag/string(@method) = "exclusive")
then $config/openapi:tags/openapi:tag/openapi:function[@name = $name]/parent::openapi:tag[@method = "exclusive"]/string(@name)
else
($name => substring-before(":"),
$config/openapi:tags/openapi:tag/openapi:function[@name = $name]/parent::openapi:tag/string(@name))
else
$name => substring-before(":")
}
return
map{
$function/annotation[@name eq "rest:path"]/replace(value, "\{\$", "{") :
map:merge((
for $method in $function/annotation[@name = $openapi:supported-methods]/substring-after(lower-case(@name), "rest:")
return
map{
$method:
map:merge((
map{ "description": $desc},
map{ "summary": $desc[1]},
$desc[2] ! map{ "description": .},
map{ "tags": $tags},
$see[1] ! map{"externalDocs": $see ! map{
"url": .,
......@@ -166,7 +185,7 @@ as map(*) {
openapi:requestBody-object($function)
))
}
}
))
};
declare %private function openapi:requestBody-object($function as element(function))
......@@ -297,15 +316,19 @@ as map(*)* {
declare %private function openapi:mediaType-object($function)
as map(*) {
let $produces := (
string($function/annotation[@name="rest:produces"]),
$function/annotation[@name="rest:produces"]/value/string(),
string($function/annotation[@name="output:media-type"]),
string($function/annotation[@name="output:method"]/openapi:method-mediaType(string(.))),
"application/xml"
)
return
map{
$produces[. != ""][1]: openapi:schema-object($function/returns)
}
map:merge(
for $iii in 1 to count($produces) return
if($produces[$iii] = "") then
()
else
map:entry($produces[$iii], openapi:schema-object($function/returns))
)
};
(:~
......@@ -329,7 +352,7 @@ as map(*)? {
}
};
declare %private function openapi:tags-object($modules as element(module)+)
declare %private function openapi:tags-object($modules as element(module)+, $config as element(openapi:config))
as map(*) {
map{
"tags": array{
......@@ -338,8 +361,14 @@ as map(*) {
map{
"name": string($module/@prefix),
"description": normalize-space($module/description)
},
for $tag in $config/openapi:tags/openapi:tag
return
map{
"name": string($tag/@name),
"description": normalize-space($tag)
}
}
}
}
};
......@@ -350,7 +379,7 @@ as map(*) {
:)
declare function openapi:spdx($licenseId as xs:string)
as map(*) {
let $collection-uri := /id("restxqopenapi")/base-uri()
let $collection-uri := collection()/id("restxqopenapi")/base-uri()
let $item :=
(($collection-uri || "/../spdx-licenses.json")
=> json-doc())("licenses")?*[?licenseId = $licenseId]
......@@ -382,6 +411,51 @@ as xs:string?{
: @param $name The name of the argument to prepare an example for :)
declare %private function openapi:example($function as element(function), $name as xs:string)
as map(*)* {
string($function/annotation[@name = "test:arg"][value[1] eq $name]/value[2])
string(($function/annotation[@name = "test:arg"][value[1] eq $name])[1]/value[2])
! map{ "example": .}
};
declare %private function openapi:xquery-resource($baseuri as xs:string)
as xs:boolean {
ends-with($baseuri, ".xqm")
or ends-with($baseuri, ".xql")
or ends-with($baseuri, ".xq")
or ends-with($baseuri, ".xquery")
or ends-with($baseuri, ".xqy")
};
(:~
: Returns the names of the child resources in collection URI supplied
: @param $target URI under which child resources are to be returned :)
declare %private function openapi:resource-uris($target as xs:string)
as item()* {
xmldb:get-child-resources($target) ! ($target || '/' || .)
};
(:~
: Returns the names of the child collections in collection URI supplied
: @param $target URI under which collections are to be returned :)
declare %private function openapi:collections-uris($target as xs:string)
as item()* {
xmldb:get-child-collections($target)
};
(:~
: Recursive function to use instead of collection as implementation of this function
: varies between Fusion and eXist so in Fusion XQuery resources are not found.
: @param $target URI under which resource listing is required
: @param $uris Existing URIs to be retained and added to until completion :)
declare %private function openapi:resources($target as xs:string, $uris as xs:string*)
as item()* {
let $current-uris as xs:string* := openapi:resource-uris($target)
let $current-collections as xs:string* := openapi:collections-uris($target)
let $new-uris as xs:string* :=
if (empty($current-collections)) then ()
else
for $collection in $current-collections
let $target := $target || '/' || $collection
return openapi:resources($target, $uris)
let $uris as xs:string* := ($current-uris, $new-uris, $uris)
return
$uris
};
......@@ -6,7 +6,7 @@
spec="1.0"
xml:id="restxqopenapi">
<title>@project.title@</title>
<dependency processor="http://exist-db.org" semver-min="@project.processorversion@"/>
<dependency processor="http://exist-db.org" semver-min="4.0.0"/>
<xquery>
<namespace>https://lab.sub.uni-goettingen.de/restxqopenapi</namespace>
<file>openapi.xqm</file>
......
File moved
......@@ -10,4 +10,11 @@
<server url="http://localhost:8080/exist/restxq">Local development server</server>
<server url="https://example.com/api/v1">Production server</server>
</servers>
<tags>
<tag name="public" method="exclusive">
The public part of the API. No authentication. World-readable.
<function name="openapi-test-simple:del"/>
<function name="openapi-test-simple:get"/>
</tag>
</tags>
</config>
......@@ -6,17 +6,22 @@ import module namespace openapi="https://lab.sub.uni-goettingen.de/restxqopenapi
at "content/openapi.xqm";
(: prepare a json document on HTTP requests :)
declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";
declare namespace output="http://www.w3.org/2010/xslt-xquery-serialization";
declare option output:method "json";
declare option output:media-type "application/json";
let $thisPath := replace(system:get-module-load-path(),
'^(xmldb:exist://)?(embedded-eXist-server)?(.+)$', '$3')
(: we register the REST interface :)
let $prepare := xmldb:get-child-resources("/db/apps/openapi/content")[. != "openapi.xqm"]
! exrest:register-module(xs:anyURI("/db/apps/openapi/content/" || .))
let $prepare :=
if(xs:boolean(request:get-parameter("register", "false")))
then
xmldb:get-child-resources($thisPath || "/content")[. != "openapi.xqm"]
! exrest:register-module(xs:anyURI($thisPath || "/content/" || .))
else ()
(: locate the target path :)
let $thisPath := replace(system:get-module-load-path(),
'^(xmldb:exist://)?(embedded-eXist-server)?(.+)$', '$3')
let $target := request:get-parameter("target", $thisPath)
return
......
......@@ -8,21 +8,106 @@
<type>application</type>
<target>openapi</target>
<changelog>
<change version="1.7.0">
<ul xmlns="http://www.w3.org/1999/xhtml">
<li class="feat">Features
<ul style="margin-left: 15px;">
<li>add compatibility to <a href="http://fusiondb.com">fusiondb</a></li>
<li>move ci test stage to service container</li>
</ul>
</li>
</ul>
</change>
<change version="1.6.0">
<ul xmlns="http://www.w3.org/1999/xhtml">
<li class="bugs">Bugfix
<ul style="margin-left: 15px;">
<li>fix incorrect array parsing of %rest:produces</li>
</ul>
</li>
</ul>
</change>
<change version="1.5.2">
<ul xmlns="http://www.w3.org/1999/xhtml">
<li class="bugs">Bugfix
<ul style="margin-left: 15px;">
<li>omit “null” on description</li>
</ul>
</li>
</ul>
</change>
<change version="1.5.1">
<ul xmlns="http://www.w3.org/1999/xhtml">
<li class="bugs">Bugfix
<ul style="margin-left: 15px;">
<li>bugfix for string() call with multiple test annotations</li>
</ul>
</li>
</ul>
</change>
<change version="1.5.0">
<ul xmlns="http://www.w3.org/1999/xhtml">
<li class="feat">Features
<ul style="margin-left: 15px;">
<li>customized tags via config file</li>
</ul>
</li>
</ul>
</change>
<change version="1.4.0">
<ul xmlns="http://www.w3.org/1999/xhtml">
<li class="feat">Features
<ul style="margin-left: 15px;">
<li>Support multiple method annotations</li>
<li>Support a single path mapped to different functions</li>
</ul>
</li>
</ul>
</change>
<change version="1.3.0">
<ul xmlns="http://www.w3.org/1999/xhtml">
<li class="feat">Features
<ul style="margin-left: 15px;">
<li>increase performance on complex applications by reduce calling inspect:inspect-module#1</li>
</ul>
</li>
<li class="bugs">Bugfixes
<ul style="margin-left: 15px;">
<li>fix call for fallback config file</li>
</ul>
</li>
</ul>
</change>
<change version="1.2.0">
<ul xmlns="http://www.w3.org/1999/xhtml">
<li class="feat">Features
<ul style="margin-left: 15px;">
<li>Add a parameter for re-registration of RestXQ functions</li>
</ul>
</li>
</ul>
</change>
<change version="1.1.1">
<ul xmlns="http://www.w3.org/1999/xhtml">
<li class="feat">Features
<ul style="margin-left: 15px;">
<li>add a pareser for cookie and header parameter</li>
<li>refine test modules</li>
<li>update SPDX</li>
</ul>
</li>
</ul>
</change>
<change version="1.0.0">
<ul xmlns="http://www.w3.org/1999/xhtml">
<li class="feat">Features
<ul>
<ul style="margin-left: 15px;">
<li>Prepares OpenAPI 3.0.2 documentation file (json)</li>
<li>Parse <a href="http://exquery.github.io/exquery/exquery-restxq-specification/restxq-1.0-specification.html">RESTXQ</a></li>
<li>Parse <a href="http://xqdoc.org/xqdoc_comments_doc.html">xqDocs</a></li>
<li>Parse <a href="http://xqdoc.org/xqdoc_comments_doc.html">xqDoc</a></li>
<li>Parse <a href="http://exist-db.org/exist/apps/doc/xqsuite.xml">test annotations</a></li>
</ul>
</li>
<li class="bugs">Bugfixes
<ul>
<li></li>
</ul>
</li>
</ul>
</change>
</changelog>
......
This diff is collapsed.