base_jsonify: fail gracefully
It could happen that a module adds an ir.export.line for a new field but when tests run the field is not loaded if this module is not a dependency of the module running tests. Furthermore, in general, it seems too strict to break the whole computation if there's a field that is missing. It seems more reasonable to log an error when this happen so that technical action can be taken to fix it.pull/2418/head
parent
90c2511706
commit
0ffc105bff
|
@ -5,12 +5,16 @@
|
||||||
# Simone Orsi <simahawk@gmail.com>
|
# Simone Orsi <simahawk@gmail.com>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
from odoo import api, fields, models
|
import logging
|
||||||
|
|
||||||
|
from odoo import api, fields, models, tools
|
||||||
from odoo.exceptions import UserError
|
from odoo.exceptions import UserError
|
||||||
from odoo.tools.translate import _
|
from odoo.tools.translate import _
|
||||||
|
|
||||||
from .utils import convert_simple_to_full_parser
|
from .utils import convert_simple_to_full_parser
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Base(models.AbstractModel):
|
class Base(models.AbstractModel):
|
||||||
|
|
||||||
|
@ -70,17 +74,47 @@ class Base(models.AbstractModel):
|
||||||
@api.model
|
@api.model
|
||||||
def _jsonify_record(self, parser, rec, root):
|
def _jsonify_record(self, parser, rec, root):
|
||||||
"""Jsonify one record (rec). Private function called by jsonify."""
|
"""Jsonify one record (rec). Private function called by jsonify."""
|
||||||
|
strict = self.env.context.get("jsonify_record_strict", False)
|
||||||
for field in parser:
|
for field in parser:
|
||||||
field_dict, subparser = rec.__parse_field(field)
|
field_dict, subparser = rec.__parse_field(field)
|
||||||
field_name = field_dict["name"]
|
field_name = field_dict["name"]
|
||||||
|
if field_name not in rec._fields:
|
||||||
|
if strict:
|
||||||
|
# let it fail
|
||||||
|
rec._fields[field_name] # pylint: disable=pointless-statement
|
||||||
|
if not tools.config["test_enable"]:
|
||||||
|
# If running live, log proper error
|
||||||
|
# so that techies can track it down
|
||||||
|
_logger.error(
|
||||||
|
"%(model)s.%(fname)s not available",
|
||||||
|
{"model": self._name, "fname": field_name},
|
||||||
|
)
|
||||||
|
continue
|
||||||
json_key = field_dict.get("target", field_name)
|
json_key = field_dict.get("target", field_name)
|
||||||
field = rec._fields[field_name]
|
field = rec._fields[field_name]
|
||||||
if field_dict.get("function"):
|
if field_dict.get("function"):
|
||||||
function = field_dict["function"]
|
function = field_dict["function"]
|
||||||
value = self._function_value(rec, function, field_name)
|
try:
|
||||||
|
value = self._function_value(rec, function, field_name)
|
||||||
|
except UserError:
|
||||||
|
if strict:
|
||||||
|
raise
|
||||||
|
if not tools.config["test_enable"]:
|
||||||
|
_logger.error(
|
||||||
|
"%(model)s.%(func)s not available",
|
||||||
|
{"model": self._name, "func": str(function)},
|
||||||
|
)
|
||||||
|
continue
|
||||||
elif subparser:
|
elif subparser:
|
||||||
if not (field.relational or field.type == "reference"):
|
if not (field.relational or field.type == "reference"):
|
||||||
self._jsonify_bad_parser_error(field_name)
|
if strict:
|
||||||
|
self._jsonify_bad_parser_error(field_name)
|
||||||
|
if not tools.config["test_enable"]:
|
||||||
|
_logger.error(
|
||||||
|
"%(model)s.%(fname)s not relational",
|
||||||
|
{"model": self._name, "fname": field_name},
|
||||||
|
)
|
||||||
|
continue
|
||||||
value = [
|
value = [
|
||||||
self._jsonify_record(subparser, r, {}) for r in rec[field_name]
|
self._jsonify_record(subparser, r, {}) for r in rec[field_name]
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
# Copyright 2017 ACSONE SA/NV
|
# Copyright 2017 ACSONE SA/NV
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
from odoo import fields
|
import mock
|
||||||
|
|
||||||
|
from odoo import fields, tools
|
||||||
from odoo.exceptions import UserError
|
from odoo.exceptions import UserError
|
||||||
from odoo.tests.common import SavepointCase
|
from odoo.tests.common import SavepointCase
|
||||||
|
|
||||||
|
@ -310,15 +312,41 @@ class TestParser(SavepointCase):
|
||||||
|
|
||||||
self.assertDictEqual(json_menu, {"action": "Users"})
|
self.assertDictEqual(json_menu, {"action": "Users"})
|
||||||
|
|
||||||
def test_bad_parsers(self):
|
def test_bad_parsers_strict(self):
|
||||||
|
rec = self.category.with_context(jsonify_record_strict=True)
|
||||||
bad_field_name = ["Name"]
|
bad_field_name = ["Name"]
|
||||||
with self.assertRaises(KeyError):
|
with self.assertRaises(KeyError):
|
||||||
self.category.jsonify(bad_field_name, one=True)
|
rec.jsonify(bad_field_name, one=True)
|
||||||
|
|
||||||
bad_function_name = {"fields": [{"name": "name", "function": "notafunction"}]}
|
bad_function_name = {"fields": [{"name": "name", "function": "notafunction"}]}
|
||||||
with self.assertRaises(UserError):
|
with self.assertRaises(UserError):
|
||||||
self.category.jsonify(bad_function_name, one=True)
|
rec.jsonify(bad_function_name, one=True)
|
||||||
|
|
||||||
bad_subparser = {"fields": [({"name": "name"}, [{"name": "subparser_name"}])]}
|
bad_subparser = {"fields": [({"name": "name"}, [{"name": "subparser_name"}])]}
|
||||||
with self.assertRaises(UserError):
|
with self.assertRaises(UserError):
|
||||||
self.category.jsonify(bad_subparser, one=True)
|
rec.jsonify(bad_subparser, one=True)
|
||||||
|
|
||||||
|
def test_bad_parsers_fail_gracefully(self):
|
||||||
|
rec = self.category
|
||||||
|
|
||||||
|
logger_patch_path = "odoo.addons.base_jsonify.models.models._logger.error"
|
||||||
|
|
||||||
|
# logging is disabled when testing as it's useless and makes build fail.
|
||||||
|
tools.config["test_enable"] = False
|
||||||
|
|
||||||
|
bad_field_name = ["Name"]
|
||||||
|
with mock.patch(logger_patch_path) as mocked_logger:
|
||||||
|
rec.jsonify(bad_field_name, one=True)
|
||||||
|
mocked_logger.assert_called()
|
||||||
|
|
||||||
|
bad_function_name = {"fields": [{"name": "name", "function": "notafunction"}]}
|
||||||
|
with mock.patch(logger_patch_path) as mocked_logger:
|
||||||
|
rec.jsonify(bad_function_name, one=True)
|
||||||
|
mocked_logger.assert_called()
|
||||||
|
|
||||||
|
bad_subparser = {"fields": [({"name": "name"}, [{"name": "subparser_name"}])]}
|
||||||
|
with mock.patch(logger_patch_path) as mocked_logger:
|
||||||
|
rec.jsonify(bad_subparser, one=True)
|
||||||
|
mocked_logger.assert_called()
|
||||||
|
|
||||||
|
tools.config["test_enable"] = True
|
||||||
|
|
Loading…
Reference in New Issue