{"action":"created","issue":{"url":"https://api.github.com/repos/marklogic/roxy/issues/416","labels_url":"https://api.github.com/repos/marklogic/roxy/issues/416/labels{/name}","comments_url":"https://api.github.com/repos/marklogic/roxy/issues/416/comments","events_url":"https://api.github.com/repos/marklogic/roxy/issues/416/events","html_url":"https://github.com/marklogic/roxy/issues/416","id":69871472,"number":416,"title":"/roxy/rewrite.xqy broken with ML 8.0-2","user":{"login":"aajacobs","id":8723378,"avatar_url":"https://avatars.githubusercontent.com/u/8723378?v=3","gravatar_id":"","url":"https://api.github.com/users/aajacobs","html_url":"https://github.com/aajacobs","followers_url":"https://api.github.com/users/aajacobs/followers","following_url":"https://api.github.com/users/aajacobs/following{/other_user}","gists_url":"https://api.github.com/users/aajacobs/gists{/gist_id}","starred_url":"https://api.github.com/users/aajacobs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aajacobs/subscriptions","organizations_url":"https://api.github.com/users/aajacobs/orgs","repos_url":"https://api.github.com/users/aajacobs/repos","events_url":"https://api.github.com/users/aajacobs/events{/privacy}","received_events_url":"https://api.github.com/users/aajacobs/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/marklogic/roxy/labels/bug","name":"bug","color":"fc2929"},{"url":"https://api.github.com/repos/marklogic/roxy/labels/deployer","name":"deployer","color":"c7def8"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":3,"created_at":"2015-04-21T15:06:56Z","updated_at":"2015-04-21T17:40:20Z","closed_at":null,"body":"Roxy's /roxy/rewrite.xqy relies on a conf:rewrite function in ML server that was removed in the ML8.0-2 release.  As a result, any app using the /roxy/rewrite.xqy will begin showing error similar to the one below:\r\n```\r\n<error:error xsi:schemaLocation=\"http://marklogic.com/xdmp/error error.xsd\" xmlns:error=\"http://marklogic.com/xdmp/error\"\r\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\r\n  <error:code>XDMP-UNDFUN</error:code>\r\n  <error:name>err:XPST0017</error:name>\r\n  <error:xquery-version>1.0-ml</error:xquery-version>\r\n  <error:message>Undefined function</error:message>\r\n  <error:format-string>XDMP-UNDFUN: (err:XPST0017) Undefined function conf:rewrite()</error:format-string>\r\n  <error:retryable>false</error:retryable>\r\n  <error:expr> </error:expr>\r\n  <error:data>\r\n    <error:datum>conf:rewrite()</error:datum>\r\n  </error:data>\r\n  <error:stack>\r\n    <error:frame>\r\n      <error:line>7</error:line>\r\n      <error:column>9</error:column>\r\n      <error:operation>xdmp:eval(\"&amp;#10;        import module namespace conf = &amp;quot;http://marklog...\", (fn:QName(\"\",\"method\"), \"GET\", fn:QName(\"\",\"uri\"), ...))</error:operation>\r\n      <error:xquery-version>1.0-ml</error:xquery-version>\r\n    </error:frame>\r\n    <error:frame>\r\n      <error:uri>/roxy/rewrite.xqy</error:uri>\r\n      <error:line>40</error:line>\r\n      <error:column>6</error:column>\r\n      <error:variables>\r\n\t<error:variable>\r\n\t  <error:name xmlns=\"\">uri</error:name>\r\n\t  <error:value>\"/web/scripts/d3js/crossfilter.v1.min.js\"</error:value>\r\n\t</error:variable>\r\n\t<error:variable>\r\n\t  <error:name xmlns=\"\">method</error:name>\r\n\t  <error:value>\"GET\"</error:value>\r\n\t</error:variable>\r\n\t<error:variable>\r\n\t  <error:name xmlns=\"\">path</error:name>\r\n\t  <error:value>\"/web/scripts/d3js/crossfilter.v1.min.js\"</error:value>\r\n\t</error:variable>\r\n\t<error:variable>\r\n\t  <error:name xmlns=\"\">final-uri</error:name>\r\n\t  <error:value>()</error:value>\r\n\t</error:variable>\r\n      </error:variables>\r\n      <error:xquery-version>1.0-ml</error:xquery-version>\r\n    </error:frame>\r\n  </error:stack>\r\n</error:error>\r\n```\r\n\r\nGeert moved the functions missing from ML8 into the /roxy/rewrite.xqy file as a temporary fix, which I've verified is working with my app.  His new /roxy/rewrite.xqy file is below:\r\n\r\n```\r\nxquery version \"1.0-ml\";\r\n\r\nimport module namespace config = \"http://marklogic.com/roxy/config\" at \"/app/config/config.xqy\";\r\nimport module namespace def = \"http://marklogic.com/roxy/defaults\" at \"/roxy/config/defaults.xqy\";\r\nimport module namespace req = \"http://marklogic.com/roxy/request\" at \"/roxy/lib/request.xqy\";\r\n\r\ndeclare namespace rest = \"http://marklogic.com/appservices/rest\";\r\n\r\ndeclare option xdmp:mapping \"false\";\r\n\r\nlet $uri  := xdmp:get-request-url()\r\nlet $method := xdmp:get-request-method()\r\nlet $path := xdmp:get-request-path()\r\nlet $final-uri :=\r\n  req:rewrite(\r\n    $uri,\r\n    $path,\r\n    $method,\r\n    $config:ROXY-ROUTES)\r\nreturn\r\n  if ($final-uri) then $final-uri\r\n  else\r\n    try\r\n    {\r\n      xdmp:eval('\r\n      import module namespace conf = \"http://marklogic.com/rest-api/endpoints/config\"\r\n                at \"/MarkLogic/rest-api/endpoints/config.xqy\";\r\n      import module namespace rest = \"http://marklogic.com/appservices/rest\"\r\n        at \"/MarkLogic/appservices/utils/rest.xqy\";\r\n      import module namespace eput = \"http://marklogic.com/rest-api/lib/endpoint-util\"\r\n          at \"/MarkLogic/rest-api/lib/endpoint-util.xqy\";\r\n              declare variable $method external;\r\n              declare variable $uri external;\r\n              declare variable $path external;\r\n\r\n      (: This is a hack, but allows us to maintain one rule set endpoints and rewriter,\r\n          while pushing the finer-grained error handling to the endpoint level. :)\r\n      declare function conf:rewrite-rules(\r\n      ) as map:map\r\n      {\r\n          let $old-rules := eput:get-rest-options()\r\n          return\r\n              if (exists($old-rules))\r\n              then $old-rules\r\n              else\r\n                  let $all-methods := (\"GET\",\"POST\",\"PUT\",\"DELETE\",\"HEAD\",\"OPTIONS\")\r\n                  let $new-rules   := map:map()\r\n                  let $unsupported := map:map()\r\n                  return (\r\n                      for $rule in (\r\n                          conf:get-default-request-rule(),\r\n                          conf:get-config-indexes-request-rule(),\r\n                          conf:get-config-namespaces-item-request-rule(),\r\n                          conf:get-config-namespaces-request-rule(),\r\n                          conf:get-config-properties-request-rule(),\r\n                          conf:get-config-query-child-request-rule(),\r\n                          conf:get-config-query-list-request-rule(),\r\n                          conf:get-config-query-request-rule(),\r\n                          conf:get-document-query-rule(),\r\n                          conf:get-document-update-rule(),\r\n                          conf:get-keyvalue-list-request-rule(),\r\n                          conf:get-qbe-request-rule(),\r\n                          conf:get-ping-request-rule(),\r\n                          conf:get-rsrc-list-query-rule(),\r\n                          conf:get-rsrc-item-query-rule(),\r\n                          conf:get-rsrc-item-update-rule(),\r\n                          conf:get-rsrc-exec-query-rule(),\r\n                          conf:get-rsrc-exec-update-rule(),\r\n                          conf:get-search-query-request-rule(),\r\n                          conf:get-search-update-request-rule(),\r\n                          conf:get-tfm-list-request-rule(),\r\n                          conf:get-tfm-item-request-rule(),\r\n                          conf:get-txn-request-rule(),\r\n                          conf:get-values-request-rule(),\r\n                          conf:get-sparql-protocol-rule(),\r\n                          conf:get-graph-explore-rule(),\r\n                          conf:get-graphstore-protocol-rule(),\r\n                          conf:get-suggest-request-rule(),\r\n                          conf:get-rules-list-rule(),\r\n                          conf:get-alert-rules-item-rule(),\r\n                          conf:get-alert-match-rule(),\r\n                          conf:get-extlib-root-request-rule(),\r\n                          conf:get-extlib-request-rule()\r\n                          )\r\n                      let $endpoint   := $rule/@endpoint/string(.)\r\n                      (: Note: depends on document order in rule :)\r\n                      let $uri-params := $rule/rest:uri-param/@name/string(.)\r\n                      let $methods    := $rule/rest:http/@method/tokenize(string(.),\" \")\r\n                      for $match in $rule/@fast-match/tokenize(string(.), \"\\|\")\r\n                      return (\r\n                          for $method in $methods\r\n                          return map:put($new-rules, $method||$match, ($endpoint,$uri-params)),\r\n\r\n                          let $candidates :=\r\n                              let $candidate-methods := map:get($unsupported,$match)\r\n                              return\r\n                                  if (exists($candidate-methods))\r\n                                  then $candidate-methods\r\n                                  else $all-methods\r\n                          return map:put(\r\n                              $unsupported, $match, $candidates[not(. = $methods)]\r\n                              )\r\n                          ),\r\n\r\n                      for $match in map:keys($unsupported)\r\n                      for $method in map:get($unsupported,$match)\r\n                      return map:put(\r\n                          $new-rules,\r\n                          $method||$match,\r\n                          \"/MarkLogic/rest-api/endpoints/unsupported-method.xqy\"\r\n                          ),\r\n\r\n                      eput:set-rest-options($new-rules),\r\n                      $new-rules\r\n                      )\r\n      };\r\n\r\n      declare function conf:rewrite(\r\n          $method   as xs:string,\r\n          $uri      as xs:string,\r\n          $old-path as xs:string\r\n      ) as xs:string?\r\n      {\r\n          let $rules      := conf:rewrite-rules()\r\n          (: skip the empty step before the initial / :)\r\n          let $raw-steps  := subsequence(tokenize($old-path,\"/\"), 2)\r\n          let $raw-count  := count($raw-steps)\r\n          (: check for an empty step after a trailing / :)\r\n          let $extra-step := (subsequence($raw-steps,$raw-count,1) eq \"\")\r\n          let $step-count :=\r\n              if ($extra-step)\r\n              then $raw-count - 1\r\n              else $raw-count\r\n          let $steps      :=\r\n              if ($step-count eq 0)\r\n              then ()\r\n              else if ($extra-step)\r\n              then subsequence($raw-steps, 1, $step-count)\r\n              else $raw-steps\r\n          (: generate the key for lookup in the rules map :)\r\n          let $key        :=\r\n              (: no rule :)\r\n              if ($step-count eq 1)\r\n              then ()\r\n              (: default rule :)\r\n              else if ($step-count eq 0)\r\n              then \"\"\r\n              else\r\n                  let $first-step := subsequence($steps,1,1)\r\n                  return\r\n                  (: as in /content/help :)\r\n                  if ($first-step eq \"content\")\r\n                  then ($first-step,\"*\")\r\n                  else if (not($first-step = (\"v1\",\"LATEST\")))\r\n                  (: no rule :)\r\n                  then ()\r\n                  else\r\n                      let $second-step := subsequence($steps,2,1)\r\n                      return\r\n                      (: as in /v1/documents :)\r\n                      if ($step-count eq 2)\r\n                      then (\"*\",$second-step)\r\n                      else\r\n                          let $third-step := subsequence($steps,3,1)\r\n                          return\r\n                          if ($second-step = (\"ext\"))\r\n                          then\r\n                          (\"*\",$second-step,\"**\")\r\n                          else if ($second-step = (\"config\",\"alert\",\"graphs\")) then\r\n                              (: as in /v1/config/namespaces :)\r\n                              if ($step-count eq 3)\r\n                              then (\"*\", $second-step, $third-step)\r\n                              (: /v1/config/options/NAME or /v1/config/options/NAME/SUBNAME :)\r\n                              else if ($step-count le 5)\r\n                              then (\"*\", $second-step, $third-step, (4 to $step-count) ! \"*\")\r\n                              else ()\r\n                          (: as in /v1/transactions/TXID :)\r\n                          else if ($step-count eq 3)\r\n                          then (\"*\", $second-step, \"*\")\r\n                          (: catch all :)\r\n                          else if ($step-count le 5)\r\n                          then (\"*\", $second-step, $third-step, (4 to $step-count) ! \"*\")\r\n                          (: no rule :)\r\n                          else ()\r\n          let $key-method :=\r\n              if ($method eq \"POST\" and starts-with(\r\n                  head(xdmp:get-request-header(\"content-type\")), \"application/x-www-form-urlencoded\"\r\n                  ))\r\n              then \"GET\"\r\n              else $method\r\n          let $value :=\r\n              if (empty($key)) then ()\r\n              else map:get($rules, string-join(($key-method,$key), \"/\"))\r\n          let $value-count := count($value)\r\n          return\r\n              (: fallback :)\r\n              if ($value-count eq 0)\r\n              then $uri\r\n              else\r\n                  let $old-length := string-length($old-path)\r\n                  let $has-params := (string-length($uri) ne $old-length)\r\n                  let $new-path   :=\r\n                      (: append parameters to the rewritten path :)\r\n                      if ($has-params)\r\n                      then subsequence($value,1,1)||substring($uri,$old-length+1)\r\n                      else subsequence($value,1,1)\r\n                  return\r\n                      if ($value-count eq 1)\r\n                      then $new-path\r\n                      (: append parameters from rule to the rewritten path :)\r\n                      else string-join(\r\n                          (\r\n                              $new-path,\r\n                              let $step-names := subsequence($value,2)\r\n                              for $step-value at $i in\r\n                                  for $j in 2 to count($key)\r\n                                  let $place-holder := subsequence($key,$j,1)\r\n                                  return\r\n                                      if ($place-holder eq \"*\")\r\n                                      then subsequence($steps,$j,1)\r\n                                      else if ($place-holder eq \"**\")\r\n                                      (: using raw-steps picks up trailing slash :)\r\n                                      then string-join(subsequence($raw-steps,$j),\"/\")\r\n                                      else ()\r\n\r\n                              return (\r\n                                  if ($has-params or $i gt 1)\r\n                                      then \"&amp;amp;\"\r\n                                      else \"?\",\r\n                                  subsequence($step-names,$i,1),\r\n                                  \"=\",\r\n                                  $step-value\r\n                                  )\r\n                              ),\r\n                          \"\"\r\n                          )\r\n      };\r\n\r\n          (conf:rewrite($method, $uri, $path), $uri)[1]',\r\n          (xs:QName(\"method\"), $method,\r\n           xs:QName(\"uri\"), $uri,\r\n           xs:QName(\"path\"), $path))\r\n    }\r\n    catch($ex) {\r\n      if ($ex/error:code = \"XDMP-MODNOTFOUND\") then\r\n        $uri\r\n      else\r\n        xdmp:rethrow()\r\n    }\r\n```\r\n\r\n"},"comment":{"url":"https://api.github.com/repos/marklogic/roxy/issues/comments/94882864","html_url":"https://github.com/marklogic/roxy/issues/416#issuecomment-94882864","issue_url":"https://api.github.com/repos/marklogic/roxy/issues/416","id":94882864,"user":{"login":"grtjn","id":611397,"avatar_url":"https://avatars.githubusercontent.com/u/611397?v=3","gravatar_id":"","url":"https://api.github.com/users/grtjn","html_url":"https://github.com/grtjn","followers_url":"https://api.github.com/users/grtjn/followers","following_url":"https://api.github.com/users/grtjn/following{/other_user}","gists_url":"https://api.github.com/users/grtjn/gists{/gist_id}","starred_url":"https://api.github.com/users/grtjn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/grtjn/subscriptions","organizations_url":"https://api.github.com/users/grtjn/orgs","repos_url":"https://api.github.com/users/grtjn/repos","events_url":"https://api.github.com/users/grtjn/events{/privacy}","received_events_url":"https://api.github.com/users/grtjn/received_events","type":"User","site_admin":false},"created_at":"2015-04-21T17:40:20Z","updated_at":"2015-04-21T17:40:20Z","body":"I'll take a closer look.."}}