-
Notifications
You must be signed in to change notification settings - Fork 66
Working with the REST API
Roxy 1.3 and later allow you to make use of MarkLogic's REST API if a few different ways. You can read more about the Available Application types if interested. Below more details on how to make use of the REST api.
MarkLogic's REST API relies on a set of options to tell it how to search; MarkLogic has a REST API Developer's Guide for details on how to use the options. The REST API code expects to find its options in the modules database at /{group-name}/{appserver-name}/rest-api/.... To make sure your options are put in the right place, put them in your {project-home}/rest-api/config/. You will need to create these, although you can get examples by generating an Application Builder project and looking in the /{group-name}/{appserver-name}/rest-api/ directory in the modules database that it generates. Put all contents (recursively) of that directory into your {project-home}/rest-api/config/ directory and the deployer will make sure it goes to the right place.
Example contents:
$ cd {project-home}
$ find rest-api/config
rest-api/config
rest-api/config/options
rest-api/config/options/all.xml
rest-api/config/options/default.xml
rest-api/config/properties.xml
After running "ml {env} deploy modules", you can test that the REST API was set up correctly by pointing a browser to http://{server}:{port}/v1/search?options=all (assuming the presence of {project-home}/rest-api/config/options/all.xml. If you get a search:response element back, all's well. If you got an error, then the all.xml options file did not get where it needs to be.
If you to put the options somewhere else, copy the rest-options.dir property from deploy/default.properties to deploy/build.properties and set it to the location of your rest-api directory.
Do not put rest-api under your source directory. It will get deployed with your source code along with everything else, but won't be in the right place, and you'll end up with two sets of files. That could be confusing.
Do not try to get the rest-api into the right place by putting it under src/{group-name}/{appserver-name}/rest-api. If you do, it should work fine -- until the first time you deploy it to a a different group, or to an app server with a different name. The deployer picks these up from your deploy/*.properties files and makes sure everything goes where it needs to be.
The MarkLogic REST API can be configured to meet many needs, but sometimes, you need to write an extension. As of Roxy 1.4, the deployer will deploy extensions in a specified directory. Deploying extensions typically requires running a curl command that includes the names of the supported HTTP verbs and the function parameters as HTTP parameters. Consider this module:
xquery version "1.0-ml";
module namespace ez = "http://marklogic.com/rest-api/resource/easy";
declare namespace roxy = "http://marklogic.com/roxy";
(:
: Just writes the one parameter to the log file.
:)
declare
%roxy:params("thing1=xs:string", "thing2=xs:string")
function ez:get(
$context as map:map,
$params as map:map
) as document-node()*
{
xdmp:log("ez:get: " || map:get($params, "thing1") || "; " || map:get($params, "thing2"))
};
Note the use of XQuery annotations, a new feature in MarkLogic 6 (backward compatibility isn't an issue, because this is only relevant to the REST API, which also depends on ML6). More on that in a moment.
Assuming you have this file in ./rest-api/ext/easy.xqy, the MarkLogic-documented process to deploy this extension to a REST API app server on localhost:8020 is:
curl --anyauth --user admin:admin -X PUT -H "Content-type: application/xquery" \
-d@"./rest-api/ext/easy.xqy" \
"http://localhost:8020/v1/config/resources/easy?method=get&get:thing1=xs:string&get:thing2=xs:string"
In Roxy 1.4+, you can specify a rest-ext.dir property (default value: "rest-ext.dir=${basedir}/rest-api/ext"). Put your extension modules there, and the "ml {env} deploy modules" command will construct and execute the HTTP PUT command needed.
In order to build the correct parameters, your module needs to make clear what the parameters are. Take a look at the XQuery annotations in the code:
%roxy:params("thing1=xs:string", "thing2=xs:string")
Roxy will look for annotations of this form as part of the function declarations. The deployer doesn't actually pay attention to the namespace of the annotation; it's looking specifically for "%roxy:params". Inside the annotation, list each parameter and its type.
When calling your new endpoint, don't forget the rs: prefix on the parameters, as required by MarkLogic:
http://localhost:8020/v1/resources/easy?rs:thing1=foo&rs:thing2=bar
Roxy will create the skeleton of an extension for you.
$ ml extend app:tag
This will create a module called tag.xqy in the directory pointed to by your rest-ext.dir property. "tag" will also be used in the module's namespace, as needed by MarkLogic. The optional prefix will be used as the prefix for the module functions.
Put any library modules that your extensions need under your src directory (src/app/models/ for hybrid apps). The deployer will upload the contents of src normally.
Roxy can also manage and deploy REST API content transformations. This is handled similarly to the REST API Service Extensions, except that the XQuery and/or XSLT files representing the transforms should be maintained in the ./rest-api/transforms directory (if you wish to change the default location of this directory, you can change it with the rest-transforms.dir property in the properties files).
If your content transformation is defined as an XQuery module, you'll need to include the same XQuery annotation (%roxy:params()) that defines the transform's expected parameters. The name of the XQuery module must match the last part of the namespace. In the example, the transformation must be in add-attr.xqy.
xquery version "1.0-ml";
module namespace example = "http://marklogic.com/rest-api/transform/add-attr";
declare namespace roxy = "http://marklogic.com/roxy";
declare
%roxy:params("uri=xs:string", "priority=xs:int")
function example:transform(
$context as map:map,
$params as map:map,
$content as document-node()
) as document-node()
{
if (fn:empty($content/*)) then $content
else
let $value := (map:get($params,"value"),"UNDEFINED")[1]
let $name := (map:get($params, "name"), "transformed")[1]
let $root := $content/*
return document {
$root/preceding-sibling::node(),
element {fn:name($root)} {
attribute { fn:QName("", $name) } {$value},
$root/@*,
$root/node()
},
$root/following-sibling::node()
}
};
Content transformations can also be defined as XSL transforms. XSL transforms also require expected parameters to be defined in the file but since XSL isn't XQuery and thus doesn't support XQuery annotations, you can specify these in the same format but within an XML comment:
<!-- %roxy:params("uri=xs:string", "priority=xs:int") -->
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:example="http://marklogic.com/rest-api/example/transform"
xmlns:map="http://marklogic.com/xdmp/map">
<xsl:param name="context" as="map:map"/>
<xsl:param name="params" as="map:map"/>
<xsl:template match="/*">
<xsl:copy>
<xsl:attribute
name='{(map:get($params,"name"),"transformed")[1]}'
select='(map:get($params,"value"),"UNDEFINED")[1]'/>
<xsl:copy-of select="@*"/>
<xsl:copy-of select="node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
For both XQuery and XSLT content transformations, the basename of the file must reflect the desired name of the content transformation as defined in MarkLogic -- when you choose to apply a content transformation using one of the REST API endpoints that support it, you must specify a parameter named 'transform' with a parameter value equal to the name of the transform you wish to apply. A content transformation defined as an XQuery module named add-attr.xqy will be deployed as a content transformation named add-attr.
Roxy will create the skeleton of an transformation for you.
$ ml transform app:tag
This will create a file called tag.xslt in the directory pointed to by your rest-transform.dir property. "tag" will also be used in the module's namespace, as needed by MarkLogic. The optional prefix will be used as the namespace prefix. To create an XQuery transform:
$ ml transform app:tag xqy
This will create a library module called tag.xqy. The optional prefix will be used as the namespace prefix for the library functions.
If you look at the code generated by Application Builder, you will see that REST API extensions and transformations get put into the "http://marklogic.com/extension/plugin" collection. The Roxy deployer will do that for source code under src/marklogic.rest.*, which is where this code will need to be for the REST API to find it. To see examples of this, take a look at a modules database generated by Application Builder.