[IMP] changed server-side exceptions handling and JSON-RPC exception objects

bzr revid: nicolas.vanhoren@openerp.com-20130130154137-tzva6s0etzxy790u
This commit is contained in:
niv-openerp 2013-01-30 16:41:37 +01:00
commit 4a4282c245
8 changed files with 70 additions and 95 deletions

View File

@ -14,7 +14,6 @@ import re
import simplejson
import time
import urllib2
import xmlrpclib
import zlib
from xml.etree import ElementTree
from cStringIO import StringIO
@ -97,7 +96,7 @@ def db_monodb(req):
dbs = db_list(req)
if len(dbs) == 1:
return dbs[0]
except xmlrpclib.Fault:
except Exception:
# ignore access denied
pass
return False
@ -749,10 +748,10 @@ class Database(openerpweb.Controller):
try:
return req.session.proxy("db").drop(password, db)
except xmlrpclib.Fault, e:
if e.faultCode and e.faultCode.split(':')[0] == 'AccessDenied':
return {'error': e.faultCode, 'title': 'Drop Database'}
return {'error': _('Could not drop database !'), 'title': _('Drop Database')}
except openerp.exceptions.AccessDenied:
return {'error': 'AccessDenied', 'title': 'Drop Database'}
except Exception:
return {'error': _('Could not drop database !'), 'title': _('Drop Database')}
@openerpweb.httprequest
def backup(self, req, backup_db, backup_pwd, token):
@ -769,8 +768,8 @@ class Database(openerpweb.Controller):
('Content-Disposition', content_disposition(filename, req))],
{'fileToken': int(token)}
)
except xmlrpclib.Fault, e:
return simplejson.dumps([[],[{'error': e.faultCode, 'title': _('Backup Database')}]])
except Exception, e:
return simplejson.dumps([[],[{'error': openerp.tools.ustr(e), 'title': _('Backup Database')}]])
@openerpweb.httprequest
def restore(self, req, db_file, restore_pwd, new_db):
@ -778,9 +777,8 @@ class Database(openerpweb.Controller):
data = base64.b64encode(db_file.read())
req.session.proxy("db").restore(restore_pwd, new_db, data)
return ''
except xmlrpclib.Fault, e:
if e.faultCode and e.faultCode.split(':')[0] == 'AccessDenied':
raise Exception("AccessDenied")
except openerp.exceptions.AccessDenied, e:
raise Exception("AccessDenied")
@openerpweb.jsonrequest
def change_password(self, req, fields):
@ -789,10 +787,10 @@ class Database(openerpweb.Controller):
dict(map(operator.itemgetter('name', 'value'), fields)))
try:
return req.session.proxy("db").change_admin_password(old_password, new_password)
except xmlrpclib.Fault, e:
if e.faultCode and e.faultCode.split(':')[0] == 'AccessDenied':
return {'error': e.faultCode, 'title': _('Change Password')}
return {'error': _('Error, password not changed !'), 'title': _('Change Password')}
except openerp.exceptions.AccessDenied:
return {'error': 'AccessDenied', 'title': _('Change Password')}
except Exception:
return {'error': _('Error, password not changed !'), 'title': _('Change Password')}
class Session(openerpweb.Controller):
_cp_path = "/web/session"
@ -1194,7 +1192,7 @@ class Binary(openerpweb.Controller):
image_data = base64.b64decode(image_base64)
except (TypeError, xmlrpclib.Fault):
except Exception:
image_data = self.placeholder(req)
headers.append(('ETag', retag))
headers.append(('Content-Length', len(image_data)))
@ -1311,7 +1309,7 @@ class Binary(openerpweb.Controller):
'filename': ufile.filename,
'id': attachment_id
}
except xmlrpclib.Fault, e:
except Exception:
args = {'error':e.faultCode }
return out % (simplejson.dumps(callback), simplejson.dumps(args))

View File

@ -19,7 +19,6 @@ import time
import traceback
import urlparse
import uuid
import xmlrpclib
import babel.core
import simplejson
@ -194,35 +193,18 @@ class JsonRequest(WebRequest):
response['id'] = self.jsonrequest.get('id')
response["result"] = method(self, **self.params)
except session.AuthenticationError:
se = serialize_exception(e)
error = {
'code': 100,
'message': "OpenERP Session Invalid",
'data': {
'type': 'session_invalid',
'debug': traceback.format_exc()
}
'data': se
}
except xmlrpclib.Fault, e:
except Exception, e:
se = serialize_exception(e)
error = {
'code': 200,
'message': "OpenERP Server Error",
'data': {
'type': 'server_exception',
'fault_code': e.faultCode,
'debug': "Client %s\nServer %s" % (
"".join(traceback.format_exception("", None, sys.exc_traceback)), e.faultString)
}
}
except Exception:
logging.getLogger(__name__ + '.JSONRequest.dispatch').exception\
("An error occured while handling a json request")
error = {
'code': 300,
'message': "OpenERP WebClient Error",
'data': {
'type': 'client_exception',
'debug': "Client %s" % traceback.format_exc()
}
'data': se
}
if error:
response["error"] = error
@ -244,6 +226,36 @@ class JsonRequest(WebRequest):
r = werkzeug.wrappers.Response(body, headers=[('Content-Type', mime), ('Content-Length', len(body))])
return r
def serialize_exception(e):
tmp = {
"name": type(e).__module__ + "." + type(e).__name__ if type(e).__module__ else type(e).__name__,
"debug": traceback.format_exc(),
"message": u"%s" % e,
"arguments": to_jsonable(e.args),
}
if isinstance(e, openerp.osv.osv.except_osv):
tmp["exception_type"] = "except_osv"
elif isinstance(e, openerp.exceptions.Warning):
tmp["exception_type"] = "warning"
elif isinstance(e, openerp.exceptions.AccessError):
tmp["exception_type"] = "access_error"
elif isinstance(e, openerp.exceptions.AccessDenied):
tmp["exception_type"] = "access_denied"
return tmp
def to_jsonable(o):
if isinstance(o, str) or isinstance(o,unicode) or isinstance(o, int) or isinstance(o, long) \
or isinstance(o, bool) or o is None or isinstance(o, float):
return o
if isinstance(o, list) or isinstance(o, tuple):
return [to_jsonable(x) for x in o]
if isinstance(o, dict):
tmp = {}
for k, v in o.items():
tmp[u"%s" % k] = to_jsonable(v)
return tmp
return u"%s" % o
def jsonrequest(f):
""" Decorator marking the decorated method as being a handler for a
JSON-RPC request (the exact request path is specified via the
@ -274,28 +286,14 @@ class HttpRequest(WebRequest):
_logger.debug("%s --> %s.%s %r", self.httprequest.method, method.im_class.__name__, method.__name__, akw)
try:
r = method(self, **self.params)
except xmlrpclib.Fault, e:
r = werkzeug.exceptions.InternalServerError(cgi.escape(simplejson.dumps({
except Exception:
se = serialize_exception(e)
error = {
'code': 200,
'message': "OpenERP Server Error",
'data': {
'type': 'server_exception',
'fault_code': e.faultCode,
'debug': "Server %s\nClient %s" % (
e.faultString, traceback.format_exc())
}
})))
except Exception:
logging.getLogger(__name__ + '.HttpRequest.dispatch').exception(
"An error occurred while handling a json request")
r = werkzeug.exceptions.InternalServerError(cgi.escape(simplejson.dumps({
'code': 300,
'message': "OpenERP WebClient Error",
'data': {
'type': 'client_exception',
'debug': "Client %s" % traceback.format_exc()
}
})))
'data': se
}
r = werkzeug.exceptions.InternalServerError(cgi.escape(simplejson.dumps(error)))
if self.debug or 1:
if isinstance(r, (werkzeug.wrappers.BaseResponse, werkzeug.exceptions.HTTPException)):
_logger.debug('<-- %s', r)

View File

@ -6,7 +6,6 @@ import logging
import time
import traceback
import sys
import xmlrpclib
import openerp
@ -85,23 +84,7 @@ class OpenERPSession(object):
self.jsonp_requests = {} # FIXME use a LRU
def send(self, service_name, method, *args):
code_string = u"warning -- %s\n\n%s"
try:
return openerp.netsvc.dispatch_rpc(service_name, method, args)
except openerp.osv.osv.except_osv, e:
raise xmlrpclib.Fault(code_string % (e.name, e.value), '')
except openerp.exceptions.Warning, e:
raise xmlrpclib.Fault(code_string % ("Warning", e), '')
except openerp.exceptions.AccessError, e:
raise xmlrpclib.Fault(code_string % ("AccessError", e), '')
except openerp.exceptions.AccessDenied, e:
raise xmlrpclib.Fault('AccessDenied', openerp.tools.ustr(e))
except openerp.exceptions.DeferredException, e:
formatted_info = "".join(traceback.format_exception(*e.traceback))
raise xmlrpclib.Fault(openerp.tools.ustr(e), formatted_info)
except Exception, e:
formatted_info = "".join(traceback.format_exception(*(sys.exc_info())))
raise xmlrpclib.Fault(openerp.tools.ustr(e), formatted_info)
return openerp.netsvc.dispatch_rpc(service_name, method, args)
def proxy(self, service):
return Service(self, service)

View File

@ -247,19 +247,12 @@ instance.web.CrashManager = instance.web.Class.extend({
if (!this.active) {
return;
}
// yes, exception handling is shitty
if (error.code === 300 && error.data && error.data.type == "client_exception" && error.data.debug.match("SessionExpiredException")) {
this.show_warning({type: "Session Expired", data: { fault_code: "Your OpenERP session expired. Please refresh the current web page." }});
if (error.data.name === "openerp.addons.web.session SessionExpiredException") {
this.show_warning({type: "Session Expired", data: { message: "Your OpenERP session expired. Please refresh the current web page." }});
return;
}
if (error.data.fault_code) {
var split = ("" + error.data.fault_code).split('\n')[0].split(' -- ');
if (split.length > 1) {
error.type = split.shift();
error.data.fault_code = error.data.fault_code.substr(error.type.length + 4);
}
}
if (error.code === 200 && error.type) {
if (error.data.exception_type === "except_osv" || error.data.exception_type === "warning"
|| error.data.exception_type === "access_error") {
this.show_warning(error);
} else {
this.show_error(error);
@ -269,8 +262,11 @@ instance.web.CrashManager = instance.web.Class.extend({
if (!this.active) {
return;
}
if (error.data.exception_type === "except_osv") {
error = _.extend({}, error, {data: _.extend({}, error.data, {message: error.data.arguments[0] + "\n\n" + error.data.arguments[1]})});
}
instance.web.dialog($('<div>' + QWeb.render('CrashManager.warning', {error: error}) + '</div>'), {
title: "OpenERP " + _.str.capitalize(error.type),
title: "OpenERP " + (_.str.capitalize(error.type) || "Warning"),
buttons: [
{text: _t("Ok"), click: function() { $(this).dialog("close"); }}
]
@ -650,7 +646,7 @@ instance.web.Login = instance.web.Widget.extend({
}
},
on_db_failed: function (error, event) {
if (error.data.fault_code === 'AccessDenied') {
if (error.data.name === 'openerp.exceptions.AccessDenied') {
event.preventDefault();
}
},

View File

@ -991,7 +991,7 @@ instance.web.JsonRPC = instance.web.Class.extend(instance.web.PropertiesMixin, {
self.trigger('response', response);
if (!response.error) {
deferred.resolve(response["result"], textStatus, jqXHR);
} else if (response.error.data.type === "session_invalid") {
} else if (response.error.code === 100) {
self.uid = false;
} else {
deferred.reject(response.error, $.Event());

View File

@ -72,7 +72,7 @@ openerp.test_support = {
}
return;
}
fn(e.data.fault_code);
fn(e.data.name);
})
}
};

View File

@ -40,7 +40,7 @@
<td>
<p>
<t t-js="d">
var message = d.message ? d.message : d.error.data.fault_code;
var message = d.message ? d.message : d.error.data.message;
d.html_error = context.engine.tools.html_escape(message)
.replace(/\n/g, '<br/>');
</t>

View File

@ -415,7 +415,7 @@ instance.web_kanban.KanbanView = instance.web.View.extend({
new_group.do_save_sequences();
}).fail(function(error, evt) {
evt.preventDefault();
alert(_t("An error has occured while moving the record to this group: ") + data.fault_code);
alert(_t("An error has occured while moving the record to this group: ") + data.message);
self.do_reload(); // TODO: use draggable + sortable in order to cancel the dragging when the rcp fails
});
}