Skip to content

Commit

Permalink
Enable xqerl REST db interactions issue #53 rest db (#58)
Browse files Browse the repository at this point in the history
* rest-db main modules
* cowboy rest db handler and xQuery main modules. The rest /db/ request endpoint
enables POST, GET, PUT, DELETE requests to create, retrieve, update and delete
xqerl database items. A GET on a DB collection will list database items. Checks
are added to via feat.yml
* add swtch on type, any, item or collection
* add rest db checks
* get can retrieve resources or collections
* feat: return collections as text,xml or json
* ci:(check) can list as text or json
* refactor: add get_uri func
* ci: comment out rm collection section
* ci: rest api check unsupported media type
* feat:only create if looks like a domain
* ci: rest api checks
* rm db-list, now in cd-retrieve
* ci:(check) add HEAD check
* ci:(check) HEAD also for collection lists
  • Loading branch information
grantmacken authored Feb 27, 2022
1 parent 6032ce6 commit 26f95c6
Show file tree
Hide file tree
Showing 11 changed files with 1,512 additions and 7 deletions.
507 changes: 507 additions & 0 deletions .github/workflows/feat.yml

Large diffs are not rendered by default.

563 changes: 563 additions & 0 deletions .github/workflows/rest-db-checks.yml

Large diffs are not rendered by default.

30 changes: 30 additions & 0 deletions priv/modules/db_available.xq
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
declare variable $uri external;
declare variable $type external;
(: return true only
if a db item item is in a collection'
or a collection contains items
:)
try {

let $isItem := function( $uri ) {
let $col := remove($uri => tokenize('/'), $uri => tokenize('/') => count()) => string-join('/')
let $uriCollection := $col => uri-collection()
return $uri = ($uriCollection)
}

let $isCollection := function( $uri ) {
($uri => uri-collection() => count()) gt 0
}

return
switch ( $type )
case "collection" return $isCollection($uri)
case "item" return $isItem($uri)
case "any" return (
if ( $isItem($uri) )
then ( true() )
else ($isCollection($uri))
)
default return ( false() )
} catch * {false()}

23 changes: 23 additions & 0 deletions priv/modules/db_create.xq
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
declare variable $data external;
declare variable $uri external;
declare variable $type external;
declare variable $dbIO := QName("ERR", 'dbIO');
try {
let $item :=
switch ( $type )
case "application/xml" return ( $data => parse-xml() )
case "application/json" return( $data => parse-json() )
default return error( $dbIO,"Fail: can not handle content type")

let $looksLikeDomain := function($uri){
($uri => tokenize('/'))[3] => contains('.')
}

return
if ( $looksLikeDomain($uri) ) then
if ( $item instance of document-node() ) then (true(), $item => db:put($uri) )
else if ( $item instance of map(*) ) then (true(), $item => db:put($uri))
else if ( $item instance of array(*) ) then (true(), $item => db:put($uri))
else false()
else false()
} catch * { false() }
18 changes: 18 additions & 0 deletions priv/modules/db_delete.xq
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
declare variable $uri external;
try {
let $isItem := function( $uri ) {
try {
let $col := remove($uri => tokenize('/'), $uri => tokenize('/') => count()) => string-join('/')
let $uriCollection := $col => uri-collection()
return $uri = ($uriCollection)
} catch * {false()}
}
return
if ( $uri => $isItem() ) then (true(), db:delete($uri))
else false()
} catch * { false() }

(:
for $item in ( $uri => uri-collection() )
return ( $item => db:delete(), ' - deleted:' || $item )
:)
74 changes: 74 additions & 0 deletions priv/modules/db_retrieve.xq
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
declare variable $uri external;
declare variable $accept external;
declare variable $base external;
declare variable $dbIO := QName("ERR", 'dbIO');
try {
let $nodeKind := function( $node as node() ) as xs:string {
if ($node instance of element()) then 'element'
else if ($node instance of attribute()) then 'attribute'
else if ($node instance of text()) then 'text'
else if ($node instance of document-node()) then 'document-node'
else if ($node instance of comment()) then 'comment'
else if ($node instance of processing-instruction()) then 'processing-instruction'
(: should include namespace? :)
else ( 'unknown node')
}

let $funcType := function( $item as item()) as xs:string {
if ($item instance of map(*)) then 'map'
else if ($item instance of array(*)) then 'array'
else 'function'
}

let $itemType := function( $item as item() ) as xs:string {
if ( $item instance of node() ) then $item => $nodeKind()
else if ( $item instance of function(*) ) then $item => $funcType()
else if ( $item instance of xs:anyAtomicType ) then 'atomic'
else ( 'unknown instance' )
}

let $isItem := function( $uri ) {
try {
let $col := remove($uri => tokenize('/'), $uri => tokenize('/') => count()) => string-join('/')
let $uriCollection := $col => uri-collection()
return $uri = ($uriCollection)
} catch * {false()}
}

return (
if ( $uri => $isItem())
then
let $dbItem := $uri => db:get()
let $dbItemType := $dbItem => $itemType()
return
switch ( $dbItemType )
case 'document-node' return
switch ( $accept )
case "application/xml" return $dbItem => serialize(map{'method': 'xml','omit-xml-declaration': true() })
case "application/json" return $dbItem => serialize(map{'method': 'json'})
default return error( $dbIO, ``[ [ `{ $accept }` ] can not serialize ]``)
case 'array' return
switch ( $accept )
case "application/json" return $dbItem => serialize(map{'method': 'json'})
default return error( $dbIO, ``[ [ `{ $accept }` ] can not serialize ]``)
case 'map' return
switch ( $accept )
case "application/json" return $dbItem => serialize(map{'method': 'json'})
default return error( $dbIO, ``[ [ `{ $accept }` ] can not serialize ]``)
default return error( $dbIO, ``[ [ `{ $dbItemType }` ] can not handle this db item type ]``)
else (
let $resources := ($uri => uri-collection()) ! replace( . , 'http://', $base )
return
switch ( $accept )
case "application/xml" return (element resource {$resources ! element resource {.}}) => serialize()
case "application/json" return (array{ for $res in $resources return $res }) => serialize(map{'method': 'json'})
default return $resources => string-join('
')
)
)
} catch * {
``[
ERROR!
module: `{ $err:module => substring-after('/modules/')}`
`{$err:code}`: `{$err:description}`
]``
}
14 changes: 14 additions & 0 deletions priv/modules/db_update.xq
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
declare variable $data external;
declare variable $uri external;
declare variable $type external;

let $item :=
switch ( $type )
case "application/xml" return ( $data => parse-xml() )
case "application/json" return( $data => parse-json() )
default return ( false() )
return
if ( $item instance of document-node() ) then (true(), $item => db:put($uri) )
else if ( $item instance of map(*) ) then (true(), $item => db:put($uri))
else if ( $item instance of array(*) ) then (true(), $item => db:put($uri))
else false()
3 changes: 2 additions & 1 deletion src/xqerl_code_server.erl
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,8 @@ init_rest(DispatchFileName) ->
[
xqerl_restxq:endpoint_sort(Paths),
{"/assets/[...]", cowboy_static, {priv_dir, xqerl, "static/assets"}},
{"/xqerl", xqerl_handler_greeter, #{}}
{"/xqerl", xqerl_handler_greeter, #{}},
{"/db/:domain/[...]", xqerl_handler_rest_db, #{}}
]
),
Dispatch = cowboy_router:compile([{'_', Routes}]),
Expand Down
Loading

0 comments on commit 26f95c6

Please sign in to comment.