Closed jeet1212 closed 9 years ago
My first question would be, how was it converting the JSON into map:map before. I don't think that is default Roxy behavior. I am afraid you will have to share some bits of your custom code. Can you start with the main
function of the setcache
controller, and tell us whether /app/lib/router.xqy is in any way different from https://github.com/marklogic/roxy/blob/master/src/roxy/router.xqy? It might also help to show relevant lines from restful-router.xqy..
After more research, I think this is some misbehavior due to key/value pair handling in ML8. I might be wrong. Please ignore if this is not the roxy framework issue however I am struggling to identify/understand this misbehavior.
roxy/router.xqy has been overridden.
$user = <user-profile wk-pid="USR-b740cbc5-5e27-8b1c-d97a-1329625f6b39"><docType>Users</docType><username type="string">USR-b740cbc5-5e27-8b1c-d97a-1329625f6b39</username><userId type="string">lrunner91@wk.com</userId><email type="string">lrunner91@wk.com</email><userStatus type="string">Active</userStatus><defaultContentCollection>ALL</defaultContentCollection><role type="string">user</role><trialEndDate type="string"/><activationDate type="string">2013-12-25</activationDate><phoneNumber type="string"/><sendWelcomeEmail type="string"/><additionalEmail type="string"/><filingContent type="string">Allowed</filingContent><firstName type="string">John</firstName><lastName type="string">Doe</lastName><orgCode type="string">WK001</orgCode><orgName type="string">LoadRunner</orgName><salesTerritory type="string"/><password>5f4dcc3b5aa765d61d8327deb882cf99</password><pw>password</pw><countOfLoggedIn>11</countOfLoggedIn><created>2014-07-11T11:05:11.589-05:00</created><createdBy type="string">System</createdBy><modified>2014-07-11T11:05:11.589-05:00</modified><modifiedBy>yun.yang@wolterskluwer.com</modifiedBy></user-profile>
$session = <session wk-pid="2f27109f160ff5f2e6993023086a5cdf|4/29/2015 4:2:0 AM"><username>USR-b740cbc5-5e27-8b1c-d97a-1329625f6b39</username><created>2015-04-29T00:02:00.372+05:30</created><expiration>2015-04-29T04:02:00.372+05:30</expiration><currenttime>2015-04-29T00:02:00.372+05:30</currenttime><valid>false</valid></session>
$auth:CACHE-PREFIX = "USERSESSION"
auth:login-User($user) --->auth:cache-user($user, $session) -->ch:set-cache($name as xs:string, $value) -->ch:set-cache($hosts as xs:string*, $port as xs:string, $map) -->/setcache
declare function auth:cache-user($user, $session)
{
if($auth:ENABLE-CACHING = fn:true() ) then (
let $name := fn:concat($auth:CACHE-PREFIX,$session/@wk-pid)
let $map := map:map()
let $_ := map:put( $map, "user", $user)
let $_ := map:put( $map, "session", $session)
return ch:set-cache($name, $map)
) else ()
};
declare function ch:set-cache($name as xs:string, $value)
{
let $server := xdmp:server-status(xdmp:host(), xdmp:server())
let $port := $server/s:port/text()
let $params := map:map()
let $_ := map:put($params, $name, $value)
let $hosts := ch:server-hosts()
return ch:set-cache($hosts, $port, $params)
};
declare function ch:set-cache($hosts as xs:string*, $port as xs:string, $map)
{
for $host in $hosts
let $url := fn:concat($ch:PROTOCOL,"://",$host,":",$port,"/setcache")
let $_ := xdmp:log("Invoking setCache on - " || $url,$ch:LOG-LEVEL)
return
xdmp:http-post($url,
<options xmlns="xdmp:http">
<data>{xdmp:quote($map}</data>
</options>)
};
declare function c:main() as item()*
{
let $params := xdmp:get-request-body()
let $params := try {
xdmp:eval($params/node())
} catch($e) {
xdmp:log($e),
fn:error(xs:QName("INVALID-PARAMS"),"Expecting map")
}
let $name := map:keys($params)[1]
let $value := try {
xdmp:unquote(map:get($params, $name))/node()
} catch($e) {
map:get($params, $name)
}
let $value := if(map:keys($value)) then $value else ()
let $_ := cache:set-cache-host($name, $value)
let $result :=
<json:object type="object">
<json:responseCode>200</json:responseCode>
<json:message>Cache set successful</json:message>
</json:object>
return
(
ch:add-value("res-code", xs:int($result/json:responseCode) ),
ch:add-value("res-message", xs:string($result/json:message) ),
ch:add-value("result", $result),
ch:use-view("/worklists/render"),
ch:add-value(
"res-header",
element header {
element Date {fn:current-dateTime()},
element Content-Type {req:get("req-header")/content-type/fn:string()}
}
)
)
};
app/lib/router.xqy:
declare function router:route()
{
(: add HTTP header information to the request parameter object :)
let $x := (
map:put($req:request, "req-header",
element header {
for $header in xdmp:get-request-header-names()
return
element {fn:lower-case($header)} {
xdmp:get-request-header($header)
}
}
),
if (fn:exists(xdmp:get-request-body())) then
map:put($req:request, "req-body",
if (xdmp:get-request-body()/node() instance of element()) then
xdmp:get-request-body()/node()
else if (xdmp:get-request-body()/node() instance of text()) then
xdmp:quote(xdmp:get-request-body()/node())
else ()
)
else (),
router:set-format()
)
(: PRawal : Checks authentication before executing the request :)
let $valid-request :=
if(fn:not($config:SESSION-AUTHENTICATE)) then fn:true()
else if(xs:string($controller) = ("ping")) then fn:true()
else if(xs:string($controller) = ("login")) then fn:true()
else if(xs:string($controller) = ("logout")) then fn:true()
else if(xs:string($controller) = ("verify")) then fn:true()
else if(xs:string($controller) = ("setcache")) then fn:true()
else if(xs:string($controller) = ("browseCalibration")) then fn:true()
else if(xs:string($controller) = ("sessions")) then fn:true()
else if(xs:string($controller) = ("forgetpassword")) then fn:true()
else if(xs:string($controller) = ("resetpassword")) then fn:true()
else auth:authenticate()
(: run the controller. errors bubble up to the error module :)
let $data :=
if($valid-request) then
(
if(fn:string($valid-request)="true") then
xdmp:apply(xdmp:function(
fn:QName(fn:concat("http://marklogic.com/roxy/controller/", $controller), $func),
$controller-path))
else if(fn:string($valid-request) = "timeout") then (
xdmp:apply(xdmp:function(
fn:QName(fn:concat("http://marklogic.com/roxy/controller/login"), "login-timeout"),
$login-controller-path))
) else (
xdmp:apply(xdmp:function(
fn:QName(fn:concat("http://marklogic.com/roxy/controller/login"), "login-error"),
$login-controller-path))
)
)
else
(
xdmp:apply(xdmp:function(
fn:QName(fn:concat("http://marklogic.com/roxy/controller/login"), "login-error"),
$login-controller-path))
)
(: Roxy options :)
let $options :=
for $key in map:keys($ch:map)
where fn:starts-with($key, "ch:config-")
return
map:get($ch:map, $key)
(: remove options from the data :)
let $_ :=
for $key in map:keys($ch:map)
where fn:starts-with($key, "ch:config-")
return
map:delete($ch:map, $key)
let $format as xs:string := ($options[self::ch:config-format][ch:formats/ch:format = $format]/ch:format, $format)[1]
let $_ := (
for $header in map:get($ch:map, "res-header")/*
return
xdmp:add-response-header(fn:local-name($header), $header/fn:string()),
xdmp:set-response-code((map:get($ch:map, "res-code"), 200)[1],(map:get($ch:map, "res-message"), "OK")[1])
)
(: controller override of the view :)
let $view := ($options[self::ch:config-view][ch:formats/ch:format = $format]/ch:view, $default-view)[1][. ne ""]
(: controller override of the layout :)
let $layout :=
if (fn:exists($options[self::ch:config-layout][ch:formats/ch:format = $format])) then
$options[self::ch:config-layout][ch:formats/ch:format = $format]/ch:layout[. ne ""]
else
$default-layout
(: if the view return something other than the map or () then bypass the view and layout :)
let $bypass as xs:boolean := fn:exists($data) and fn:not($data instance of map:map)
return
if (fn:not($bypass) and (fn:exists($view) or fn:exists($layout))) then
let $view-result :=
if (fn:exists($ch:map) and fn:exists($view)) then
rh:render-view($view, $format, $ch:map)
else
()
return
if (fn:not($bypass) and fn:exists($layout)) then
let $_ :=
if (fn:exists($view-result) and
fn:not($view-result instance of map:map) and
fn:not(fn:deep-equal(document {$ch:map}, document {$view-result}))) then
map:put($ch:map, "view", $view-result)
else
map:put( $ch:map, "view",
for $key in map:keys($ch:map)
return
map:get($ch:map, $key) )
return
rh:render-layout($layout, $format, $ch:map)
else
$view-result
else if (fn:not($bypass)) then
for $key in map:keys($ch:map)
return
map:get($ch:map, $key)
else
$data,
xdmp:log( fn:concat("TEST#1 - ", xdmp:get-original-url()," - Time - ",xdmp:elapsed-time()),"info")
};
Hi @jeet1212,
To get up and running on MarkLogic 8, try replacing xdmp:eval($params/node())
with xdmp:from-json(xdmp:unquote($params/node()))
in the c:main()
function of setcache.xqy
.
xdmp:unquote
will parse your JSON string into an object-node()
, and then xdmp:from-json()
will convert the object-node()
into a json:object
, which is compatible with the map:*
functions.
I'm still not sure how this would've worked in MarkLogic 6; I wouldn't have thought that xdmp:eval()
would parse JSON strings ...
Hi Joseph,
It worked with your suggested code. Thank you so much for your help!
However i was trying doing following ways and it worked but it was carrying two changes,
1) I was adding $map inside a <m></m>
and passing <map:map/>
to the setcache.xqy
<data>{xdmp:quote(<m>{$map}</m>/*)}</data>
declare function ch:set-cache($hosts as xs:string*, $port as xs:string, $map)
{
for $host in $hosts
let $url := fn:concat($ch:PROTOCOL,"://",$host,":",$port,"/setcache")
let $_ := xdmp:log("Invoking setCache on - " || $url,$ch:LOG-LEVEL)
return
xdmp:http-post($url,
<options xmlns="xdmp:http">
<data>{xdmp:quote(<m>{$map}</m>/*)}</data>
</options>)
};
2) In the setcache.xqy, I was creating map that was getting from xdmp:eval($params/node()) inside c:main() function.
let $params := xdmp:eval($params/node())
let $params := map:map($params)
......
As far as this code is working in ML6, I am also not sure however some ML expert consultants made it workable :)
Thank you again for your time and help!
Regards, Indy
@jeet1212, it sounds like we can close this ticket, right?
Yes, Please close the ticket however we are seeing a lot of issues in our API code due to JSON/MAP handling in ML8. We are trying to investigate one by one and fixing those.
if possible, please take a look at my approach to fix the issue and provide your thoughts, will be great as when I tried solution provided by Joseph, It worked in one of the API where I was testing yesterday however failed in other cases.
Though my proposed solution is working in all the places so far.
Thank you for your time and help!
Regards, Indrajeet
Joe provided a small change, which sounds about right, but might not cover all your code. I am afraid you will have to check everywhere where you were parsing or serializing JSON.
If backwards-compatibility in the code would be important to you, you could consider checking result of eval, and if that is not an element, then apply xdmp:from-json to change object-node() into <jbasic:json>
. You could also check xdmp:version(), and if that starts with '8' do something different. But be careful not to use functions that won't compile on ML7. There are a few new function in ML8, like xdmp:from-json-string..
We are upgrading ML6 to ML8 and using roxy as well. The roxy is customized based on the customer requirement.
For one of the REST request "/setcache", we are sending below data to cache and that is being converted into map (key value pair)
ML6-
Request in Body-
Converted into below map using ML6-
ML8 -
Input and converted output is same. i.e. without map:
I suspect roxy router.xqy and request.xqy are creating some issues however not able to identify the issue yet.
Please suggest the fix for the same.
Other errors in Errorlog.txt-
Regards, Indrajeet