1

I'm looking at this example from: https://docs.marklogic.com/10.0/rest:matching-request

Specifically, this code:

  xquery version "1.0-ml"; 
 
  import module namespace rest="http://marklogic.com/appservices/rest"
  at "/MarkLogic/appservices/utils/rest.xqy";

  declare option xdmp:mapping "false";

  let $options :=
    <options xmlns="http://marklogic.com/appservices/rest">
      <request uri="^/shakespeare/(.+)/(.+)" endpoint="/redirect.xqy">
        <uri-param name="__ml_redirect__">/$1/$2</uri-param>
      </request>
      <request uri="^/shakespeare/(.+)" endpoint="/redirect.xqy">
        <uri-param name="__ml_redirect__">/$1</uri-param>
      </request>
      <request uri="^/(.+)/act(\d+)" endpoint="/endpoint.xqy">
        <uri-param name="play">$1.xml</uri-param>
        <uri-param name="act" as="integer">$2</uri-param>
        <param name="scene" as="integer" values="1|2|3" default="1"/>
      </request>
      <request uri="^/(.+)$" endpoint="/endpoint.xqy">
        <uri-param name="play">$1.xml</uri-param>
      </request>
      <request uri="^/(.+)$" endpoint="/delete.xqy">
        <http method="DELETE"/>
        <param name="reason" required="true"/>
      </request>
      <request uri="^(.+)$" endpoint="/options.xqy" user-params="allow">
        <uri-param name="__ml_options__">$1</uri-param>
        <http method="OPTIONS"/>
      </request>
    </options>

  let $uri     := "/shakespeare/hamlet"
  let $accept  := xdmp:get-request-header("Accept") 
  let $params  := map:map()

  return rest:matching-request($options, ("uri","method"))

Now, I would have thought that the match would be:

  <request uri="^/shakespeare/(.+)" endpoint="/redirect.xqy">
    <uri-param name="__ml_redirect__">/$1</uri-param>
  </request>

But, instead, the match is:

  <request uri="^/(.+)$" endpoint="/endpoint.xqy">
    <uri-param name="play">$1.xml</uri-param>
  </request>

The reason that I'm asking is that I would like to come up with a rule for this expression, which is actually good regex: ^(\/openapi\/.+.yml)$

However, for 1) that regex is invalid according to MarkLogic, but ^(/openapi/.+.yml)$ is valid, and 2) if /shakespeare/hamlet doesn't match ^/shakespeare/(.+)/(.+), then I don't understand what the regex is supposed to be that would match it.

Mads Hansen
  • 63,927
  • 12
  • 112
  • 147

1 Answers1

1

I believe the issue is that when you are executing the code to test in Query Console, it is using the URL of your Query Console request: /qconsole/endpoints/evaler.xqy.

The code example is a little confusing, because it has let $uri := "/shakespeare/hamlet", but that $uri is not used. I believe the intent was to show you what the request URI would be, and then demonstrate how the rest:matching-request() function would behave.

You can verify that the URI is indeed /qconsole/endpoints/evaler.xqy by executing this:

import module namespace rest="http://marklogic.com/appservices/rest"
  at "/MarkLogic/appservices/utils/rest.xqy";
let $options :=
  <options xmlns="http://marklogic.com/appservices/rest">
    <request uri="/qconsole/endpoints/evaler.xqy" endpoint="/redirect.xqy">
      <uri-param name="__ml_redirect__">/QUERY-CONSOLE-TEST</uri-param>
    </request>
  </options>
return rest:matching-request($options, "uri")

Also, if you configure an invalid regex, the error message for the matches() will reveal what the actual URL is:

import module namespace rest="http://marklogic.com/appservices/rest"
  at "/MarkLogic/appservices/utils/rest.xqy";
let $options :=
  <options xmlns="http://marklogic.com/appservices/rest">
    <request uri="\/" endpoint="/redirect.xqy">
      <uri-param name="__ml_redirect__">INVALID-REGEX-TEST</uri-param>
    </request>
  </options>
return rest:matching-request($options, "uri")

returns:

[1.0-ml] XDMP-REGEX: (err:FORX0002) fn:matches("/qconsole/endpoints/evaler.xqy", attribute{fn:QName("","uri")}{"/"}) -- Invalid regular expression

You can look under the covers and see how the rest module calls rest-impl.xqy methods and perform tests using the implementation functions:

declare namespace rest="http://marklogic.com/appservices/rest"; 
import module namespace rest-impl="http://marklogic.com/appservices/rest-impl"
  at  "/MarkLogic/appservices/utils/rest-impl.xqy";
let $options :=
  <options xmlns="http://marklogic.com/appservices/rest">
    <request uri="^(/openapi/.+.yml)$" endpoint="/redirect.xqy"/>
  </options>
  
let $reqenv := rest-impl:request-environment()
let $_ := map:put($reqenv, "uri", "/openapi/foo.yml") 
return
  rest-impl:matching-request-wrapper($options/rest:request, $reqenv, "uri")
Mads Hansen
  • 63,927
  • 12
  • 112
  • 147
  • Ok, but I guess that means that it's just trial and error with the regex since the check-options apparently doesn't actually check the validity of the regex: try { rest:check-options( $1 ) } catch ($e) { rest:report-error($e) } – David Steiner Dec 29 '22 at 18:32
  • It will use `matches()`, so if you are just testing your regex, you can test that directly. However, if you want to perform a more realistic test of the rewriter implementation, that might leverage uri and method, for instance - then you could test the implementation functions. I have updated with an example of how to do that. – Mads Hansen Dec 29 '22 at 19:08