← Back to team overview

openerp-dev-web team mailing list archive

lp:~openerp-dev/openobject-client-web/proto61-better-implement-JSONRPC2-xmo into lp:~openerp-dev/openobject-client-web/trunk-proto61

 

Xavier (Open ERP) has proposed merging lp:~openerp-dev/openobject-client-web/proto61-better-implement-JSONRPC2-xmo into lp:~openerp-dev/openobject-client-web/trunk-proto61.

Requested reviews:
  OpenERP R&D Team (openerp-dev)

For more details, see:
https://code.launchpad.net/~openerp-dev/openobject-client-web/proto61-better-implement-JSONRPC2-xmo/+merge/53827

Improvements to the JSON-RPC implementation:

* Fix of the JS request to send the correct types (and stop putting the request body in a parameter)
* Fix of the Python side to read its request body correctly
* Made handling of JSON-RPC requests slighly clearer and more readable, used an error code per error type, kept id around between request and response

Also fixed start/stop of the cherrypy server as it was annoying when it got stuck.
-- 
https://code.launchpad.net/~openerp-dev/openobject-client-web/proto61-better-implement-JSONRPC2-xmo/+merge/53827
Your team OpenERP R&D Team is requested to review the proposed merge of lp:~openerp-dev/openobject-client-web/proto61-better-implement-JSONRPC2-xmo into lp:~openerp-dev/openobject-client-web/trunk-proto61.
=== modified file 'addons/base/static/openerp/js/base_chrome.js'
--- addons/base/static/openerp/js/base_chrome.js	2011-03-17 12:22:39 +0000
+++ addons/base/static/openerp/js/base_chrome.js	2011-03-17 14:26:29 +0000
@@ -143,27 +143,28 @@
         // Construct a JSON-RPC2 request, method is currently unused
         params.session_id = this.session_id;
         params.context = typeof(params.context) != "undefined" ? params.context  : this.context;
-        var request = { jsonrpc: "2.0", method: "call", params: params, "id":null };
-
-        // This is a violation of the JSON-RPC2 over HTTP protocol
-        // specification but i don't know how to parse the raw POST content from
-        // cherrypy so i use a POST form with one variable named request
-        var post = { request: JSON.stringify(request) };
 
         // Use a default error handler unless defined
         error_callback = typeof(error_callback) != "undefined" ? error_callback : this.on_rpc_error;
 
         // Call using the rpc_mode
-        this.rpc_ajax(url, post, success_callback, error_callback);
+        this.rpc_ajax(url, {
+            jsonrpc: "2.0",
+            method: "call",
+            params: params,
+            id:null
+        }, success_callback, error_callback);
     },
-    rpc_ajax: function(url, post, success_callback, error_callback) {
+    rpc_ajax: function(url, payload, success_callback, error_callback) {
         var self = this;
         this.on_rpc_request();
         $.ajax({
             type: "POST",
             url: url,
             dataType: 'json',
-            data: post,
+            contentType: 'application/json',
+            data: JSON.stringify(payload),
+            processData: false,
             success: function(response, textStatus, jqXHR) {
                 self.on_rpc_response();
                 if (response.error) {
@@ -182,7 +183,7 @@
             error: function(jqXHR, textStatus, errorThrown) {
                 self.on_rpc_response();
                 var error = {
-                    code: 1,
+                    code: -32098,
                     message: "XmlHttpRequestError " + errorThrown,
                     data: {type: "xhr"+textStatus, debug: jqXHR.responseText, objects: [jqXHR, errorThrown] }
                 };
@@ -196,7 +197,7 @@
     },
     on_rpc_error: function(error) {
         // TODO this should use the $element with focus and button is displaying OPW etc...
-        this.on_log(error, error.message, error.data.type, error.data.debug);
+        this.on_log(error.message, error.data);
     },
     on_session_invalid: function(contination) {
     },

=== modified file 'openerpweb/openerpweb.py'
--- openerpweb/openerpweb.py	2011-03-10 15:53:45 +0000
+++ openerpweb/openerpweb.py	2011-03-17 14:26:29 +0000
@@ -1,7 +1,9 @@
 #!/usr/bin/python
+import functools
 
 import os, re, sys, traceback, xmlrpclib
 
+import cherrypy
 import cherrypy.lib.static
 import simplejson
 
@@ -79,60 +81,82 @@
     <-- {"jsonrpc": "2.0", "error": {"code": 1, "message": "End user error message.", "data": {"code": "codestring", "debug": "traceback" } }, "id": null}
 
     """
-    def __init__(self):
-        # result may be filled, it's content will be updated by the return
-        # value of the dispatched function if it's a dict
-        self.result = {}
-        self.error_type = ""
-        self.error_message = ""
-        self.error_debug = ""
 
     def parse(self, request):
-        self.cherrypy_request = None
-        self.cherrypy_session = None
-        d = simplejson.loads(request)
-        self.params = d.get("params",{})
+        self.params = request.get("params",{})
         self.session_id = self.params.pop("session_id", None) or "random.random"
         self.session = session_store.setdefault(self.session_id, OpenERPSession())
-        self.context = self.params.pop("context", {})
-
-    def dispatch(self, controller, f, request):
+        self.context = self.params.pop('context', None)
+        return self.params
+
+    def dispatch(self, controller, method, requestf=None, request=None):
+        ''' Calls the method asked for by the JSON-RPC2 request
+
+        :param controller: the instance of the controller which received the request
+        :type controller: type
+        :param method: the method which received the request
+        :type method: callable
+        :param requestf: a file-like object containing an encoded JSON-RPC2 request
+        :type requestf: <read() -> bytes>
+        :param request: an encoded JSON-RPC2 request
+        :type request: bytes
+
+        :returns: a string-encoded JSON-RPC2 reply
+        :rtype: bytes
+        '''
+        if requestf:
+            request = simplejson.load(requestf)
+        else:
+            request = simplejson.loads(request)
         try:
-            print "--> %s.%s %s"%(controller.__class__.__name__,f.__name__,request)
-            self.parse(request)
-            r=f(controller, self, **self.params)
-            if isinstance(r, dict):
-                self.result.update(r)
-        except OpenERPUnboundException,e:
-            self.error_type = "session_invalid"
-            self.error_message = "OpenERP Session Invalid"
-            self.error_debug = traceback.format_exc()
+            print "--> %s.%s %s"%(controller.__class__.__name__,method.__name__,request)
+            error = None
+            result = method(controller, self, **self.parse(request))
+        except OpenERPUnboundException:
+            error = {
+                'code': 1,
+                'message': "OpenERP Session Invalid",
+                'data': {
+                    'type': 'session_invalid',
+                    'debug': traceback.format_exc()
+                }
+            }
         except xmlrpclib.Fault, e:
-            tb = "".join(traceback.format_exception("", None, sys.exc_traceback))
-            self.error_type = "server_exception"
-            self.error_message = "OpenERP Server Error: %s"%e.faultCode
-            self.error_debug = "Client %s\nServer %s"%(tb,e.faultString)
-        except Exception,e:
-            self.error_type = "client_exception"
-            self.error_message = "OpenERP WebClient Error: %r"%e
-            self.error_debug = "Client %s"%traceback.format_exc()
-        r = {"jsonrpc": "2.0",  "id": None}
-        if self.error_type:
-            r["error"] = {"code": 1, "message": self.error_message, "data": { "type":self.error_type, "debug": self.error_debug } }
+            error = {
+                'code': 25,
+                'message': "OpenERP Server Error",
+                'data': {
+                    'type': "server_exception",
+                    'fault_code': e.faultCode,
+                    'debug': "Client %s\nServer %s" % (
+                        traceback.format_exc(), e.faultString)
+                }
+            }
+        except Exception:
+            error = {
+                'code': 50,
+                'message': "OpenERP WebClient Error",
+                'data': {
+                    'type': 'client_exception',
+                    'debug': "Client %s" % traceback.format_exc()
+                }
+            }
+        response = {"jsonrpc": "2.0",  "id": request.get('id')}
+        if error:
+            response["error"] = error
         else:
-            r["result"] = self.result
-        print "<--",r
+            response["result"] = result
+
+        print "<--",  response
         print
-        #import pprint
-        #pprint.pprint(r)
-        return simplejson.dumps(r)
+        return simplejson.dumps(response)
 
 def jsonrequest(f):
-    # check cleaner wrapping:
-    # functools.wraps(f)(lambda x: JsonRequest().dispatch(x, f))
-    l=lambda self, request: JsonRequest().dispatch(self, f, request)
-    l.exposed=1
-    return l
+    @cherrypy.expose
+    @functools.wraps(f)
+    def json_handler(self):
+        return JsonRequest().dispatch(self, f, requestf=cherrypy.request.body)
+    return json_handler
 
 class HttpRequest(object):
     """ Regular GET/POST request
@@ -232,8 +256,9 @@
         #'server.thread_pool' = 10,
         'tools.sessions.on': True,
     }
+    cherrypy.tree.mount(Root())
+
     cherrypy.config.update(config)
-    cherrypy_root = Root()
-    cherrypy.quickstart(cherrypy_root,'',{'/':{}})
-
-# vim:
+    cherrypy.server.subscribe()
+    cherrypy.engine.start()
+    cherrypy.engine.block()


Follow ups