A common way to offer services on the internet is via REST. The BaseX XML Database and XQuery Processor has XML super powers. It is your task to make (some of those) facilities easily available as a RESTful service.
During this assignment we will provide an initial Storage service:
Following is the underlying specification of the REST service: REST API - BaseX XML Information Service (XIS) (can be loaded into swagger.io for a nice visual repesentation).
RESTXQ (slides) is the technique that should be used to implement the REST service according to the specification.
Here is some sample output of a sequence of REST calls to the service. It can help you to see what kind of results are expected and compare the results of your service while implementing it. cURL was used to initiate the REST calls (via this script).
1. Create a REST service using RESTXQ.
2. In essence what you have to do is
Following is an example: A file upload service is offered when POSTing to URL /upload. It leverages the XQuery File Module inside an XQuery function enriched with RESTXQ annotations.
3. Responses via RESTXQ
( <rest:response> <http:response status="500"> <http:header name="Content-Language" value="en"/> <http:header name="Content-Type" value="text/plain; charset=utf-8"/> </http:response> <output:serialization-parameters> <output:media-type value="text/plain"/> </output:serialization-parameters> </rest:response>, "Upps, we are doomed (Internal server error)." )
declare %updating %rest:DELETE %rest:path("/api/v1/db/{$dbname}") function xis:db-delete(...)wrap responses in db:output() to overcome XQUP constraints.
(:~ : XIS - XML Information Service :) module namespace xis = 'http://basex.org/xis'; declare variable $xis:REST-API-DESC as xs:string+ := file:read-text("doc/rest-api/xis-rest-api.yaml"); (: ------ REST/HTTP responses --- :) declare %private function xis:response-200( $src as xs:string, $dbname as xs:string ) as item()+ { xis:response-200($src, $dbname, '') }; declare %private function xis:response-200( $src as xs:string, $dbname as xs:string, $docname as xs:string ) as item()+ { let $msg := switch($src) case 'db-create' return 'Datastore (' || $dbname || ') created.' case 'db-drop' return 'Datastore (' || $dbname || ') deleted.' case 'db-add' return 'Document (' || $docname || ') added to datastore (' || $dbname || ').' case 'db-delete' return 'Document (' || $docname || ') deleted from datastore (' || $dbname || ').' default return $dbname || ' ' || $docname return xis:response-text(200, $msg) }; (: Constructs REST response containing 404 HTTP response and a text message. :) declare %private function xis:response-404( $dbname as xs:string ) as item()+ { xis:response-text(404, 'Datastore (' || $dbname || ') not found.') }; declare %private function xis:response-404( $dbname as xs:string, $docname as xs:string ) as item()+ { xis:response-text(404, 'Document (' || $docname || ') not found in datastore (' || $dbname || ').') }; declare %private function xis:response-409( $dbname as xs:string ) as item()+ { xis:response-text(409, 'Datastore (' || $dbname || ') already exists.') }; declare %private function xis:response-409( $dbname as xs:string, $docname as xs:string ) as xs:string { xis:response-text(409, 'Document (' || $docname || ') already exists in datastore (' || $dbname || ').') }; declare %private function xis:response-text( $status as xs:integer, $msg as xs:string ) as item()+ { ( <rest:response> <http:response status="{ $status }"> <http:header name="Content-Language" value="en"/> <http:header name="Content-Type" value="text/plain; charset=utf-8"/> </http:response> <output:serialization-parameters> <output:media-type value='text/plain'/> </output:serialization-parameters> </rest:response>, $msg ) }; (: -------------------- Basic information services -------------------------- :) (:~ : This function generates the welcome page. : @return HTML page :) declare %rest:GET %rest:path("/") function xis:landing-page() as element(Q{http://www.w3.org/1999/xhtml}html) { let $title := 'XIS – XML Information Service' return <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>{ $title }</title> <link rel="stylesheet" type="text/css" href="static/style.css"/> </head> <body> <div class="right"><img src="static/basex.svg" width="96"/></div> <h2>{ $title }</h2> <ul> <li><a href="api/v1">XIS REST API</a></li> <li><a href="http://phobos103.inf.uni-konstanz.de/xml15/tutorial/xis/test/curl/curl.txt">cURL log</a></li> </ul> </body> </html> }; declare %rest:GET %rest:path("/api/v1") %output:media-type("text/plain") function xis:rest-api-description() as xs:string+ { $xis:REST-API-DESC }; (: ------------------------- Database services ------------------------------ :) declare %rest:GET %rest:path("/api/v1/db") %output:media-type("text/plain") function xis:create-db-info() as xs:string+ { $xis:REST-API-DESC }; declare %updating %rest:POST %rest:path("/api/v1/db/{$dbname}") function xis:db-post( $dbname as xs:string ) { if (db:exists($dbname)) then (: Return 409 (CONFLICT) and text message. :) db:output(xis:response-409($dbname)) else ( db:create($dbname), db:output(xis:response-200('db-create', $dbname)) ) }; declare %rest:GET %rest:path("/api/v1/db/{$dbname}") function xis:db-get( $dbname as xs:string ) { if (db:exists($dbname)) then element datastore { attribute name { $dbname }, db:list($dbname) ! element document { attribute name { . } } } else xis:response-404($dbname) }; declare %updating %rest:DELETE %rest:path("/api/v1/db/{$dbname}") function xis:db-delete( $dbname as xs:string ) { if (db:exists($dbname)) then ( db:drop($dbname), db:output(xis:response-200('db-drop', $dbname)) ) else db:output(xis:response-404($dbname)) }; (: ----------------- Database / Document services ------------------------------ :) declare %updating %rest:POST('{$content}') %rest:path("/api/v1/db/{$dbname}/{$docname}") function xis:db-doc-post( $dbname as xs:string, $docname as xs:string, $content as document-node() ) { if (db:exists($dbname)) then if (db:exists($dbname, $docname)) then db:output(xis:response-409($dbname, $docname)) else ( db:add($dbname, $content, $docname), db:output(xis:response-200('db-add', $dbname, $docname)) ) else db:output(xis:response-404($dbname)) }; declare %updating %rest:DELETE %rest:path("/api/v1/db/{$dbname}/{$docname}") function xis:db-doc-delete( $dbname as xs:string, $docname as xs:string ) { if (db:exists($dbname)) then if (db:exists($dbname, $docname)) then ( db:delete($dbname, $docname), db:output(xis:response-200('db-delete', $dbname, $docname)) ) else db:output(xis:response-404($docname, $dbname)) else db:output(xis:response-404($dbname)) }; declare %rest:GET %rest:path("/api/v1/db/{$dbname}/{$docname}") function xis:db-doc-get( $dbname as xs:string, $docname as xs:string ) { if (db:exists($dbname)) then if (db:exists($dbname, $docname)) then doc(concat($dbname, '/', $docname)) else xis:response-404($docname, $dbname) else xis:response-404($dbname) };