From dfb2c2dd1ab9b76a54ae4d193f227ff3796bd8c9 Mon Sep 17 00:00:00 2001 From: Phil Barber Date: Thu, 5 Oct 2023 16:11:51 -0400 Subject: [PATCH] DEVEXP-578: Submit eval request DEVEXP-578: Submit eval request DEVEXP-578: Submit eval request --- marklogic/client.py | 7 + marklogic/eval.py | 138 ++++++++++++++++++ marklogic/rows.py | 16 +-- test-app/src/main/ml-data/musicians/logo.png | Bin 0 -> 20778 bytes tests/test_eval.py | 143 ++++++++++++++++--- 5 files changed, 278 insertions(+), 26 deletions(-) create mode 100644 marklogic/eval.py create mode 100644 test-app/src/main/ml-data/musicians/logo.png diff --git a/marklogic/client.py b/marklogic/client.py index 6c6f912..61e23ab 100644 --- a/marklogic/client.py +++ b/marklogic/client.py @@ -3,6 +3,7 @@ from marklogic.documents import DocumentManager from marklogic.rows import RowManager from marklogic.transactions import TransactionManager +from marklogic.eval import EvalManager from requests.auth import HTTPDigestAuth from urllib.parse import urljoin @@ -84,3 +85,9 @@ def transactions(self): if not hasattr(self, "_transactions"): self._transactions = TransactionManager(self) return self._transactions + + @property + def eval(self): + if not hasattr(self, "_eval"): + self._eval = EvalManager(self) + return self._eval diff --git a/marklogic/eval.py b/marklogic/eval.py new file mode 100644 index 0000000..07f741a --- /dev/null +++ b/marklogic/eval.py @@ -0,0 +1,138 @@ +import json + +from decimal import Decimal +from marklogic.documents import Document +from requests import Session +from requests_toolbelt.multipart.decoder import MultipartDecoder + +""" +Defines an EvalManager class to simplify usage of the "/v1/eval" REST +endpoint defined at https://docs.marklogic.com/REST/POST/v1/eval. +""" + + +class EvalManager: + """ + Provides a method to simplify sending an XQuery or + JavaScript eval request to the eval endpoint. + """ + + def __init__(self, session: Session): + self._session = session + + def xquery( + self, xquery: str, vars: dict = None, return_response: bool = False, **kwargs + ): + """ + Send an XQuery script to MarkLogic via a POST to the endpoint + defined at https://docs.marklogic.com/REST/POST/v1/eval. + + :param xquery: an XQuery string + :param vars: a dict containing variables to include + :param return_response: boolean specifying if the entire original response + object should be returned (True) or if only the data should be returned (False) + upon a success (2xx) response. Note that if the status code of the response is + not 2xx, then the entire response is always returned. + """ + if xquery is None: + raise ValueError("No script found; must specify a xquery") + return self.__send_request({"xquery": xquery}, vars, return_response, **kwargs) + + def javascript( + self, + javascript: str, + vars: dict = None, + return_response: bool = False, + **kwargs + ): + """ + Send a JavaScript script to MarkLogic via a POST to the endpoint + defined at https://docs.marklogic.com/REST/POST/v1/eval. + + :param javascript: a JavaScript string + :param vars: a dict containing variables to include + :param return_response: boolean specifying if the entire original response + object should be returned (True) or if only the data should be returned (False) + upon a success (2xx) response. Note that if the status code of the response is + not 2xx, then the entire response is always returned. + """ + if javascript is None: + raise ValueError("No script found; must specify a javascript") + return self.__send_request( + {"javascript": javascript}, vars, return_response, **kwargs + ) + + def __send_request( + self, data: dict, vars: dict = None, return_response: bool = False, **kwargs + ): + """ + Send a script (XQuery or javascript) and possibly a dict of vars + to MarkLogic via a POST to the endpoint defined at + https://docs.marklogic.com/REST/POST/v1/eval. + """ + if vars is not None: + data["vars"] = json.dumps(vars) + response = self._session.post("v1/eval", data=data, **kwargs) + return ( + self.__process_response(response) + if response.status_code == 200 and not return_response + else response + ) + + def __process_response(self, response): + """ + Process a multipart REST response by putting them in a list and + transforming each part based on the "X-Primitive" header. + """ + if "Content-Length" in response.headers: + return None + + parts = MultipartDecoder.from_response(response).parts + transformed_parts = [] + for part in parts: + encoding = part.encoding + primitive_header = part.headers["X-Primitive".encode(encoding)].decode( + encoding + ) + primitive_function = EvalManager.__primitive_value_converters.get( + primitive_header + ) + if primitive_function is not None: + transformed_parts.append(primitive_function(part)) + else: + transformed_parts.append(part.text) + return transformed_parts + + __primitive_value_converters = { + "integer": lambda part: int(part.text), + "decimal": lambda part: Decimal(part.text), + "boolean": lambda part: ("False" == part.text), + "string": lambda part: part.text, + "map": lambda part: json.loads(part.text), + "element()": lambda part: part.text, + "array": lambda part: json.loads(part.text), + "array-node()": lambda part: json.loads(part.text), + "object-node()": lambda part: EvalManager.__process_object_node_part(part), + "document-node()": lambda part: EvalManager.__process_document_node_part(part), + "binary()": lambda part: Document( + EvalManager.__get_decoded_uri_from_part(part), part.content + ), + } + + def __get_decoded_uri_from_part(part): + encoding = part.encoding + return part.headers["X-URI".encode(encoding)].decode(encoding) + + def __process_object_node_part(part): + if b"X-URI" in part.headers: + return Document( + EvalManager.__get_decoded_uri_from_part(part), json.loads(part.text) + ) + else: + return json.loads(part.text) + + def __process_document_node_part(part): + if b"X-URI" in part.headers: + return Document(EvalManager.__get_decoded_uri_from_part(part), part.text) + else: + return part.text diff --git a/marklogic/rows.py b/marklogic/rows.py index c4fd7fa..1b3abb8 100644 --- a/marklogic/rows.py +++ b/marklogic/rows.py @@ -3,24 +3,23 @@ """ Defines a RowManager class to simplify usage of the "/v1/rows" & "/v1/rows/graphql" REST -endpoints defined at https://docs.marklogic.com/REST/POST/v1/rows/graphql +endpoints defined at https://docs.marklogic.com/REST/POST/v1/rows/graphql. """ class RowManager: """ - Provides a method to simplify sending a GraphQL request to the GraphQL rows endpoint. + Provides a method to simplify sending a GraphQL + request to the GraphQL rows endpoint. """ def __init__(self, session: Session): self._session = session - def graphql( - self, graphql_query: str, return_response: bool = False, *args, **kwargs - ): + def graphql(self, graphql_query: str, return_response: bool = False, **kwargs): """ Send a GraphQL query to MarkLogic via a POST to the endpoint defined at - https://docs.marklogic.com/REST/POST/v1/rows/graphql + https://docs.marklogic.com/REST/POST/v1/rows/graphql. :param graphql_query: a GraphQL query string. Note - this is the query string only, not the entire query JSON object. See the following for more information: @@ -69,18 +68,17 @@ def query( sparql: str = None, format: str = "json", return_response: bool = False, - *args, **kwargs ): """ Send a query to MarkLogic via a POST to the endpoint defined at - https://docs.marklogic.com/REST/POST/v1/rows + https://docs.marklogic.com/REST/POST/v1/rows. Just like that endpoint, this function can be used for four different types of queries: Optic DSL, Serialized Optic, SQL, and SPARQL. The type of query processed by the function is dependent upon the parameter used in the call to the function. For more information about Optic and using the Optic DSL, SQL, and SPARQL, - see https://docs.marklogic.com/guide/app-dev/OpticAPI + see https://docs.marklogic.com/guide/app-dev/OpticAPI. If multiple query parameters are passed into the call, the function uses the query parameter that is first in the list: dsl, plan, sql, sparql. diff --git a/test-app/src/main/ml-data/musicians/logo.png b/test-app/src/main/ml-data/musicians/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c2a0d50d6a463480d038a184fed41c791b1ff8a7 GIT binary patch literal 20778 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%0A3d+cqGkWS*3GeS$&pnf+v&_=JL(R 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 literal 0 HcmV?d00001 diff --git a/tests/test_eval.py b/tests/test_eval.py index ba2c634..bc27f10 100644 --- a/tests/test_eval.py +++ b/tests/test_eval.py @@ -1,24 +1,133 @@ +import decimal + +from marklogic.documents import Document from requests_toolbelt.multipart.decoder import MultipartDecoder +from pytest import raises -def test_eval(client): - """ - This shows how a user would do an eval today. It's a good example of how a multipart/mixed - response is a little annoying to deal with, as it requires using the requests_toolbelt - library and a class called MultipartDecoder. +def test_xquery_common_primitives(client): + parts = client.eval.xquery( + """( + 'A', 1, 1.1, fn:false(), fn:doc('/musicians/logo.png')) + """ + ) + __verify_common_primitives(parts) - Client support for this might look like this: - response = client.eval.xquery("world") - And then it's debatable whether we want to do anything beyond what MultipartDecoder - is doing for handling the response. - """ - response = client.post( - "v1/eval", - headers={"Content-type": "application/x-www-form-urlencoded"}, - data={"xquery": "world"}, +def test_javascript_common_primitives(client): + parts = client.eval.javascript( + """xdmp.arrayValues([ + 'A', 1, 1.1, false, fn.doc('/musicians/logo.png') + ])""" + ) + __verify_common_primitives(parts) + + +def test_xquery_specific_primitives(client): + parts = client.eval.xquery( + """( + world, + object-node {'A': 'a'}, + fn:doc('/doc2.xml'), + document {}, + array-node {1, "23", 4} + )""" ) + assert type(parts[0]) is str + assert "world" == parts[0] + assert type(parts[1]) is dict + assert {"A": "a"} == parts[1] + assert type(parts[2]) is Document + assert "/doc2.xml" == parts[2].uri + assert "world" in parts[2].content + assert type(parts[3]) is str + assert '\n' == parts[3] + assert type(parts[4]) is list + assert "23" == parts[4][1] + assert 3 == len(parts[4]) + + +def test_javascript_specific_primitives(client): + parts = client.eval.javascript( + """xdmp.arrayValues([ + {'A': 'a'}, + ['Z', 'Y', 1], + fn.head(cts.search('Armstrong')) + ])""" + ) + assert type(parts[0]) is dict + assert {"A": "a"} == parts[0] + assert type(parts[1]) is list + assert "Z" == parts[1][0] + assert 3 == len(parts[1]) + assert type(parts[2]) is Document + assert "/musicians/musician1.json" == parts[2].uri + assert { + "musician": { + "lastName": "Armstrong", + "firstName": "Louis", + "dob": "1901-08-04", + "instrument": ["trumpet", "vocal"], + } + } == parts[2].content + + +def test_javascript_noquery(client): + with raises(ValueError, match="No script found; must specify a javascript"): + client.eval.javascript(None) + + +def test_xquery_noquery(client): + with raises(ValueError, match="No script found; must specify a xquery"): + client.eval.xquery(None) + + +def test_xquery_with_return_response(client): + response = client.eval.xquery("('A', 1, 1.1, fn:false())", return_response=True) + assert 200 == response.status_code + parts = MultipartDecoder.from_response(response).parts + assert 4 == len(parts) + + +def test_xquery_vars(client): + vars = {"word1": "hello", "word2": "world"} + script = """ + xquery version "1.0-ml"; + declare variable $word1 as xs:string external; + declare variable $word2 as xs:string external; + fn:concat($word1, " ", $word2) + """ + parts = client.eval.xquery(script, vars) + assert type(parts[0]) is str + assert "hello world" == parts[0] + + +def test_javascript_vars(client): + vars = {"word1": "hello", "word2": "world"} + parts = client.eval.javascript("xdmp.arrayValues([word1, word2])", vars) + assert type(parts[0]) is str + assert "hello" == parts[0] + + +def test_xquery_empty_sequence(client): + parts = client.eval.xquery("()") + assert parts is None + + +def test_javascript_script(client): + parts = client.eval.javascript("[]") + assert [[]] == parts + - decoder = MultipartDecoder.from_response(response) - content = decoder.parts[0].text - assert "world" == content +def __verify_common_primitives(parts): + assert type(parts[0]) is str + assert "A" == parts[0] + assert type(parts[1]) is int + assert 1 == parts[1] + assert type(parts[2]) is decimal.Decimal + assert decimal.Decimal("1.1") == parts[2] + assert type(parts[3]) is bool + assert parts[3] is False + assert type(parts[4]) is Document + assert "/musicians/logo.png" == parts[4].uri + assert b"PNG" in parts[4].content