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
Simone Orsi 2021-12-03 17:19:22 +01:00 committed by Sébastien BEAU
parent 90c2511706
commit 0ffc105bff
2 changed files with 70 additions and 8 deletions

View File

@ -5,12 +5,16 @@
# Simone Orsi <simahawk@gmail.com>
# 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.tools.translate import _
from .utils import convert_simple_to_full_parser
_logger = logging.getLogger(__name__)
class Base(models.AbstractModel):
@ -70,17 +74,47 @@ class Base(models.AbstractModel):
@api.model
def _jsonify_record(self, parser, rec, root):
"""Jsonify one record (rec). Private function called by jsonify."""
strict = self.env.context.get("jsonify_record_strict", False)
for field in parser:
field_dict, subparser = rec.__parse_field(field)
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)
field = rec._fields[field_name]
if field_dict.get("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:
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 = [
self._jsonify_record(subparser, r, {}) for r in rec[field_name]
]

View File

@ -1,7 +1,9 @@
# Copyright 2017 ACSONE SA/NV
# 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.tests.common import SavepointCase
@ -310,15 +312,41 @@ class TestParser(SavepointCase):
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"]
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"}]}
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"}])]}
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