360 lines
15 KiB
Plaintext
360 lines
15 KiB
Plaintext
(:~
|
|
: Taxonomies functions.
|
|
:
|
|
: @author Rave Technologies, https://www.rave-tech.com/, 2017
|
|
:)
|
|
|
|
|
|
module namespace blproduct = 'http://www.rave-tech.com/bloomsbury/product';
|
|
import module namespace config = 'http://www.rave-tech.com/bloomsbury/config' at './module/config.xqm';
|
|
import module namespace audit = 'http://www.rave-tech.com/bloomsbury/audit' at './module/manage-audit.xqm';
|
|
import module namespace schematron = "http://github.com/Schematron/schematron-basex";
|
|
declare namespace product = 'http://cms.bloomsbury.com/product-manifest';
|
|
|
|
(:~
|
|
: Add Product into the system.
|
|
: @param $body Product XML as document node <product>....</<product>
|
|
: @header $authorization Authorization key
|
|
: @return element(result)
|
|
:)
|
|
declare
|
|
%updating
|
|
%rest:path("/products")
|
|
%rest:POST("{$body}")
|
|
%rest:header-param("Authorization", "{$authorization}", "none")
|
|
%rest:consumes("application/xml", "text/xml")
|
|
function blproduct:add(
|
|
$body as document-node(),
|
|
$authorization as xs:string
|
|
)
|
|
{
|
|
config:session-check($authorization),
|
|
try
|
|
{
|
|
let $productID := if($body/product:product/@id/string()!='')
|
|
then $body/product:product/@id/string()
|
|
else fn:concat($config:ProductIDPrefix,fn:string(convert:dateTime-to-integer(fn:adjust-dateTime-to-timezone(convert:integer-to-dateTime(prof:current-ms())))))
|
|
let $body := if($body/product:product/@id/string()!='')
|
|
then $body
|
|
else
|
|
copy $c := $body
|
|
modify replace node $c/product:product/@id with (attribute{'id'}{$productID})
|
|
return $c
|
|
let $name := $body/product:product/@name/string()
|
|
let $targetUri := fn:concat($config:ProductDir,$productID,$config:LatestDir,$productID,'.xml')
|
|
return
|
|
(:Step 1: Check if Product is already exist :)
|
|
if(fn:doc-available(fn:concat($config:CoreDatabase,$targetUri)))
|
|
then update:output(<result><status>Failure</status><message>Product is already available</message></result>)
|
|
else blproduct:validate-and-add-product($body,$productID,$name,$targetUri,'Product Add',config:session-value($authorization))
|
|
}
|
|
catch *
|
|
{
|
|
admin:write-log("[Product Add][Error: " || $err:description || "]"),
|
|
admin:write-log("[Product Add][Error: " || $err:additional || "]"),
|
|
update:output(<result><status>Error</status><message>Error generated. Please check system log</message></result>)
|
|
}
|
|
};
|
|
|
|
|
|
(:~
|
|
: Update Product.
|
|
: @param $body Product XML as document node <product>....</product>
|
|
: @header $authorization Authorization key
|
|
: @return element(result)
|
|
:)
|
|
declare
|
|
%updating
|
|
%rest:path("/products/{$productID=.+}")
|
|
%rest:PUT("{$body}")
|
|
%rest:header-param("Authorization", "{$authorization}", "none")
|
|
%rest:consumes("application/xml", "text/xml")
|
|
function blproduct:update(
|
|
$body as document-node(),
|
|
$productID as xs:string,
|
|
$authorization as xs:string
|
|
)
|
|
{
|
|
config:session-check($authorization),
|
|
try
|
|
{
|
|
let $productID := $body/product:product/@id/string()
|
|
let $name := $body/product:product/@name/string()
|
|
let $targetUri := fn:concat($config:ProductDir,$productID,$config:LatestDir,$productID,'.xml')
|
|
return
|
|
(:Step 1: Check if Product is already exist :)
|
|
if(fn:doc-available(fn:concat($config:CoreDatabase ,$targetUri)))
|
|
then blproduct:validate-and-add-product($body,$productID,$name,$targetUri,'Product Update',config:session-value($authorization))
|
|
else update:output(<result><status>Failure</status><message>Product is not available to update</message></result>)
|
|
}
|
|
catch *
|
|
{
|
|
admin:write-log("[Product Update][Error: " || $err:description || "]"),
|
|
admin:write-log("[Product Update][Error: " || $err:additional || "]"),
|
|
update:output(<result><status>Error</status><message>Error generated. Please check system log</message></result>)
|
|
}
|
|
};
|
|
|
|
(:~
|
|
: Display Product specific detail.
|
|
: @param $productID ID of the product
|
|
: @header $authorization Authorization key
|
|
: @return element(result)
|
|
:)
|
|
declare
|
|
%rest:path("/products/{$productID=.+}")
|
|
%rest:GET
|
|
%rest:header-param("Authorization", "{$authorization}", "none")
|
|
|
|
function blproduct:info(
|
|
$productID as xs:string?,
|
|
$authorization as xs:string
|
|
)
|
|
{
|
|
config:session-check($authorization),
|
|
try
|
|
{
|
|
let $targetUri := fn:concat($config:ProductDir,$productID,$config:LatestDir,$productID,'.xml')
|
|
let $productXml := db:open($config:CoreDatabase,$targetUri)
|
|
return
|
|
if($productXml)
|
|
then <result><status>Success</status>{$productXml}</result>
|
|
else <result><status>Failure</status><message>Product is unavailable</message></result>
|
|
}
|
|
catch *
|
|
{
|
|
admin:write-log("[Product Info][Error: " || $err:description || "]"),
|
|
admin:write-log("[Product Info][Error: " || $err:additional || "]"),
|
|
<result><status>Error</status><message>Error generated. Please check system log</message></result>
|
|
}
|
|
};
|
|
|
|
(:~
|
|
: To get specific or list of available product.
|
|
: @param $query Name of the product to filter
|
|
: @param $page Page number of the list optional
|
|
: @param $size Total number of records to display in a page optional
|
|
: @header $authorization Authorization key
|
|
: @return element(result)
|
|
:)
|
|
declare
|
|
%rest:path("/products")
|
|
%rest:GET
|
|
%rest:query-param("q", "{$query}")
|
|
%rest:query-param("user", "{$user}")
|
|
%rest:query-param("page", "{$page}")
|
|
%rest:query-param("size", "{$size}")
|
|
%rest:header-param("Authorization", "{$authorization}", "none")
|
|
|
|
function blproduct:list(
|
|
$query as xs:string?,
|
|
$user as xs:string?,
|
|
$page as xs:integer?,
|
|
$size as xs:integer?,
|
|
$authorization as xs:string
|
|
)
|
|
{
|
|
config:session-check($authorization),
|
|
try
|
|
{
|
|
|
|
|
|
let $page := if($page) then $page else $config:page
|
|
let $size := if($size) then $size else $config:size
|
|
let $productRecords := <products>{
|
|
for $eachProduct in db:open($config:CoreDatabase ,$config:ProductDir)/product:product[if($user) then ((*:managers/*:user/@id/string()=$user) or (db:open($config:CoreDatabase,$config:UserDir)/user[role='admin'][id=$user]) or (@createdBy/string()=$user)) else .]
|
|
[fn:contains(fn:base-uri(.),$config:LatestDir)]
|
|
[fn:not(fn:matches(fn:base-uri(.),$config:VersionDir))]
|
|
[fn:not(@delete)]
|
|
[if($query) then (matches(@name,$query,'i')) else .]
|
|
let $id := $eachProduct/@id/string()
|
|
let $name := $eachProduct/@name/string()
|
|
let $taxonomyRef := $eachProduct/@taxonomyRef/string()
|
|
let $pipelineRef := $eachProduct/@pipelineRef/string()
|
|
let $description := $eachProduct/@pipelineRef/string()
|
|
let $delete := $eachProduct/@delete/string()
|
|
order by $name
|
|
return <product xmlns="http://cms.bloomsbury.com/product-manifest" id="{$id}" name="{$name}" taxonomyRef="{$taxonomyRef}" pipelineRef="{$pipelineRef}" description="{$description}" delete="{$delete}"></product>
|
|
}</products>
|
|
let $countRecord := count($productRecords/*:product)
|
|
let $start := if($page eq 1)
|
|
then $page
|
|
else if($page gt 1)
|
|
then fn:sum(($page * $size)+1) - $size
|
|
else fn:sum(($page * $size)) - $size
|
|
let $end := if($page eq 1)
|
|
then $size
|
|
else if($page gt 1)
|
|
then fn:sum(($start + $size)-1)
|
|
else fn:sum($start + $size)
|
|
return
|
|
if($productRecords/*:product)
|
|
then <result><status>Success</status><total>{$countRecord}</total><entities>{$productRecords/*:product[position() = $start to $end]}</entities></result>
|
|
else <result><status>Success</status><message>Product is unavailable</message></result>
|
|
|
|
}
|
|
catch *
|
|
{
|
|
admin:write-log("[Product List][Error: " || $err:description || "]"),
|
|
admin:write-log("[Product List][Error: " || $err:additional || "]"),
|
|
<result><status>Error</status><message>Error generated. Please check system log</message></result>
|
|
}
|
|
};
|
|
|
|
(:~
|
|
: Soft Delete product.
|
|
: @param $productID ID of the product to delete
|
|
: @header $authorization Authorization key
|
|
: @return string message
|
|
:)
|
|
declare
|
|
%updating
|
|
%rest:path("/products/{$productID=.+}")
|
|
%rest:DELETE
|
|
%rest:header-param("Authorization", "{$authorization}", "none")
|
|
|
|
function blproduct:delete(
|
|
$productID as xs:string,
|
|
$authorization as xs:string
|
|
)
|
|
{
|
|
(: TODO: we added @delete into root element however it is not valid as per RELAX NG :)
|
|
config:session-check($authorization),
|
|
let $productXml := db:open($config:CoreDatabase,$config:ProductDir)/product:product[@id=$productID]
|
|
[fn:contains(base-uri(.),$config:LatestDir)]
|
|
return
|
|
if($productXml)
|
|
then
|
|
let $config:ProductUri := fn:concat($config:ProductDir,$productID,$config:LatestDir,$productID,'.xml')
|
|
return
|
|
(
|
|
insert node (attribute{'delete'}{'Yes'}) into db:open($config:CoreDatabase,$config:ProductUri)/product:product,
|
|
audit:product($productID,'Product Delete',config:session-value($authorization)),
|
|
update:output(<result><status>Success</status><message>Product Deleted</message></result>)
|
|
)
|
|
else update:output(<result><status>Failure</status><message>Product is unavailable</message></result>)
|
|
};
|
|
|
|
|
|
(:~
|
|
: Add or update products.
|
|
: @param $productXml Product XML content
|
|
: @param $productID ID of the product
|
|
: @param $name Name of the product
|
|
: @param $targetUri The saving path of the product
|
|
: @param $messageFlag The action of product (add or update)
|
|
: @param $sessionValue Session Value
|
|
: @return element(result)
|
|
:)
|
|
declare %private %updating function blproduct:validate-and-add-product(
|
|
$productXml as document-node(),
|
|
$productID as xs:string,
|
|
$name as xs:string,
|
|
$targetUri as xs:string,
|
|
$messageFlag as xs:string,
|
|
$sessionValue as xs:string
|
|
)
|
|
{
|
|
(:Validate as per Relax NG :)
|
|
let $reportChunk := <report>
|
|
<info>
|
|
<id>{$productID}</id>
|
|
<name>{$name}</name>
|
|
<submitted-on>{fn:adjust-dateTime-to-timezone(convert:integer-to-dateTime(prof:current-ms()))}</submitted-on>
|
|
<requester-id>{fn:substring-before($sessionValue,'$$$$')}</requester-id>
|
|
<requester-email>{fn:substring-after($sessionValue,'$$$$')}</requester-email>
|
|
</info>
|
|
<ingestion-report>
|
|
<steps/>
|
|
</ingestion-report>
|
|
</report>
|
|
let $startTime := fn:adjust-dateTime-to-timezone(convert:integer-to-dateTime(prof:current-ms()))
|
|
let $validate := <validate><status>valid</status></validate>
|
|
(: TODO: skipped it to ingest invalid file :)
|
|
(:validate:rng-report($productXml, bin:decode-string(db:retrieve($config:Database, fn:concat($config:ValidationDir,$config:ProductRelaxNG)),'UTF-8'), fn:true()):)
|
|
return
|
|
if($validate/status='valid')
|
|
then
|
|
(
|
|
config:update-message("[" || $messageFlag || "][Product is valid as per Relax NG : " || $productID || "]")
|
|
,
|
|
let $reportUri := fn:concat($config:ProductDir,$productID,'/','report.xml')
|
|
let $appendReport := let $updateReport := <step>
|
|
<type>RELAX NG Validation</type>
|
|
<started-on>{$startTime}</started-on>
|
|
<completed-on>{fn:adjust-dateTime-to-timezone(convert:integer-to-dateTime(prof:current-ms()))}</completed-on>
|
|
<status>Pass</status>
|
|
<error></error>
|
|
</step>
|
|
return
|
|
copy $target := $reportChunk
|
|
modify insert node $updateReport as last into $target/ingestion-report/steps
|
|
return $target
|
|
return
|
|
(
|
|
if($messageFlag='Product Update') then blproduct:create-version($productID) else(),
|
|
db:replace($config:CoreDatabase,$targetUri,$productXml),
|
|
db:replace($config:CoreDatabase,$reportUri,$appendReport),
|
|
audit:product($productID,$messageFlag,$sessionValue),
|
|
update:output(<result><status>Success</status><message>{$messageFlag}</message></result>)
|
|
)
|
|
)
|
|
else
|
|
(
|
|
let $appendReport := let $updateReport := <step>
|
|
<type>RELAX NG Validation</type>
|
|
<started-on>{$startTime}</started-on>
|
|
<completed-on>{fn:adjust-dateTime-to-timezone(convert:integer-to-dateTime(prof:current-ms()))}</completed-on>
|
|
<status>Fail</status>
|
|
<error>{$validate}</error>
|
|
</step>
|
|
return
|
|
copy $target := $reportChunk
|
|
modify
|
|
(
|
|
insert node $updateReport as last into $target/ingestion-report/steps,
|
|
insert node <finished-on>{fn:adjust-dateTime-to-timezone(convert:integer-to-dateTime(prof:current-ms()))}</finished-on> as last into $target/ingestion-report
|
|
)
|
|
return $target
|
|
let $reportUri := fn:concat($config:ProductDir,$productID,'/','report.xml')
|
|
return db:replace($config:CoreDatabase,$reportUri,$appendReport)
|
|
,
|
|
update:output(<result><status>Failure</status><message>Product is invalid as per Relax NG. Error report generated</message></result>)
|
|
)
|
|
};
|
|
|
|
(:~
|
|
: Maintain history of the product.
|
|
: @param $productID ID of the product
|
|
: @return empty sequence
|
|
:)
|
|
declare %private %updating function blproduct:create-version(
|
|
$productID as xs:string
|
|
)
|
|
{
|
|
let $versionFileName := let $tstamp := fn:current-dateTime()
|
|
let $suffix := fn:format-dateTime($tstamp, "[Y1,4][M01][D01][H01][m01][s01][f01]")
|
|
return fn:concat($productID,'_version_',$suffix,'.xml')
|
|
let $targetUri := fn:concat($config:ProductDir,$productID,$config:LatestDir,$productID,'.xml')
|
|
let $versionUri := fn:replace(fn:substring-before($targetUri,fn:tokenize($targetUri,'/')[last()]),$config:LatestDir,$config:VersionDir)
|
|
return
|
|
(
|
|
db:replace($config:CoreDatabase, fn:concat($versionUri,$versionFileName), db:open($config:CoreDatabase,$targetUri))
|
|
,
|
|
config:update-message("[Product Update][Product version is created : " || $productID || "]")
|
|
,
|
|
let $versionControlUri := fn:concat($config:ProductDir,$productID,'/',$config:VersionControlFileName)
|
|
return
|
|
if(fn:doc-available(fn:concat('/',$config:CoreDatabase ,$versionControlUri)))
|
|
then
|
|
let $versionDoc := db:open($config:CoreDatabase,$versionControlUri)
|
|
let $nextVersion := fn:sum(max($versionDoc/versions/version/@number) + 1)
|
|
return
|
|
(
|
|
insert node <version number="{$nextVersion}" uri="{fn:concat($versionUri,$versionFileName)}" on="{fn:adjust-dateTime-to-timezone(fn:current-dateTime())}"/> into db:open($config:CoreDatabase,$versionControlUri)/versions,
|
|
config:update-message("[Product Control][Product Control updated : " || $productID || "]")
|
|
)
|
|
else
|
|
db:replace($config:CoreDatabase,$versionControlUri,<versions><version number="1" uri="{fn:concat($versionUri,$versionFileName)}" on="{fn:adjust-dateTime-to-timezone(fn:current-dateTime())}"/></versions>)
|
|
)
|
|
}; |