11import json
22import requests
33
4- from decimal import Decimal
54from marklogic .cloud_auth import MarkLogicCloudAuth
6- from marklogic .documents import Document , DocumentManager
7- from marklogic .eval import EvalManager
5+ from marklogic .documents import DocumentManager
6+ from marklogic .impl . eval import process_multipart_mixed_response
87from marklogic .rows import RowManager
98from marklogic .transactions import TransactionManager
109from requests .auth import HTTPDigestAuth
11- from requests_toolbelt .multipart .decoder import MultipartDecoder
1210from urllib .parse import urljoin
1311
1412
@@ -72,6 +70,61 @@ def prepare_request(self, request, *args, **kwargs):
7270 request .url = urljoin (self .base_url , request .url )
7371 return super (Client , self ).prepare_request (request , * args , ** kwargs )
7472
73+ @property
74+ def documents (self ):
75+ if not hasattr (self , "_documents" ):
76+ self ._documents = DocumentManager (session = self )
77+ return self ._documents
78+
79+ @property
80+ def rows (self ):
81+ if not hasattr (self , "_rows" ):
82+ self ._rows = RowManager (session = self )
83+ return self ._rows
84+
85+ @property
86+ def transactions (self ):
87+ if not hasattr (self , "_transactions" ):
88+ self ._transactions = TransactionManager (session = self )
89+ return self ._transactions
90+
91+ def eval (
92+ self ,
93+ javascript : str = None ,
94+ xquery : str = None ,
95+ vars : dict = None ,
96+ return_response : bool = False ,
97+ ** kwargs ,
98+ ):
99+ """
100+ Send a script to MarkLogic via a POST to the endpoint
101+ defined at https://docs.marklogic.com/REST/POST/v1/eval. Must define either
102+ 'javascript' or 'xquery'.
103+
104+ :param javascript: a JavaScript script
105+ :param xquery: an XQuery script
106+ :param vars: a dict containing variables to include
107+ :param return_response: boolean specifying if the entire original response
108+ object should be returned (True) or if only the data should be returned (False)
109+ upon a success (2xx) response. Note that if the status code of the response is
110+ not 2xx, then the entire response is always returned.
111+ """
112+ data = {}
113+ if javascript :
114+ data = {"javascript" : javascript }
115+ elif xquery :
116+ data = {"xquery" : xquery }
117+ else :
118+ raise ValueError ("Must define either 'javascript' or 'xquery' argument." )
119+ if vars :
120+ data ["vars" ] = json .dumps (vars )
121+ response = self .post ("v1/eval" , data = data , ** kwargs )
122+ return (
123+ process_multipart_mixed_response (response )
124+ if response .status_code == 200 and not return_response
125+ else response
126+ )
127+
75128 def invoke (
76129 self , module : str , vars : dict = None , return_response : bool = False , ** kwargs
77130 ):
@@ -88,97 +141,11 @@ def invoke(
88141 not 2xx, then the entire response is always returned.
89142 """
90143 data = {"module" : module }
91- if vars is not None :
144+ if vars :
92145 data ["vars" ] = json .dumps (vars )
93146 response = self .post ("v1/invoke" , data = data , ** kwargs )
94147 return (
95- self . process_multipart_mixed_response (response )
148+ process_multipart_mixed_response (response )
96149 if response .status_code == 200 and not return_response
97150 else response
98151 )
99-
100- def process_multipart_mixed_response (self , response ):
101- """
102- Process a multipart REST response by putting them in a list and
103- transforming each part based on the "X-Primitive" header.
104-
105- :param response: The original multipart/mixed response from a call to a
106- MarkLogic server.
107- """
108- if "Content-Length" in response .headers :
109- return None
110-
111- parts = MultipartDecoder .from_response (response ).parts
112- transformed_parts = []
113- for part in parts :
114- encoding = part .encoding
115- header = part .headers ["X-Primitive" .encode (encoding )].decode (encoding )
116- primitive_function = Client .__primitive_value_converters .get (header )
117- if primitive_function is not None :
118- transformed_parts .append (primitive_function (part ))
119- else :
120- # Return the binary created by requests_toolbelt so we don't get an
121- # error trying to convert it to something else.
122- transformed_parts .append (part .content )
123- return transformed_parts
124-
125- @property
126- def documents (self ):
127- if not hasattr (self , "_documents" ):
128- self ._documents = DocumentManager (session = self )
129- return self ._documents
130-
131- @property
132- def rows (self ):
133- if not hasattr (self , "_rows" ):
134- self ._rows = RowManager (session = self )
135- return self ._rows
136-
137- @property
138- def transactions (self ):
139- if not hasattr (self , "_transactions" ):
140- self ._transactions = TransactionManager (session = self )
141- return self ._transactions
142-
143- @property
144- def eval (self ):
145- if not hasattr (self , "_eval" ):
146- self ._eval = EvalManager (session = self )
147- return self ._eval
148-
149- __primitive_value_converters = {
150- "integer" : lambda part : int (part .text ),
151- "decimal" : lambda part : Decimal (part .text ),
152- "boolean" : lambda part : ("False" == part .text ),
153- "string" : lambda part : part .text ,
154- "map" : lambda part : json .loads (part .text ),
155- "element()" : lambda part : part .text ,
156- "array" : lambda part : json .loads (part .text ),
157- "array-node()" : lambda part : json .loads (part .text ),
158- "object-node()" : lambda part : Client .__process_object_node_part (part ),
159- "document-node()" : lambda part : Client .__process_document_node_part (part ),
160- # It appears that binary() will only be returned for a binary node retrieved
161- # from the database, and thus an X-URI will always exist. Have not found a
162- # scenario that indicates otherwise.
163- "binary()" : lambda part : Document (
164- Client .__get_decoded_uri_from_part (part ), part .content
165- ),
166- }
167-
168- def __get_decoded_uri_from_part (part ):
169- encoding = part .encoding
170- return part .headers ["X-URI" .encode (encoding )].decode (encoding )
171-
172- def __process_object_node_part (part ):
173- if b"X-URI" in part .headers :
174- return Document (
175- Client .__get_decoded_uri_from_part (part ), json .loads (part .text )
176- )
177- else :
178- return json .loads (part .text )
179-
180- def __process_document_node_part (part ):
181- if b"X-URI" in part .headers :
182- return Document (Client .__get_decoded_uri_from_part (part ), part .text )
183- else :
184- return part .text
0 commit comments