diff --git a/base_jsonify/models/models.py b/base_jsonify/models/models.py index 22bfa6ab8..470d772a4 100644 --- a/base_jsonify/models/models.py +++ b/base_jsonify/models/models.py @@ -5,12 +5,16 @@ # Simone Orsi # 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] ] diff --git a/base_jsonify/tests/test_get_parser.py b/base_jsonify/tests/test_get_parser.py index 43475e11e..017630acc 100644 --- a/base_jsonify/tests/test_get_parser.py +++ b/base_jsonify/tests/test_get_parser.py @@ -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