Skip to content
This repository has been archived by the owner on Nov 9, 2022. It is now read-only.

Commit

Permalink
Fixed #241 temporarily: added auto-creation of roxy-evaler REST-api, …
Browse files Browse the repository at this point in the history
…with exteval REST extension that replaces QConsole evaler endpoint, triggered with server-version=8ea
  • Loading branch information
grtjn authored and dmcassel committed Aug 28, 2014
1 parent e4d80a2 commit 220a54f
Show file tree
Hide file tree
Showing 3 changed files with 192 additions and 1 deletion.
5 changes: 5 additions & 0 deletions deploy/default.properties
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,8 @@ local-server=localhost
#
mlcp-home=/usr/local/mlcp
mlcp-vmargs=-Xmx512m

#
# Temporary fix to support MarkLogic 8 EA2
#
evaler-port=7998
118 changes: 118 additions & 0 deletions deploy/lib/ext/exteval.xqy
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
xquery version "1.0-ml";

module namespace ext = "http://marklogic.com/rest-api/resource/exteval";

import module namespace admin = "http://marklogic.com/xdmp/admin" at "/MarkLogic/admin.xqy";
import module namespace json = "http://marklogic.com/xdmp/json" at "/MarkLogic/json/json.xqy";

declare namespace roxy = "http://marklogic.com/roxy";
declare default function namespace "http://www.w3.org/2005/xpath-functions";

declare option xdmp:output "indent=yes";
declare option xdmp:output "indent-untyped=yes";
declare option xdmp:mapping "false";

(: One of $sid or $dbid must not be empty :)
declare function ext:get-eval-options(
$sid as xs:unsignedLong?,
$dbid as xs:unsignedLong?
) as element()
{
let $config := admin:get-configuration()
let $database-id :=
if ( $sid )
then ( admin:appserver-get-database( $config, $sid))
else if ( $dbid )
then ( $dbid )
else ( (: This REST framework doesn't return 400 :)
error((),"REQUIREDPARAM","sid or dbid") )
let $server-id :=
if ( $sid )
then ( $sid )
else ( xdmp:server() )
let $collation :=
try {
admin:appserver-get-collation($config, $server-id) }
catch ($ex) {
if ($ex/error:code eq 'SEC-PRIV')
then default-collation()
else xdmp:rethrow()
}
let $modules-id :=
try {
admin:appserver-get-modules-database($config, $server-id) }
catch ($ex) {
if ($ex/error:code eq 'SEC-PRIV')
then xdmp:modules-database()
else xdmp:rethrow()
}
let $xquery-version :=
try {
admin:appserver-get-default-xquery-version($config, $server-id) }
catch ($ex) {
if ($ex/error:code eq 'SEC-PRIV')
then 'app-server'
else xdmp:rethrow()
}
let $modules-root :=
try {
admin:appserver-get-root($config, $server-id) }
catch ($ex) {
if ($ex/error:code eq 'SEC-PRIV')
then xdmp:modules-root()
else xdmp:rethrow()
}
let $options :=
(: avoid setting options unless needed, for more flexible security :)
<options xmlns="xdmp:eval">{
if ($database-id eq xdmp:database()) then ()
else element database { $database-id },
if ($modules-id eq xdmp:modules-database()) then ()
else element modules { $modules-id },
if ($collation eq default-collation()) then ()
else element default-collation { $collation },
if ($xquery-version eq xdmp:xquery-version()) then ()
else element default-xquery-version { $xquery-version },
(: we should always have a root path, but better safe than sorry :)
if (empty($modules-root) or $modules-root eq xdmp:modules-root()) then ()
else element root { $modules-root },
element isolation { "different-transaction" },
if (xdmp:database-is-replica($database-id))
then element timestamp {xdmp:database-nonblocking-timestamp($database-id)}
else (),
element ignore-amps { "true" }
}</options>
return
$options
};

declare
%roxy:params("sid=xs:unsignedLong?", "dbid=xs:unsignedLong?", "querytype=xs:string?", "action=xs:string?")
function ext:post(
$context as map:map,
$params as map:map,
$input as document-node()*
) as document-node()*
{
map:put($context, "output-types", "application/json; charset=utf-8"),
document {
try {

let $sid := for $p in map:get($params, "sid") return xs:unsignedLong($p)
let $dbid := for $p in map:get($params, "dbid") return xs:unsignedLong($p)
let $query := $input

let $eval-opts := ext:get-eval-options($sid, $dbid)
let $results := xdmp:eval($query, (), $eval-opts)
return
xdmp:to-json($results),

xdmp:set-response-code(200, "OK")

} catch ($e) {
xdmp:log($e),
xdmp:set-response-code(500, $e/error:format-string),
json:transform-to-json(<error>{ $e/error:format-string }</error>, json:config("custom"))
}
}
};
70 changes: 69 additions & 1 deletion deploy/lib/server_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,9 @@ def execute_query(query, properties = {})
r = execute_query_4 query, properties
elsif @server_version == 5 || @server_version == 6
r = execute_query_5 query, properties
# temporary fix to support MarkLogic 8 EA2
elsif @properties["ml.server-version"] == "8ea"
r = execute_query_8ea query, properties
else
r = execute_query_7 query, properties
end
Expand Down Expand Up @@ -390,7 +393,7 @@ def config
def bootstrap
raise ExitException.new("Bootstrap requires the target environment's hostname to be defined") unless @hostname.present?

logger.info "Bootstrapping your project into MarkLogic on #{@hostname}..."
logger.info "Bootstrapping your project into MarkLogic #{@properties['ml.server-version']} on #{@hostname}..."
setup = File.read(ServerConfig.expand_path("#{@@path}/lib/xquery/setup.xqy"))
r = execute_query %Q{#{setup} setup:do-setup(#{get_config})}
logger.debug "code: #{r.code.to_i}"
Expand Down Expand Up @@ -1446,6 +1449,71 @@ def execute_query_7(query, properties = {})
r
end

# temporary fix to support MarkLogic 8 EA2
def execute_query_8ea(query, properties = {})
# check for presense of temporary Roxy exteval extension
r = go("http#{@use_https ? 's' : ''}://#{@hostname}:8002/v1/rest-apis", "get")
if ! r.body.match(/roxy-evaler/)
logger.info "\nDeploying Roxy evaler.."
r = go("http#{@use_https ? 's' : ''}://#{@hostname}:8002/v1/rest-apis",
"post",
{"Content-Type" => "application/xml"},
nil,
"<rapi:rest-api xmlns:rapi='http://marklogic.com/rest-api'>
<rapi:name>roxy-evaler</rapi:name>
<rapi:group>Default</rapi:group>
<rapi:database>Documents</rapi:database>
<rapi:modules-database>Modules</rapi:modules-database>
<rapi:port>#{@properties['ml.evaler-port']}</rapi:port>
</rapi:rest-api>")
roxyRest = Roxy::MLRest.new({
:user_name => @ml_username,
:password => @ml_password,
:server => @hostname,
:app_port => 7998,
:rest_port => 7998,
:logger => @logger,
:auth_method => @properties["ml.authentication-method"]
})
roxyRest.install_extensions(ServerConfig.expand_path("#{@@path}/lib/ext/"))
end

# We need a context for this query. Here's what we look for, in order of preference:
# 1. A caller-specified database
# 2. A caller-specified application server
# 3. An application server that is present by default
# 4. Any database
if properties[:db_name] != nil
db_id = get_db_id(properties[:db_name])
elsif properties[:app_name] != nil
sid = get_sid(properties[:app_name])
else
sid = get_sid("Manage")
end

db_id = get_any_db_id if db_id.nil? && sid.nil?

if db_id.present?
logger.debug "using dbid: #{db_id}"
r = go("http#{@use_https ? 's' : ''}://#{@hostname}:#{@properties['ml.evaler-port']}/v1/resources/exteval?rs:dbid=#{db_id}",
"post",
{'Content-type' => 'application/xquery'},
nil,
query)
else
logger.debug "using sid: #{sid}"
r = go("http#{@use_https ? 's' : ''}://#{@hostname}:#{@properties['ml.evaler-port']}/v1/resources/exteval?rs:sid=#{sid}",
"post",
{'Content-type' => 'application/xquery'},
nil,
query)
end

raise ExitException.new(JSON.pretty_generate(JSON.parse(r.body))) if r.body.match(/\{"error"/)

r
end

def ServerConfig.substitute_properties(sub_me, with_me, prefix = "")
dangling_vars = {}
begin
Expand Down

0 comments on commit 220a54f

Please sign in to comment.