based stored queries now check to see if required element is present before trying to filter by it

(:~
: This is a module contatining the required stored queries for Care Services Discovery
: @version 1.0
: @see https://github.com/his-interop/openinfoman @see http://ihe.net
:
:)
module namespace csd_bsq = "https://github.com/his-interop/openinfoman/csd_bsq";

import module namespace csd = "urn:ihe:iti:csd:2013" at "csd_base_library.xqm";

declare default element  namespace   "urn:ihe:iti:csd:2013";

declare variable $csd_bsq:stored_functions :=
(
   <function uuid='4e8bbeb9-f5f5-11e2-b778-0800200c9a66'
   	     method='csd_bsq:provider_search'
 	     content-type='text/xml; charset=utf-8'
	     />,
   <function uuid='dc6aedf0-f609-11e2-b778-0800200c9a66'
   	     method='csd_bsq:organization_search'
 	     content-type='text/xml; charset=utf-8'
	     />,
   <function uuid='e3d8ecd0-f605-11e2-b778-0800200c9a66'
   	     method='csd_bsq:service_search'
 	     content-type='text/xml; charset=utf-8'
	     />,
   <function uuid='c7640530-f600-11e2-b778-0800200c9a66'
   	     method='csd_bsq:facility_search'
 	     content-type='text/xml; charset=utf-8'
	     />
);

declare function csd_bsq:provider_search($requestParams, $doc) as element()
{
<CSD xmlns:csd="urn:ihe:iti:csd:2013"  >
  <organizationDirectory/>
  <serviceDirectory/>
  <facilityDirectory/>
  <providerDirectory>
    {

      let $provs0 := if (exists($requestParams/id))
	then csd:filter_by_primary_id($doc/CSD/providerDirectory/*,$requestParams/id)
      else $doc/CSD/providerDirectory/*

      let $provs1 := if(exists($requestParams/otherID))
	then csd:filter_by_other_id($provs0,$requestParams/otherID)
      else $provs0

      let $provs2 := if(exists($requestParams/commonName))
	then csd:filter_by_common_name($provs1,$requestParams/commonName)
      else $provs1

      let $provs3 := if (exists($requestParams/codedType))
	then csd:filter_by_coded_type($provs2,$requestParams/codedType)
      else $provs2

      let $provs4 := if (exists($requestParams/address/addressLine))
	then csd:filter_by_address($provs3, $requestParams/address/addressLine)
      else $provs3

      let $provs5 :=  if (exists($requestParams/record))
	then csd:filter_by_record($provs4,$requestParams/record)
      else  $provs4

      return if (exists($requestParams/start)) then
	if (exists($requestParams/max))
	  then csd:limit_items($provs5,$requestParams/start,$requestParams/max)
	else csd:limit_items($provs5,$requestParams/start,<max/>)
      else
	if (exists($requestParams/max))
	  then csd:limit_items($provs5,<start/>,$requestParams/max)
	else $provs5

    }
  </providerDirectory>
</CSD>

};

declare function csd_bsq:organization_search($requestParams, $doc) as element()
{
<CSD xmlns:csd="urn:ihe:iti:csd:2013"  >
  <organizationDirectory>
    {
      let $orgs0 := if (exists($requestParams/id))
	then csd:filter_by_primary_id($doc/CSD/organizationDirectory/*,$requestParams/id)
	else $doc/CSD/organizationDirectory/*

      let $orgs1 := if (exists($requestParams/primaryName))
	then csd:filter_by_primary_name($orgs0,$requestParams/primaryName)
      else $orgs0

      let $orgs2 := if(exists($requestParams/name))
	then csd:filter_by_name($orgs1,$requestParams/name)
      else $orgs1

      let $orgs3 := if(exists($requestParams/codedType))
	then csd:filter_by_coded_type($orgs2,$requestParams/codedType)
	else $orgs2

      let $orgs4 :=if (exists($requestParams/address/addressLine))
	then csd:filter_by_address($orgs3, $requestParams/address/addressLine)
	else $orgs3

      let $orgs5 := if (exists($requestParams/record))
	then csd:filter_by_record($orgs4,$requestParams/record)
      else $orgs4

      let $orgs6 := if (exists($requestParams/otherID))
	then csd:filter_by_other_id($orgs5,$requestParams/otherID)
      else $orgs5

      return if (exists($requestParams/start)) then
	if (exists($requestParams/max))
	  then csd:limit_items($orgs6,$requestParams/start,$requestParams/max)
	else csd:limit_items($orgs6,$requestParams/start,<max/>)
      else
	if (exists($requestParams/max))
	  then csd:limit_items($orgs6,<start/>,$requestParams/max)
	else $orgs6

    }
  </organizationDirectory>
  <serviceDirectory/>
  <facilityDirectory/>
  <providerDirectory/>
</CSD>
};

declare function csd_bsq:facility_search($requestParams, $doc) as element()
{
<CSD xmlns:csd="urn:ihe:iti:csd:2013"  >
  <organizationDirectory/>
  <serviceDirectory/>
  <facilityDirectory>
    {
      let $facs0 := if (exists($requestParams/id))
	then csd:filter_by_primary_id($doc/CSD/facilityDirectory/*,$requestParams/id)
      else $doc/CSD/facilityDirectory/*

      let $facs1 := if (exists($requestParams/primaryName))
	then csd:filter_by_primary_name($facs0,$requestParams/primaryName)
      else $facs0

      let $facs2 := if (exists($requestParams/name))
	then csd:filter_by_name($facs1,$requestParams/name)
      else $facs1

      let $facs3 := if(exists($requestParams/codedType))
	then csd:filter_by_coded_type($facs2,$requestParams/codedType)
      else $facs2

      let $facs4 := if(exists($requestParams/address/addressLine))
	then csd:filter_by_address($facs3, $requestParams/address/addressLine)
      else $facs3

      let $facs5 := if (exists($requestParams/record))
	then csd:filter_by_record($facs4,$requestParams/record)
      else $facs4

      let $facs6 := if(exists($requestParams/otherID))
	then csd:filter_by_other_id($facs5,$requestParams/otherID)
      else $facs5

      return if (exists($requestParams/start)) then
	if (exists($requestParams/max))
	  then csd:limit_items($facs6,$requestParams/start,$requestParams/max)
	else csd:limit_items($facs6,$requestParams/start,<max/>)
      else
	if (exists($requestParams/max))
	  then csd:limit_items($facs6,<start/>,$requestParams/max)
	else $facs6

    }
  </facilityDirectory>
  <providerDirectory/>
</CSD>
};

declare function csd_bsq:service_search($requestParams, $doc) as element()
{
<CSD xmlns:csd="urn:ihe:iti:csd:2013"  >
  <organizationDirectory/>
  <serviceDirectory>
    {
      let $svcs0 := if (exists($requestParams/id))
	then csd:filter_by_primary_id($doc/CSD/serviceDirectory/*,$requestParams/id)
      else $doc/CSD/serviceDirectory/*

      let $svcs1 := if(exists($requestParams/codedType))
	then csd:filter_by_coded_type($svcs0,$requestParams/codedType)
      else $svcs0

      let $svcs2 :=  if (exists($requestParams/record))
	then csd:filter_by_record($svcs1,$requestParams/record)
      else $svcs1

      return if (exists($requestParams/start)) then
	if (exists($requestParams/max))
	  then csd:limit_items(svcs2,$requestParams/start,$requestParams/max)
	else csd:limit_items(svcs2,$requestParams/start,<max/>)
      else
	if (exists($requestParams/max))
	  then csd:limit_items(svcs2,<start/>,$requestParams/max)
	else svcs2

    }
  </serviceDirectory>
  <facilityDirectory/>
  <providerDirectory/>
</CSD>

};

(:~
: This is a module contatining the required stored queries for Care Services Discovery
: @version 1.0
: @see https://github.com/his-interop/openinfoman @see http://ihe.net
:
:)
module namespace csd_bsq = "https://github.com/his-interop/openinfoman/csd_bsq";

import module namespace csd = "urn:ihe:iti:csd:2013" at "csd_base_library.xqm";

declare default element  namespace   "urn:ihe:iti:csd:2013";

declare variable $csd_bsq:stored_functions :=
(
   <function uuid='4e8bbeb9-f5f5-11e2-b778-0800200c9a66'
   	     method='csd_bsq:provider_search'
 	     content-type='text/xml; charset=utf-8'
	     />,
   <function uuid='dc6aedf0-f609-11e2-b778-0800200c9a66'
   	     method='csd_bsq:organization_search'
 	     content-type='text/xml; charset=utf-8'
	     />,
   <function uuid='e3d8ecd0-f605-11e2-b778-0800200c9a66'
   	     method='csd_bsq:service_search'
 	     content-type='text/xml; charset=utf-8'
	     />,
   <function uuid='c7640530-f600-11e2-b778-0800200c9a66'
   	     method='csd_bsq:facility_search'
 	     content-type='text/xml; charset=utf-8'
	     />
);

declare function csd_bsq:provider_search($requestParams, $doc) as element()
{
<CSD xmlns:csd="urn:ihe:iti:csd:2013"  >
  <organizationDirectory/>
  <serviceDirectory/>
  <facilityDirectory/>
  <providerDirectory>
    {

      let $provs0 := csd:filter_by_primary_id($doc/CSD/providerDirectory/*,$requestParams/id)

      let $provs1 := csd:filter_by_other_id($provs0,$requestParams/otherID)

      let $provs2 := csd:filter_by_common_name($provs1,$requestParams/commonName)

      let $provs3 := csd:filter_by_coded_type($provs2,$requestParams/codedType)

      let $provs4 := csd:filter_by_address($provs3, $requestParams/address/addressLine)

      let $provs5 :=  csd:filter_by_record($provs4,$requestParams/record)

      return csd:limit_items($provs5,$requestParams/start,$requestParams/max)
    }
  </providerDirectory>
</CSD>

};

declare function csd_bsq:organization_search($requestParams, $doc) as element()
{
<CSD xmlns:csd="urn:ihe:iti:csd:2013"  >
  <organizationDirectory>
    {
      let $orgs0 := csd:filter_by_primary_id($doc/CSD/organizationDirectory/*,$requestParams/id)

      let $orgs1 := csd:filter_by_primary_name($orgs0,$requestParams/primaryName)

      let $orgs2 := csd:filter_by_name($orgs1,$requestParams/name)

      let $orgs3 := csd:filter_by_coded_type($orgs2,$requestParams/codedType)

      let $orgs4 := csd:filter_by_address($orgs3, $requestParams/address/addressLine)

      let $orgs5 := csd:filter_by_record($orgs4,$requestParams/record)

      let $orgs6 := csd:filter_by_other_id($orgs5,$requestParams/otherID)

      return csd:limit_items($orgs6,$requestParams/start,$requestParams/max)

    }
  </organizationDirectory>
  <serviceDirectory/>
  <facilityDirectory/>
  <providerDirectory/>
</CSD>
};

declare function csd_bsq:facility_search($requestParams, $doc) as element()
{
<CSD xmlns:csd="urn:ihe:iti:csd:2013"  >
  <organizationDirectory/>
  <serviceDirectory/>
  <facilityDirectory>
    {
      let $facs0 := csd:filter_by_primary_id($doc/CSD/facilityDirectory/*,$requestParams/id)

      let $facs1 := csd:filter_by_primary_name($facs0,$requestParams/primaryName)

      let $facs2 := csd:filter_by_name($facs1,$requestParams/name)

      let $facs3 := csd:filter_by_coded_type($facs2,$requestParams/codedType)

      let $facs4 := csd:filter_by_address($facs3, $requestParams/address/addressLine)

      let $facs5 := csd:filter_by_record($facs4,$requestParams/record)

      let $facs6 := csd:filter_by_other_id($facs5,$requestParams/otherID)

      return csd:limit_items($facs6,$requestParams/start,$requestParams/max)

    }
  </facilityDirectory>
  <providerDirectory/>
</CSD>
};

declare function csd_bsq:service_search($requestParams, $doc) as element()
{
<CSD xmlns:csd="urn:ihe:iti:csd:2013"  >
  <organizationDirectory/>
  <serviceDirectory>
    {
      let $svcs0 := csd:filter_by_primary_id($doc/CSD/serviceDirectory/*,$requestParams/id)

      let $svcs1 := csd:filter_by_coded_type($svcs0,$requestParams/codedType)

      let $svcs2 :=  csd:filter_by_record($svcs1,$requestParams/record)

      return csd:limit_items($svcs2,$requestParams/start,$requestParams/max)
    }
  </serviceDirectory>
  <facilityDirectory/>
  <providerDirectory/>
</CSD>

};


in CSR processor be a bit more strict when processing updating

(:~
: This is the Care Services Discovery RESTful document processor
: @version 1.0
: @see https://github.com/openhie/openinfoman
:
:)
module namespace csr_proc = "https://github.com/openhie/openinfoman/csr_proc";

import module namespace csd_dm = "https://github.com/openhie/openinfoman/csd_dm";
import module namespace csd_mcs = "https://github.com/openhie/openinfoman/csd_mcs";

declare   namespace   csd = "urn:ihe:iti:csd:2013";
declare namespace xquery = "http://basex.org/modules/xquery";

(:this should proabably be moved to the database :)
declare variable $csr_proc:stored_query_dir := concat(file:current-dir() , "../resources/stored_query_definitions");
declare variable $csr_proc:stored_updating_query_dir := concat(file:current-dir() , "../resources/stored_updating_query_definitions");

declare variable $csr_proc:stored_functions_doc := 'stored_functions.xml';
declare variable $csr_proc:stored_updating_functions_doc := 'stored_updating_functions.xml';

declare function csr_proc:stored_functions($db) {
 db:open($db,$csr_proc:stored_functions_doc)/careServicesFunctions/csd:careServicesFunction
};

declare function csr_proc:stored_updating_functions($db) {
 db:open($db,$csr_proc:stored_updating_functions_doc)/careServicesFunctions/csd:careServicesFunction
};

declare updating function csr_proc:init($db) {
  (
    if (not (db:is-xml($db,$csr_proc:stored_updating_functions_doc))) then
      db:add($db, <careServicesFunctions/>,$csr_proc:stored_updating_functions_doc)
    else
      ()
      ,
    if (not (db:is-xml($db,$csr_proc:stored_functions_doc))) then
      db:add($db, <careServicesFunctions/>,$csr_proc:stored_functions_doc)
    else
      ()
  )
};

declare updating function csr_proc:clear_stored_functions($db) {
  (
    db:delete($db,$csr_proc:stored_functions_doc)
    ,db:delete($db,$csr_proc:stored_updating_functions_doc)
    ,csr_proc:init($db)
    )

};

declare updating function csr_proc:load_functions_from_files($db) {
  (
    if (file:is-dir($csr_proc:stored_query_dir)) then
      for $file in file:list($csr_proc:stored_query_dir,boolean('false'),"*.xml")
      let $func := doc(concat($csr_proc:stored_query_dir,'/',$file))/csd:careServicesFunction
      return if (exists($func)) then csr_proc:load_stored_function($db,$func) else ()
    else()
      ,
    if (file:is-dir($csr_proc:stored_updating_query_dir)) then
      for $file in file:list($csr_proc:stored_updating_query_dir,boolean('false'),"*.xml")
      let $func := doc(concat($csr_proc:stored_updating_query_dir,'/',$file))/csd:careServicesFunction
      return if (exists($func)) then csr_proc:load_stored_updating_function($db,$func) else ()
    else ()
  )
};

declare updating function csr_proc:delete_stored_function($db,$urn) {
  let $stored_updating_functions := db:open($db,$csr_proc:stored_updating_functions_doc)/careServicesFunctions/*
  let $stored_functions := db:open($db,$csr_proc:stored_functions_doc)/careServicesFunctions/*
  let $functions := ($stored_functions,$stored_updating_functions)

  let $old := $functions[@urn = $urn]
  return if ($old) then delete node $old else ()

};

declare updating function csr_proc:load_stored_updating_function($db,$func) {
  let $stored_updating_functions := db:open($db,$csr_proc:stored_updating_functions_doc)/careServicesFunctions
  let $urn := $func/@urn
  let $old := $stored_updating_functions/csd:careServicesFunction[@urn = $urn]
  return
    if (exists($urn)) then
      if (exists($old))
	then
	(delete node $old,
	insert node $func into $stored_updating_functions
	)
      else
	insert node $func into $stored_updating_functions
  else ()
};

declare updating function csr_proc:load_stored_function($db,$func) {
  let $stored_functions := db:open($db,$csr_proc:stored_functions_doc)/careServicesFunctions
  let $urn := $func/@urn
  let $old := $stored_functions/csd:careServicesFunction[@urn = $urn]
  return
    if (exists($urn) and exists($stored_functions)) then
      if (exists($old))
	then
	(delete node $old,
	insert node $func into $stored_functions
	)
      else
	insert node $func into $stored_functions
    else ()
};

declare function csr_proc:process_CSR($db,$careServicesRequest, $doc_name,$base_url)
{
csr_proc:process_CSR($db,$careServicesRequest, $doc_name, $base_url,map:merge(()))
};

declare function csr_proc:process_CSR($db,$careServicesRequest, $doc_name,$base_url,$bindings as map(*))
{
let $function :=$careServicesRequest/csd:function
let $adhoc :=$careServicesRequest/csd:expression
return if (exists($function))
then
  let $urn := string($function/@urn)
  return
    if ($urn = 'urn:ihe:iti:csd:2014:adhoc')
    then
      csr_proc:process_CSR_adhoc($db,$careServicesRequest/csd:function,$doc_name,$bindings)
    else
      let $csr :=
      <csd:careServicesRequest>
	<csd:function urn="{$urn}" resource='{$doc_name}' base_url='{$base_url}'>
	  {($function/*)[1]}
	</csd:function>
      </csd:careServicesRequest>
      return csr_proc:process_CSR_stored($db,$csr,$bindings)
else if (exists($adhoc))
then
  csr_proc:process_CSR_adhoc($db,$adhoc,$doc_name,$bindings)
else
 (:
  <rest:response>
    <http:response status="400" message="Invalid care services request.">
      <http:header name="Content-Language" value="en"/>
      <http:header name="Content-Type" value="text/html; charset=utf-8"/>
    </http:response>
  </rest:response>
  :)
  $careServicesRequest

};

declare function csr_proc:process_CSR_adhoc($db,$expression,$doc_name) {
   csr_proc:process_CSR_adhoc($expression,$doc_name,map:merge(()))
};

declare function csr_proc:process_CSR_adhoc($db,$expression,$doc_name,$bindings as map(*))
{

let $doc :=  csd_dm:open_document($db,$doc_name)

let $expr :=string($expression)
return if ($expr) then
  let $result := xquery:eval($expr,map{"":$doc},$bindings)
  return(
   <rest:response>
   <http:response status="200" >
      <http:header name="Content-Type" value="{$expression/@content-type}"/>
    </http:response>
  </rest:response>
  ,$result
 )
else
   <rest:response>
     <http:response status="400" message="No ad-hoc expression" />
  </rest:response>
};

declare function csr_proc:process_CSR_stored($db,$careServicesRequest)
{
  csr_proc:process_CSR_stored($db,$careServicesRequest,map:merge(()))
};

declare function csr_proc:process_CSR_stored($db,$careServicesRequest,$bindings as map(*))
{
let $function :=$careServicesRequest/csd:function
let $stored_functions := csr_proc:stored_functions($db)
let $urn := string($function/@urn)
let $definition := ($stored_functions[@urn = $urn])[1]/csd:definition/text()
let $content_type := csr_proc:lookup_stored_content_type($db,$function/@urn)
let $doc_name := string($function/@resource)
let $doc :=  csd_dm:open_document($db,$doc_name)

let $results0 := csr_proc:process_CSR_stored_results($db,$doc,$careServicesRequest,$bindings)

return if (exists($definition)) then
  let $results1:=
    if ($function/@encapsulated) then
      csr_proc:wrap_result($results0,$content_type)
    else $results0
  return
    (
    <rest:response>
      <http:response status="200" >
	<http:header name="Content-Type" value="{$content_type}"/>
      </http:response>
    </rest:response>
    ,
    $results1
    )
else
  <rest:response>
    <http:response status="404" message="No registered function with URN='{$function/@urn}'.">
      <http:header name="Content-Language" value="en"/>
      <http:header name="Content-Type" value="text/html; charset=utf-8"/>
    </http:response>
  </rest:response>
};

declare function csr_proc:wrap_result($result,$content-type) {
 <csd:careServicesResponse content-type="{$content-type}"><csd:result>{$result}</csd:result></csd:careServicesResponse>
};

declare function csr_proc:process_CSR_stored_results($db,$doc,$careServicesRequest)
{
  csr_proc:process_CSR_stored_results($db,$doc,$careServicesRequest,map:merge(()))
};

declare function csr_proc:process_CSR_stored_results($db,$doc,$careServicesRequest,$bindings as map(*))
{
let $function :=
  if (exists($careServicesRequest/csd:function ))
  then $careServicesRequest/csd:function
  else $careServicesRequest

let $urn :=
  if (exists($careServicesRequest/csd:function ))
  then string($function/@urn)
  else string($function/@function)

let $doc_name :=  string($function/@resource)
let $base_url :=  string($function/@base_url)

let $stored_functions := csr_proc:stored_functions($db)
let $definition := ($stored_functions[@urn = $urn])[1]/csd:definition/text()

let $options := csr_proc:lookup_stored_options($db,$function/@urn)

let $requestParams :=
 <csd:requestParams resource='{$doc_name}' function='{$urn}' base_url='{$base_url}'>
   {
     if ($function/csd:requestParams) then $function/csd:requestParams/*
   else $function/requestParams/*
   }
 </csd:requestParams>

let $csr_bindings :=  map{'':$doc,'careServicesRequest':$requestParams}
let $all_bindings :=  map:merge(($csr_bindings, $bindings))

return if (exists($definition)) then
  xquery:eval($definition,$all_bindings,$options)
else ()
};

declare function csr_proc:create_adhoc_doc($adhoc_query,$content_type) {
let $content := if ($content_type) then $content_type else "application/xml"
return
 <csd:careServicesRequest xmlns:csd='urn:ihe:iti:csd:2013'>
  <csd:expression content-type='{$content}'>
  {$adhoc_query}
  </csd:expression>
</csd:careServicesRequest>

};

declare function csr_proc:get_function_definition($db,$urn) {
  csr_proc:stored_functions($db)[@urn = $urn][1]
};

declare function csr_proc:get_updating_function_definition($db,$urn) {
  csr_proc:stored_updating_functions($db)[@urn = $urn][1]
};

declare function csr_proc:get_any_function_definition($db,$urn) {
  (csr_proc:stored_updating_functions($db)[@urn = $urn] , csr_proc:stored_functions($db)[@urn = $urn])[1]
};

declare updating function csr_proc:process_updating_CSR($db,$function,$doc_name,$base_url)
{
  csr_proc:process_updating_CSR($db,$function,$doc_name,$base_url,map:merge(()))
};

declare updating function csr_proc:process_updating_CSR_results($db,$function)
{
  csr_proc:process_updating_CSR_results($db,$function,map:merge(()))
};

declare updating function csr_proc:process_updating_CSR_results($db,$careServicesRequest, $bindings as map(*)) {
let $function :=$careServicesRequest//csd:function
let $doc_name := string($function/@resource)
let $base_url := string($function/@base_url)
let $doc := csd_dm:open_document($db,$doc_name)
let $urn := $function/@urn
let $stored_updating_functions := csr_proc:stored_updating_functions($db)
let $definition := $stored_updating_functions[@urn = $urn][1]/csd:definition/text()
let $content_type := csr_proc:lookup_stored_content_type($db,$function/@urn)
let $requestParams := <csd:requestParams resource='{$doc_name}' function='{$urn}' base_url='{$base_url}'>
  {
    if ($function/csd:requestParams) then $function/csd:requestParams/*
    else $function/requestParams/*
  }
</csd:requestParams>

let $csr_bindings :=  map{'':$doc,'careServicesRequest':$requestParams}
let $all_bindings :=  map:merge(($csr_bindings, $bindings))

let $options := csr_proc:lookup_stored_options($db,$function/@urn)

return if (exists($definition)) then
  (
  xquery:update($definition,$all_bindings,$options)
  )
else  ()

};

declare updating function csr_proc:process_updating_CSR_stored_results($db,$doc,$careServicesRequest)
{
  csr_proc:process_updating_CSR_stored_results($db,$doc,$careServicesRequest,map:merge(()))
};

declare updating function csr_proc:process_updating_CSR_stored_results($db,$doc,$careServicesRequest,$bindings as map(*))
{
let $stored_updating_functions := csr_proc:stored_updating_functions($db)

let $csr_bindings :=  map{'':$doc,'careServicesRequest':$careServicesRequest}
let $all_bindings :=  map:merge(($csr_bindings, $bindings))
let $function := $careServicesRequest/@function
let $options := csr_proc:lookup_stored_options($db,$function)
let $definition := $stored_updating_functions[@urn = $function][1]/csd:definition/text()
let $out := serialize($careServicesRequest)
return if (exists($definition)) then
  xquery:update($definition,$all_bindings,$options)
else
    db:output(
    <rest:response>
      <http:response status="404" message="No registered updating function with URN='{$function}'">
	<http:header name="Content-Language" value="en"/>
	<http:header name="Content-Type" value="text/html; charset=utf-8"/>
      </http:response>
    </rest:response>
       )
};

declare updating function csr_proc:process_updating_CSR($db,$careServicesRequest, $doc_name, $base_url,$bindings as map(*))
{

let $doc := csd_dm:open_document($db,$doc_name)
let $function := ($careServicesRequest//csd:function)[1]
let $urn := string($function/@urn)
let $content_type := csr_proc:lookup_stored_content_type($db,$function/@urn)
let $requestParams := <csd:requestParams resource="{$doc_name}" function="{$urn}" base_url="{$base_url}">
  {
    if (exists($function/csd:requestParams)) then $function/csd:requestParams/*
    else $function/requestParams/*
  }
</csd:requestParams>
return csr_proc:process_updating_CSR_stored_results($db,$doc,$requestParams,$bindings)
};

declare function csr_proc:lookup_stored_options($db,$urn)
{
  let $stored_updating_functions := csr_proc:stored_updating_functions($db)
  let $stored_functions := csr_proc:stored_functions($db)
  let $func := ($stored_functions[@urn = $urn], $stored_updating_functions[@urn = $urn])[1]
   return  ($func/csd:extension[@urn='urn:openhie.org:openinfoman:csr_processor' and  @type='xquery:options']/xquery:options)[1]
     (: See: http://docs.basex.org/wiki/XQuery_Module :)
};

declare function csr_proc:lookup_stored_content_type($db,$urn)
{
  let $stored_updating_functions := csr_proc:stored_updating_functions($db)
  let $stored_functions := csr_proc:stored_functions($db)
  return string(($stored_functions[@urn = $urn]/@content-type, $stored_updating_functions[@urn = $urn]/@content-type  , "text/xml")[1])
};

(:~
: This is the Care Services Discovery RESTful document processor
: @version 1.0
: @see https://github.com/openhie/openinfoman
:
:)
module namespace csr_proc = "https://github.com/openhie/openinfoman/csr_proc";

import module namespace csd_dm = "https://github.com/openhie/openinfoman/csd_dm";
import module namespace csd_mcs = "https://github.com/openhie/openinfoman/csd_mcs";

declare   namespace   csd = "urn:ihe:iti:csd:2013";
declare namespace xquery = "http://basex.org/modules/xquery";

(:this should proabably be moved to the database :)
declare variable $csr_proc:stored_query_dir := concat(file:current-dir() , "../resources/stored_query_definitions");
declare variable $csr_proc:stored_updating_query_dir := concat(file:current-dir() , "../resources/stored_updating_query_definitions");

declare variable $csr_proc:stored_functions_doc := 'stored_functions.xml';
declare variable $csr_proc:stored_updating_functions_doc := 'stored_updating_functions.xml';

declare function csr_proc:stored_functions($db) {
 db:open($db,$csr_proc:stored_functions_doc)/careServicesFunctions/csd:careServicesFunction
};

declare function csr_proc:stored_updating_functions($db) {
 db:open($db,$csr_proc:stored_updating_functions_doc)/careServicesFunctions/csd:careServicesFunction
};

declare updating function csr_proc:init($db) {
  (
    if (not (db:is-xml($db,$csr_proc:stored_updating_functions_doc))) then
      db:add($db, <careServicesFunctions/>,$csr_proc:stored_updating_functions_doc)
    else
      ()
      ,
    if (not (db:is-xml($db,$csr_proc:stored_functions_doc))) then
      db:add($db, <careServicesFunctions/>,$csr_proc:stored_functions_doc)
    else
      ()
  )
};

declare updating function csr_proc:clear_stored_functions($db) {
  (
    db:delete($db,$csr_proc:stored_functions_doc)
    ,db:delete($db,$csr_proc:stored_updating_functions_doc)
    ,csr_proc:init($db)
    )

};

declare updating function csr_proc:load_functions_from_files($db) {
  (
    if (file:is-dir($csr_proc:stored_query_dir)) then
      for $file in file:list($csr_proc:stored_query_dir,boolean('false'),"*.xml")
      let $func := doc(concat($csr_proc:stored_query_dir,'/',$file))/csd:careServicesFunction
      return if (exists($func)) then csr_proc:load_stored_function($db,$func) else ()
    else()
      ,
    if (file:is-dir($csr_proc:stored_updating_query_dir)) then
      for $file in file:list($csr_proc:stored_updating_query_dir,boolean('false'),"*.xml")
      let $func := doc(concat($csr_proc:stored_updating_query_dir,'/',$file))/csd:careServicesFunction
      return if (exists($func)) then csr_proc:load_stored_updating_function($db,$func) else ()
    else ()
  )
};

declare updating function csr_proc:delete_stored_function($db,$urn) {
  let $stored_updating_functions := db:open($db,$csr_proc:stored_updating_functions_doc)/careServicesFunctions/*
  let $stored_functions := db:open($db,$csr_proc:stored_functions_doc)/careServicesFunctions/*
  let $functions := ($stored_functions,$stored_updating_functions)

  let $old := $functions[@urn = $urn]
  return if ($old) then delete node $old else ()

};

declare updating function csr_proc:load_stored_updating_function($db,$func) {
  let $stored_updating_functions := db:open($db,$csr_proc:stored_updating_functions_doc)/careServicesFunctions
  let $urn := $func/@urn
  let $old := $stored_updating_functions/csd:careServicesFunction[@urn = $urn]
  return
    if (exists($urn)) then
      if (exists($old))
	then
	(delete node $old,
	insert node $func into $stored_updating_functions
	)
      else
	insert node $func into $stored_updating_functions
  else ()
};

declare updating function csr_proc:load_stored_function($db,$func) {
  let $stored_functions := db:open($db,$csr_proc:stored_functions_doc)/careServicesFunctions
  let $urn := $func/@urn
  let $old := $stored_functions/csd:careServicesFunction[@urn = $urn]
  return
    if (exists($urn) and exists($stored_functions)) then
      if (exists($old))
	then
	(delete node $old,
	insert node $func into $stored_functions
	)
      else
	insert node $func into $stored_functions
    else ()
};

declare function csr_proc:process_CSR($db,$careServicesRequest, $doc_name,$base_url)
{
csr_proc:process_CSR($db,$careServicesRequest, $doc_name, $base_url,map:merge(()))
};

declare function csr_proc:process_CSR($db,$careServicesRequest, $doc_name,$base_url,$bindings as map(*))
{
let $function :=$careServicesRequest/csd:function
let $adhoc :=$careServicesRequest/csd:expression
return if (exists($function))
then
  let $urn := string($function/@urn)
  return
    if ($urn = 'urn:ihe:iti:csd:2014:adhoc')
    then
      csr_proc:process_CSR_adhoc($db,$careServicesRequest/csd:function,$doc_name,$bindings)
    else
      let $csr :=
      <csd:careServicesRequest>
	<csd:function urn="{$urn}" resource='{$doc_name}' base_url='{$base_url}'>
	  {($function/*)[1]}
	</csd:function>
      </csd:careServicesRequest>
      return csr_proc:process_CSR_stored($db,$csr,$bindings)
else if (exists($adhoc))
then
  csr_proc:process_CSR_adhoc($db,$adhoc,$doc_name,$bindings)
else
 (:
  <rest:response>
    <http:response status="400" message="Invalid care services request.">
      <http:header name="Content-Language" value="en"/>
      <http:header name="Content-Type" value="text/html; charset=utf-8"/>
    </http:response>
  </rest:response>
  :)
  $careServicesRequest

};

declare function csr_proc:process_CSR_adhoc($db,$expression,$doc_name) {
   csr_proc:process_CSR_adhoc($expression,$doc_name,map:merge(()))
};

declare function csr_proc:process_CSR_adhoc($db,$expression,$doc_name,$bindings as map(*))
{

let $doc :=  csd_dm:open_document($db,$doc_name)

let $expr :=string($expression)
return if ($expr) then
  let $result := xquery:eval($expr,map{"":$doc},$bindings)
  return(
   <rest:response>
   <http:response status="200" >
      <http:header name="Content-Type" value="{$expression/@content-type}"/>
    </http:response>
  </rest:response>
  ,$result
 )
else
   <rest:response>
     <http:response status="400" message="No ad-hoc expression" />
  </rest:response>
};

declare function csr_proc:process_CSR_stored($db,$careServicesRequest)
{
  csr_proc:process_CSR_stored($db,$careServicesRequest,map:merge(()))
};

declare function csr_proc:process_CSR_stored($db,$careServicesRequest,$bindings as map(*))
{
let $function :=$careServicesRequest/csd:function
let $stored_functions := csr_proc:stored_functions($db)
let $urn := string($function/@urn)
let $definition := ($stored_functions[@urn = $urn])[1]/csd:definition/text()
let $content_type := csr_proc:lookup_stored_content_type($db,$function/@urn)
let $doc_name := string($function/@resource)
let $doc :=  csd_dm:open_document($db,$doc_name)

let $results0 := csr_proc:process_CSR_stored_results($db,$doc,$careServicesRequest,$bindings)

return if (exists($definition)) then
  let $results1:=
    if ($function/@encapsulated) then
      csr_proc:wrap_result($results0,$content_type)
    else $results0
  return
    (
    <rest:response>
      <http:response status="200" >
	<http:header name="Content-Type" value="{$content_type}"/>
      </http:response>
    </rest:response>
    ,
    $results1
    )
else
  <rest:response>
    <http:response status="404" message="No registered function with URN='{$function/@urn}'.">
      <http:header name="Content-Language" value="en"/>
      <http:header name="Content-Type" value="text/html; charset=utf-8"/>
    </http:response>
  </rest:response>
};

declare function csr_proc:wrap_result($result,$content-type) {
 <csd:careServicesResponse content-type="{$content-type}"><csd:result>{$result}</csd:result></csd:careServicesResponse>
};

declare function csr_proc:process_CSR_stored_results($db,$doc,$careServicesRequest)
{
  csr_proc:process_CSR_stored_results($db,$doc,$careServicesRequest,map:merge(()))
};

declare function csr_proc:process_CSR_stored_results($db,$doc,$careServicesRequest,$bindings as map(*))
{
let $function :=
  if (exists($careServicesRequest/csd:function ))
  then $careServicesRequest/csd:function
  else $careServicesRequest

let $urn :=
  if (exists($careServicesRequest/csd:function ))
  then string($function/@urn)
  else string($function/@function)

let $doc_name :=  string($function/@resource)
let $base_url :=  string($function/@base_url)

let $stored_functions := csr_proc:stored_functions($db)
let $definition := ($stored_functions[@urn = $urn])[1]/csd:definition/text()

let $options := csr_proc:lookup_stored_options($db,$function/@urn)

let $requestParams :=
 <csd:requestParams resource='{$doc_name}' function='{$urn}' base_url='{$base_url}'>
   {
     if ($function/csd:requestParams) then $function/csd:requestParams/*
   else $function/requestParams/*
   }
 </csd:requestParams>

let $csr_bindings :=  map{'':$doc,'careServicesRequest':$requestParams}
let $all_bindings :=  map:merge(($csr_bindings, $bindings))

return if (exists($definition)) then
  xquery:eval($definition,$all_bindings,$options)
else ()
};

declare function csr_proc:create_adhoc_doc($adhoc_query,$content_type) {
let $content := if ($content_type) then $content_type else "application/xml"
return
 <csd:careServicesRequest xmlns:csd='urn:ihe:iti:csd:2013'>
  <csd:expression content-type='{$content}'>
  {$adhoc_query}
  </csd:expression>
</csd:careServicesRequest>

};

declare function csr_proc:get_function_definition($db,$urn) {
  csr_proc:stored_functions($db)[@urn = $urn][1]
};

declare function csr_proc:get_updating_function_definition($db,$urn) {
  csr_proc:stored_updating_functions($db)[@urn = $urn][1]
};

declare function csr_proc:get_any_function_definition($db,$urn) {
  (csr_proc:stored_updating_functions($db)[@urn = $urn] , csr_proc:stored_functions($db)[@urn = $urn])[1]
};

declare updating function csr_proc:process_updating_CSR($db,$function,$doc_name,$base_url)
{
  csr_proc:process_updating_CSR($db,$function,$doc_name,$base_url,map:merge(()))
};

declare updating function csr_proc:process_updating_CSR_results($db,$function)
{
  csr_proc:process_updating_CSR_results($db,$function,map:merge(()))
};

declare updating function csr_proc:process_updating_CSR_results($db,$careServicesRequest, $bindings as map(*)) {
let $function :=$careServicesRequest//csd:function
let $doc_name := string($function/@resource)
let $base_url := string($function/@base_url)
let $doc := csd_dm:open_document($db,$doc_name)
let $urn := $function/@urn
let $stored_updating_functions := csr_proc:stored_updating_functions($db)
let $definition := $stored_updating_functions[@urn = $urn][1]/csd:definition/text()
let $content_type := csr_proc:lookup_stored_content_type($db,$function/@urn)
let $requestParams := <csd:requestParams resource='{$doc_name}' function='{$urn}' base_url='{$base_url}'>
  {
    if ($function/csd:requestParams) then $function/csd:requestParams/*
    else $function/requestParams/*
  }
</csd:requestParams>

let $csr_bindings :=  map{'':$doc,'careServicesRequest':$requestParams}
let $all_bindings :=  map:merge(($csr_bindings, $bindings))

let $options := csr_proc:lookup_stored_options($db,$function/@urn)

return if (exists($definition)) then
  (
  xquery:update($definition,$all_bindings,$options)
  )
else  ()

};

declare updating function csr_proc:process_updating_CSR_stored_results($db,$doc,$careServicesRequest)
{
  csr_proc:process_updating_CSR_stored_results($db,$doc,$careServicesRequest,map:merge(()))
};

declare updating function csr_proc:process_updating_CSR_stored_results($db,$doc,$careServicesRequest,$bindings as map(*))
{
let $stored_updating_functions := csr_proc:stored_updating_functions($db)

let $csr_bindings :=  map{'':$doc,'careServicesRequest':$careServicesRequest}
let $all_bindings :=  map:merge(($csr_bindings, $bindings))
let $function := $careServicesRequest/@function
let $options := csr_proc:lookup_stored_options($db,$function)
let $definition := $stored_updating_functions[@urn = $function][1]/csd:definition/text()

return if (exists($definition)) then
  xquery:update($definition,$all_bindings,$options)
else
    db:output(
    <rest:response>
      <http:response status="404" message="No registered updating function with URN='{$function}">
	<http:header name="Content-Language" value="en"/>
	<http:header name="Content-Type" value="text/html; charset=utf-8"/>
      </http:response>
    </rest:response>
       )
};

declare updating function csr_proc:process_updating_CSR($db,$careServicesRequest, $doc_name, $base_url,$bindings as map(*))
{

let $doc := csd_dm:open_document($db,$doc_name)
let $function :=$careServicesRequest//csd:function
let $urn := $function/@urn
let $content_type := csr_proc:lookup_stored_content_type($db,$function/@urn)
let $requestParams := <csd:requestParams resource="{$doc_name}" function="{$urn}" base_url="{$base_url}">
  {
    if ($function/csd:requestParams) then $function/csd:requestParams/*
    else $function/requestParams/*
  }
</csd:requestParams>
return csr_proc:process_updating_CSR_stored_results($db,$doc,$requestParams,$bindings)
};

declare function csr_proc:lookup_stored_options($db,$urn)
{
  let $stored_updating_functions := csr_proc:stored_updating_functions($db)
  let $stored_functions := csr_proc:stored_functions($db)
  let $func := ($stored_functions[@urn = $urn], $stored_updating_functions[@urn = $urn])[1]
   return  ($func/csd:extension[@urn='urn:openhie.org:openinfoman:csr_processor' and  @type='xquery:options']/xquery:options)[1]
     (: See: http://docs.basex.org/wiki/XQuery_Module :)
};

declare function csr_proc:lookup_stored_content_type($db,$urn)
{
  let $stored_updating_functions := csr_proc:stored_updating_functions($db)
  let $stored_functions := csr_proc:stored_functions($db)
  return string(($stored_functions[@urn = $urn]/@content-type, $stored_updating_functions[@urn = $urn]/@content-type  , "text/xml")[1])
};


Update BaseX Array Module function signatures: clean up annotations and arguments for `array:sort`.

xquery version "3.1";
(:~
:  BaseX array module functions
:
: @see http://docs.basex.org/wiki/Arry_Module
:)
module namespace array = "http://www.w3.org/2005/xpath-functions/array";

import module namespace a = "http://reecedunn.co.uk/xquery/annotations" at "res://reecedunn.co.uk/xquery/annotations.xqy";

declare %a:since("basex", "8.0") function array:size($input as array(*)) as xs:integer external;
declare %a:since("basex", "8.0") function array:get($array as array(*), $position as xs:integer) as item()* external;
declare %a:since("basex", "8.0") function array:append($array as array(*), $member as item()*) as array(*) external;
declare %a:since("basex", "8.0") function array:subarray($array as array(*), $position as xs:integer) as array(*) external;
declare %a:since("basex", "8.0") function array:subarray($array as array(*), $position as xs:integer, $length as xs:integer) as array(*) external;
declare %a:since("basex", "8.5") function array:put($array as array(*), $position as xs:integer, $member as item()*) as array(*) external;
declare %a:since("basex", "8.0") function array:remove($array as array(*), $positions as xs:integer*) as array(*) external;
declare %a:since("basex", "8.0") function array:insert-before($array as array(*), $position as xs:integer, $member as item()*) as array(*) external;
declare %a:since("basex", "8.0") function array:head($array as array(*)) as item()* external;
declare %a:since("basex", "8.0") function array:tail($array as array(*)) as array(*) external;
declare %a:since("basex", "8.0") function array:reverse($array as array(*)) as array(*) external;
declare %a:since("basex", "8.0") function array:join($arrays as array(*)*) as array(*) external;
declare %a:since("basex", "8.0") function array:flatten($items as item()*) as item()* external;
declare %a:since("basex", "8.0") function array:for-each($array as array(*), $function as function(item()*) as item()*) as array(*) external;
declare %a:since("basex", "8.0") function array:filter($array as array(*), $function as function(item()*) as xs:boolean) as array(*) external;
declare %a:since("basex", "8.0") function array:fold-left($array as array(*), $zero as item()*, $function as function(item()*, item()*) as item()*) as item()* external;
declare %a:since("basex", "8.0") function array:fold-right($array as array(*), $zero as item()*, $function as function(item()*, item()*) as item()*) as item()* external;
declare %a:since("basex", "8.0") function array:for-each-pair($array1 as array(*), $array2 as array(*), $function as function(item()*) as item()*) as array(*) external;
declare %a:since("basex", "8.0") function array:sort($array as array(*)) as array(*) external;
declare %a:since("basex", "8.6") function array:sort($array as array(*), $collation as xs:string?) as array(*) external;
declare %a:since("basex", "8.0") %a:until("basex", "8.6") function array:sort($array as array(*), $key as function(item()*) as xs:anyAtomicType*) as array(*) external;
declare %a:since("basex", "8.6") function array:sort($array as array(*), $collation as xs:string?, $key as function(item()*) as xs:anyAtomicType*) as array(*) external;
declare %a:since("basex", "8.0") %a:until("basex", "8.4") %a:see-also("basex", "8.4", "fn:serialize") function array:serialize($input as map(*)) as xs:string external;

xquery version "3.1";
(:~
:  BaseX array module functions
:
: @see http://docs.basex.org/wiki/Arry_Module
:)
module namespace array = "http://www.w3.org/2005/xpath-functions/array";

import module namespace a = "http://reecedunn.co.uk/xquery/annotations" at "res://reecedunn.co.uk/xquery/annotations.xqy";

declare %a:since("basex", "8.0") function array:size($input as array(*)) as xs:integer external;
declare %a:since("basex", "8.0") function array:get($array as array(*), $position as xs:integer) as item()* external;
declare %a:since("basex", "8.0") function array:append($array as array(*), $member as item()*) as array(*) external;
declare %a:since("basex", "8.0") function array:subarray($array as array(*), $position as xs:integer) as array(*) external;
declare %a:since("basex", "8.0") function array:subarray($array as array(*), $position as xs:integer, $length as xs:integer) as array(*) external;
declare %a:since("basex", "8.5") function array:put($array as array(*), $position as xs:integer, $member as item()*) as array(*) external;
declare %a:since("basex", "8.0") function array:remove($array as array(*), $positions as xs:integer*) as array(*) external;
declare %a:since("basex", "8.0") function array:insert-before($array as array(*), $position as xs:integer, $member as item()*) as array(*) external;
declare %a:since("basex", "8.0") function array:head($array as array(*)) as item()* external;
declare %a:since("basex", "8.0") function array:tail($array as array(*)) as array(*) external;
declare %a:since("basex", "8.0") function array:reverse($array as array(*)) as array(*) external;
declare %a:since("basex", "8.0") function array:join($arrays as array(*)*) as array(*) external;
declare %a:since("basex", "8.0") function array:flatten($items as item()*) as item()* external;
declare %a:since("basex", "8.0") function array:for-each($array as array(*), $function as function(item()*) as item()*) as array(*) external;
declare %a:since("basex", "8.0") function array:filter($array as array(*), $function as function(item()*) as xs:boolean) as array(*) external;
declare %a:since("basex", "8.0") function array:fold-left($array as array(*), $zero as item()*, $function as function(item()*, item()*) as item()*) as item()* external;
declare %a:since("basex", "8.0") function array:fold-right($array as array(*), $zero as item()*, $function as function(item()*, item()*) as item()*) as item()* external;
declare %a:since("basex", "8.0") function array:for-each-pair($array1 as array(*), $array2 as array(*), $function as function(item()*) as item()*) as array(*) external;
declare %a:since("basex", "8.0") function array:sort($array as array(*)) as array(*) external;
declare %a:since("basex", "8.0") function array:sort($array as array(*), $collation as xs:string?) as array(*) external;
declare %a:since("basex", "8.0") %a:until("basex", "8.6") function array:sort($array as array(*), $collation as xs:string?, $key as function(item()*) as xs:anyAtomicType*) as array(*) external; (: [8.6]$collation as xs:string? :)
declare %a:since("basex", "8.0") %a:until("basex", "8.4") function array:serialize($input as map(*)) as xs:string external;