Also non-browser jsonrpc (as it goes through a similar process): for internal performance reasons, name_search and read_group have been converted to a *lazy* name_get, so the "display name" is not unnecessarily computed. However this is an issue for the RPC endpoints (/xmlrpc and /jsonrpc) as they have no support for `lazy` and thus tend to blow up and / or do the wrong thing when trying to output a lazy: * xmlrpc has no way to handle lazy at all and straight blows up * jsonrpc falls back to `json_default` so they try to stringify the lazy, which might have worked except *Problematically* both endpoints delegate the actual work to `dispatch_rpc` which handles dispatching between various services and ultimately creates a *new* cursor before calling model methods (`object` service and `execute`/`execute_kw`). This means by the time the result is serialized to be output, the lazy's cursor has long been closed, and thus any access to an unevaluated `lazy` errors out when trying to fetch the underlying item. This also means we can't just add a hook to serialize the lazy in the xmlrpc marshaller, though we do have to do that. We *also* (for both xmlrpc and jsonrpc) have to force evluation of lazy values before our cursor is closed, meaning it has to be done right after the method is invoked, iterating the entire response. Related to task 2170343 closes odoo/odoo#49286 X-original-commit: e2b5a359c1d5eccbe725c1c3169b4130d7bca49b Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
78 lines
2.6 KiB
Python
78 lines
2.6 KiB
Python
from datetime import date, datetime
|
|
from xmlrpc.client import dumps, loads
|
|
import xmlrpc.client
|
|
|
|
from werkzeug.wrappers import Response
|
|
|
|
from odoo.http import Controller, dispatch_rpc, request, route
|
|
from odoo.service import wsgi_server
|
|
from odoo.fields import Date, Datetime
|
|
from odoo.tools import lazy
|
|
|
|
|
|
class OdooMarshaller(xmlrpc.client.Marshaller):
|
|
|
|
"""
|
|
XMLRPC Marshaller that converts date(time) objects to strings in iso8061 format.
|
|
"""
|
|
|
|
dispatch = dict(xmlrpc.client.Marshaller.dispatch)
|
|
|
|
def dump_datetime(self, value, write):
|
|
# override to marshall as a string for backwards compatibility
|
|
value = Datetime.to_string(value)
|
|
self.dump_unicode(value, write)
|
|
dispatch[datetime] = dump_datetime
|
|
|
|
def dump_date(self, value, write):
|
|
value = Date.to_string(value)
|
|
self.dump_unicode(value, write)
|
|
dispatch[date] = dump_date
|
|
|
|
def dump_lazy(self, value, write):
|
|
v = value._value
|
|
return self.dispatch[type(v)](self, v, write)
|
|
dispatch[lazy] = dump_lazy
|
|
|
|
|
|
# monkey-patch xmlrpc.client's marshaller
|
|
xmlrpc.client.Marshaller = OdooMarshaller
|
|
|
|
|
|
class RPC(Controller):
|
|
"""Handle RPC connections."""
|
|
|
|
def _xmlrpc(self, service):
|
|
"""Common method to handle an XML-RPC request."""
|
|
data = request.httprequest.get_data()
|
|
params, method = loads(data)
|
|
result = dispatch_rpc(service, method, params)
|
|
return dumps((result,), methodresponse=1, allow_none=False)
|
|
|
|
@route("/xmlrpc/<service>", auth="none", methods=["POST"], csrf=False, save_session=False)
|
|
def xmlrpc_1(self, service):
|
|
"""XML-RPC service that returns faultCode as strings.
|
|
|
|
This entrypoint is historical and non-compliant, but kept for
|
|
backwards-compatibility.
|
|
"""
|
|
try:
|
|
response = self._xmlrpc(service)
|
|
except Exception as error:
|
|
response = wsgi_server.xmlrpc_handle_exception_string(error)
|
|
return Response(response=response, mimetype='text/xml')
|
|
|
|
@route("/xmlrpc/2/<service>", auth="none", methods=["POST"], csrf=False, save_session=False)
|
|
def xmlrpc_2(self, service):
|
|
"""XML-RPC service that returns faultCode as int."""
|
|
try:
|
|
response = self._xmlrpc(service)
|
|
except Exception as error:
|
|
response = wsgi_server.xmlrpc_handle_exception_int(error)
|
|
return Response(response=response, mimetype='text/xml')
|
|
|
|
@route('/jsonrpc', type='json', auth="none", save_session=False)
|
|
def jsonrpc(self, service, method, args):
|
|
""" Method used by client APIs to contact OpenERP. """
|
|
return dispatch_rpc(service, method, args)
|