[IMP] base, web, *: make KarmaError an except_orm
* = gamification, test_website, stock - KarmaError is now handled as a 400 exception. - test_website has been updated. - NO_POSTMORTEM is now clean it was referencing duplicates as most the exceptions inherit from except_orm. - serialize_exception from http.py has been moved to ir_http to take advantage of the odoo inheritance system. We can then extend ir_http serialize_exception method to add the KarmaError logic if and only if gamification is installed. Places where serialize_exception was previously used are updated. Part of https://github.com/odoo/odoo/pull/32132 task-1894820
This commit is contained in:
@@ -1,15 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from werkzeug.exceptions import Forbidden
|
||||
|
||||
from odoo import api, tools, fields, models
|
||||
from odoo import api, tools, fields, models, exceptions, http
|
||||
from odoo.tools.translate import html_translate
|
||||
|
||||
|
||||
class KarmaError(Forbidden):
|
||||
class KarmaError(exceptions.except_orm):
|
||||
""" Karma-related error, used for forum and posts. """
|
||||
pass
|
||||
def __init__(self, msg):
|
||||
super(KarmaError, self).__init__(msg)
|
||||
|
||||
|
||||
class Http(models.AbstractModel):
|
||||
_inherit = 'ir.http'
|
||||
|
||||
@classmethod
|
||||
def serialize_exception(self, e):
|
||||
res = super(Http, self).serialize_exception(e)
|
||||
if isinstance(e, KarmaError):
|
||||
res["exception_type"] = "karma_error"
|
||||
return res
|
||||
|
||||
|
||||
class KarmaRank(models.Model):
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
from odoo.addons.web.controllers.main import _serialize_exception
|
||||
from odoo.tools import html_escape
|
||||
|
||||
import json
|
||||
@@ -37,7 +36,7 @@ class StockReportController(http.Controller):
|
||||
response.set_cookie('fileToken', token)
|
||||
return response
|
||||
except Exception as e:
|
||||
se = _serialize_exception(e)
|
||||
se = request.env['ir.http'].serialize_exception(e)
|
||||
error = {
|
||||
'code': 200,
|
||||
'message': 'Odoo Server Error',
|
||||
|
||||
@@ -14,6 +14,7 @@ dependencies. Neither we want to add in website module some routes, views and
|
||||
models which only purpose is to run tests.""",
|
||||
'depends': [
|
||||
'website',
|
||||
'gamification',
|
||||
],
|
||||
'data': [
|
||||
'views/templates.xml',
|
||||
|
||||
@@ -8,6 +8,7 @@ from odoo import http
|
||||
from odoo.http import request
|
||||
from odoo.addons.portal.controllers.web import Home
|
||||
from odoo.exceptions import UserError, ValidationError, AccessError, MissingError, AccessError, AccessDenied
|
||||
from odoo.addons.gamification.models.gamification_karma_rank import KarmaError
|
||||
|
||||
|
||||
class WebsiteTest(Home):
|
||||
@@ -80,6 +81,14 @@ class WebsiteTest(Home):
|
||||
def test_missing_error_http(self, **kwargs):
|
||||
raise MissingError("This is a missing http test")
|
||||
|
||||
@http.route('/test_karma_error_json', type='json', auth='public', website=True)
|
||||
def test_karma_error_rpc(self, **kwargs):
|
||||
raise KarmaError("This is a karma rpc test")
|
||||
|
||||
@http.route('/test_karma_error_http', type='http', auth='public', website=True)
|
||||
def test_karma_error_http(self, **kwargs):
|
||||
raise KarmaError("This is a karma http test")
|
||||
|
||||
@http.route('/test_internal_error_json', type='json', auth='public', website=True)
|
||||
def test_internal_error_json(self, **kwargs):
|
||||
raise werkzeug.exceptions.InternalServerError()
|
||||
|
||||
@@ -44,6 +44,13 @@ tour.register('test_error_website', {
|
||||
content: "rpc error 403 modal has message",
|
||||
extra_trigger: 'div.o_dialog_warning.modal-body:contains("This is an access denied rpc test")',
|
||||
trigger: 'button.btn.btn-primary',
|
||||
}, {
|
||||
content: "trigger rpc missing error",
|
||||
trigger: 'a[href="/test_karma_error_json"]',
|
||||
}, {
|
||||
content: "rpc missing error modal has message",
|
||||
extra_trigger: 'div.o_dialog_warning.modal-body:contains("This is a karma rpc test")',
|
||||
trigger: 'button.btn.btn-primary',
|
||||
}, {
|
||||
content: "trigger rpc error 500",
|
||||
trigger: 'a[href="/test_internal_error_json"]',
|
||||
@@ -131,6 +138,24 @@ tour.register('test_error_website', {
|
||||
run: function () {
|
||||
window.location.href = window.location.origin + '/test_karma_error_http?debug=0';
|
||||
},
|
||||
}, {
|
||||
content: "http karma error page has title and message",
|
||||
extra_trigger: 'h1:contains("Oops! Something went wrong.")',
|
||||
trigger: 'div.container pre:contains("This is a karma http test")',
|
||||
run: function () {
|
||||
window.location.href = window.location.origin + '/test_karma_error_http?debug=1';
|
||||
},
|
||||
}, {
|
||||
content: "http karma error page debug has title and message open",
|
||||
extra_trigger: 'h1:contains("Oops! Something went wrong.")',
|
||||
trigger: 'div#error_main.collapse.show pre:contains("This is a karma http test")',
|
||||
run: function () {},
|
||||
}, {
|
||||
content: "http karma error page debug has traceback closed",
|
||||
trigger: 'body:has(div#error_traceback.collapse:not(.show) pre#exception_traceback)',
|
||||
run: function () {
|
||||
window.location.href = window.location.origin + '/test_access_denied_http?debug=0';
|
||||
},
|
||||
}, {
|
||||
content: "http error 403 page has title but no message",
|
||||
extra_trigger: 'h1:contains("403: Forbidden")',
|
||||
|
||||
@@ -42,8 +42,7 @@ from odoo.tools.translate import _
|
||||
from odoo.tools.misc import str2bool, xlwt, file_open
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
from odoo import http, tools
|
||||
from odoo.http import content_disposition, dispatch_rpc, request, \
|
||||
serialize_exception as _serialize_exception, Response
|
||||
from odoo.http import content_disposition, dispatch_rpc, request, Response
|
||||
from odoo.exceptions import AccessError, UserError, AccessDenied
|
||||
from odoo.models import check_method_name
|
||||
from odoo.service import db, security
|
||||
@@ -83,7 +82,7 @@ def serialize_exception(f):
|
||||
return f(*args, **kwargs)
|
||||
except Exception as e:
|
||||
_logger.exception("An exception occured during an http request")
|
||||
se = _serialize_exception(e)
|
||||
se = request.registry['ir.http'].serialize_exception(e)
|
||||
error = {
|
||||
'code': 200,
|
||||
'message': "Odoo Server Error",
|
||||
@@ -1694,7 +1693,7 @@ class ReportController(http.Controller):
|
||||
else:
|
||||
return
|
||||
except Exception as e:
|
||||
se = _serialize_exception(e)
|
||||
se = request.env['ir.http'].serialize_exception(e)
|
||||
error = {
|
||||
'code': 200,
|
||||
'message': "Odoo Server Error",
|
||||
|
||||
@@ -17,6 +17,7 @@ var map_title ={
|
||||
validation_error: _lt("Validation Error"),
|
||||
except_orm: _lt("Global Business Error"),
|
||||
access_denied: _lt("Access Denied"),
|
||||
karma_error: _lt("Karma Error"),
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,6 +9,7 @@ import mimetypes
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
import werkzeug
|
||||
import werkzeug.exceptions
|
||||
@@ -20,7 +21,7 @@ import odoo
|
||||
from odoo import api, http, models, tools, SUPERUSER_ID
|
||||
from odoo.exceptions import AccessDenied, AccessError
|
||||
from odoo.http import request, STATIC_CACHE, content_disposition
|
||||
from odoo.tools import pycompat, consteq
|
||||
from odoo.tools import consteq, pycompat, ustr
|
||||
from odoo.tools.mimetypes import guess_mimetype
|
||||
from odoo.modules.module import get_resource_path, get_module_path
|
||||
|
||||
@@ -178,6 +179,33 @@ class IrHttp(models.AbstractModel):
|
||||
return attach
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def serialize_exception(self, e):
|
||||
tmp = {
|
||||
"name": type(e).__module__ + "." + type(e).__name__ if type(e).__module__ else type(e).__name__,
|
||||
"debug": traceback.format_exc(),
|
||||
"message": ustr(e),
|
||||
"arguments": e.args,
|
||||
"exception_type": "internal_error"
|
||||
}
|
||||
if isinstance(e, odoo.exceptions.UserError):
|
||||
tmp["exception_type"] = "user_error"
|
||||
elif isinstance(e, odoo.exceptions.Warning):
|
||||
tmp["exception_type"] = "warning"
|
||||
elif isinstance(e, odoo.exceptions.RedirectWarning):
|
||||
tmp["exception_type"] = "warning"
|
||||
elif isinstance(e, odoo.exceptions.AccessError):
|
||||
tmp["exception_type"] = "access_error"
|
||||
elif isinstance(e, odoo.exceptions.MissingError):
|
||||
tmp["exception_type"] = "missing_error"
|
||||
elif isinstance(e, odoo.exceptions.AccessDenied):
|
||||
tmp["exception_type"] = "access_denied"
|
||||
elif isinstance(e, odoo.exceptions.ValidationError):
|
||||
tmp["exception_type"] = "validation_error"
|
||||
elif isinstance(e, odoo.exceptions.except_orm):
|
||||
tmp["exception_type"] = "except_orm"
|
||||
return tmp
|
||||
|
||||
@classmethod
|
||||
def _handle_exception(cls, exception):
|
||||
# in case of Exception, e.g. 404, we don't step into _dispatch
|
||||
|
||||
+6
-31
@@ -98,16 +98,16 @@ def replace_request_password(args):
|
||||
args[2] = '*'
|
||||
return tuple(args)
|
||||
|
||||
|
||||
# don't trigger debugger for those exceptions, they carry user-facing warnings
|
||||
# and indications, they're not necessarily indicative of anything being
|
||||
# *broken*
|
||||
NO_POSTMORTEM = (odoo.osv.orm.except_orm,
|
||||
odoo.exceptions.AccessError,
|
||||
odoo.exceptions.ValidationError,
|
||||
odoo.exceptions.MissingError,
|
||||
NO_POSTMORTEM = (odoo.exceptions.except_orm,
|
||||
odoo.exceptions.AccessDenied,
|
||||
odoo.exceptions.Warning,
|
||||
odoo.exceptions.RedirectWarning)
|
||||
|
||||
|
||||
def dispatch_rpc(service_name, method, params):
|
||||
""" Handle a RPC call.
|
||||
|
||||
@@ -316,7 +316,7 @@ class WebRequest(object):
|
||||
"""Called within an except block to allow converting exceptions
|
||||
to abitrary responses. Anything returned (except None) will
|
||||
be used as response."""
|
||||
self._failed = exception # prevent tx commit
|
||||
self._failed = exception # prevent tx commit
|
||||
if not isinstance(exception, NO_POSTMORTEM) \
|
||||
and not isinstance(exception, werkzeug.exceptions.HTTPException):
|
||||
odoo.tools.debugger.post_mortem(
|
||||
@@ -641,7 +641,7 @@ class JsonRequest(WebRequest):
|
||||
error = {
|
||||
'code': 200,
|
||||
'message': "Odoo Server Error",
|
||||
'data': serialize_exception(exception),
|
||||
'data': request.registry['ir.http'].serialize_exception(exception),
|
||||
}
|
||||
if isinstance(exception, werkzeug.exceptions.NotFound):
|
||||
error['http_status'] = 404
|
||||
@@ -691,31 +691,6 @@ class JsonRequest(WebRequest):
|
||||
except Exception as e:
|
||||
return self._handle_exception(e)
|
||||
|
||||
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": ustr(e),
|
||||
"arguments": e.args,
|
||||
"exception_type": "internal_error"
|
||||
}
|
||||
if isinstance(e, odoo.exceptions.UserError):
|
||||
tmp["exception_type"] = "user_error"
|
||||
elif isinstance(e, odoo.exceptions.Warning):
|
||||
tmp["exception_type"] = "warning"
|
||||
elif isinstance(e, odoo.exceptions.RedirectWarning):
|
||||
tmp["exception_type"] = "warning"
|
||||
elif isinstance(e, odoo.exceptions.AccessError):
|
||||
tmp["exception_type"] = "access_error"
|
||||
elif isinstance(e, odoo.exceptions.MissingError):
|
||||
tmp["exception_type"] = "missing_error"
|
||||
elif isinstance(e, odoo.exceptions.AccessDenied):
|
||||
tmp["exception_type"] = "access_denied"
|
||||
elif isinstance(e, odoo.exceptions.ValidationError):
|
||||
tmp["exception_type"] = "validation_error"
|
||||
elif isinstance(e, odoo.exceptions.except_orm):
|
||||
tmp["exception_type"] = "except_orm"
|
||||
return tmp
|
||||
|
||||
class HttpRequest(WebRequest):
|
||||
""" Handler for the ``http`` request type.
|
||||
|
||||
Reference in New Issue
Block a user