From 4d7c07a6022bc8fef0c00c5f6077e3e030ecf940 Mon Sep 17 00:00:00 2001 From: Hadrien Huvelle Date: Mon, 24 Feb 2025 09:58:26 +0100 Subject: [PATCH 1/3] [IMP] edi_exchange_template: add validator for code snippet --- .../models/edi_exchange_template_mixin.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/edi_exchange_template_oca/models/edi_exchange_template_mixin.py b/edi_exchange_template_oca/models/edi_exchange_template_mixin.py index 471c9ef80..3a6ca8785 100644 --- a/edi_exchange_template_oca/models/edi_exchange_template_mixin.py +++ b/edi_exchange_template_oca/models/edi_exchange_template_mixin.py @@ -138,12 +138,16 @@ def _evaluate_code_snippet(self, **render_values): if not isinstance(result, dict): _logger.error("code_snippet should return a dict into `result`") return {} + validator = getattr( + self, + f"_evaluate_code_snippet_validate_{self.generator}", + lambda result: False, + ) + err_msg = validator(result) + if err_msg: + _logger.error("code_snippet validation error: %s", err_msg) + return {} return result - def _get_validator(self, exchange_record): - # TODO: lookup for validator ( - # can be to validate received file or generated file) - pass - def validate(self, exchange_record): pass From c59b81922a74130c3b359f87b31ad27a5546b0f5 Mon Sep 17 00:00:00 2001 From: Hadrien Huvelle Date: Mon, 24 Feb 2025 11:40:57 +0100 Subject: [PATCH 2/3] [IMP] edi_exchange_template: add json generator [IMP] edi_exchange_template: added _evaluate_code_snippet_validate_json and patched test to comply with it --- edi_exchange_template_oca/README.rst | 5 ++- .../models/edi_exchange_template_mixin.py | 5 +++ .../models/edi_exchange_template_output.py | 15 ++++++- .../readme/CONTRIBUTORS.rst | 5 ++- .../static/description/index.html | 3 ++ .../tests/test_edi_backend_output.py | 41 +++++++++++++++++++ 6 files changed, 71 insertions(+), 3 deletions(-) diff --git a/edi_exchange_template_oca/README.rst b/edi_exchange_template_oca/README.rst index 1f321e828..83d1d00ca 100644 --- a/edi_exchange_template_oca/README.rst +++ b/edi_exchange_template_oca/README.rst @@ -62,7 +62,10 @@ Authors Contributors ~~~~~~~~~~~~ -* Simone Orsi +- Simone Orsi +- John Herholz +- Italo Lopes +- Hadrien HUVELLE Maintainers ~~~~~~~~~~~ diff --git a/edi_exchange_template_oca/models/edi_exchange_template_mixin.py b/edi_exchange_template_oca/models/edi_exchange_template_mixin.py index 3a6ca8785..3fcfd12a4 100644 --- a/edi_exchange_template_oca/models/edi_exchange_template_mixin.py +++ b/edi_exchange_template_oca/models/edi_exchange_template_mixin.py @@ -149,5 +149,10 @@ def _evaluate_code_snippet(self, **render_values): return {} return result + def _evaluate_code_snippet_validate_json(self, result): + if "payload" not in result.keys(): + return "JSON code_snippet should return a dict with a 'payload' key" + return False + def validate(self, exchange_record): pass diff --git a/edi_exchange_template_oca/models/edi_exchange_template_output.py b/edi_exchange_template_oca/models/edi_exchange_template_output.py index 0786fd0af..d0ce7c1bc 100644 --- a/edi_exchange_template_oca/models/edi_exchange_template_output.py +++ b/edi_exchange_template_oca/models/edi_exchange_template_output.py @@ -1,6 +1,8 @@ # Copyright 2020 ACSONE SA # @author Simone Orsi # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). +import json + import lxml.etree as etree from odoo import fields, models @@ -16,7 +18,7 @@ class EDIExchangeOutputTemplate(models.Model): _description = "EDI Exchange Output Template" generator = fields.Selection( - [("qweb", "Qweb Template"), ("report", "Report")], + [("json", "JSON"), ("qweb", "Qweb Template"), ("report", "Report")], required=True, default="qweb", ) @@ -86,6 +88,12 @@ def _generate_report(self, exchange_record, **kw): res_ids = values.get("res_ids", []) return self.env["ir.actions.report"]._render(report, res_ids, data=values)[0] + def _generate_json(self, exchange_record, **kw): + # We expect to have json data into `payload` key. + # See validator `_evaluate_code_snippet_validate_json` in the mixin. + result = self._get_render_values(exchange_record, **kw) + return result + def _get_render_values(self, exchange_record, **kw): """Collect values to render current template.""" values = { @@ -118,8 +126,13 @@ def _post_process_output(self, output): output = xml_purge_nswrapper(output) if self.prettify: output = self._prettify_xml(output) + elif self.generator == "json": + output = self._post_process_json_output(output) return output + def _post_process_json_output(self, output): + return json.dumps(output["payload"]) + def _prettify_xml(self, xml_string): return etree.tostring(etree.fromstring(xml_string), pretty_print=True) diff --git a/edi_exchange_template_oca/readme/CONTRIBUTORS.rst b/edi_exchange_template_oca/readme/CONTRIBUTORS.rst index f583948be..9f8b335fd 100644 --- a/edi_exchange_template_oca/readme/CONTRIBUTORS.rst +++ b/edi_exchange_template_oca/readme/CONTRIBUTORS.rst @@ -1 +1,4 @@ -* Simone Orsi +- Simone Orsi \<\> +- John Herholz \<\> +- Italo Lopes \<\> +- Hadrien HUVELLE \<\> diff --git a/edi_exchange_template_oca/static/description/index.html b/edi_exchange_template_oca/static/description/index.html index b03e328a1..b1119bf50 100644 --- a/edi_exchange_template_oca/static/description/index.html +++ b/edi_exchange_template_oca/static/description/index.html @@ -408,6 +408,9 @@

Authors

Contributors

diff --git a/edi_exchange_template_oca/tests/test_edi_backend_output.py b/edi_exchange_template_oca/tests/test_edi_backend_output.py index 9e6c75b0f..823fded9c 100644 --- a/edi_exchange_template_oca/tests/test_edi_backend_output.py +++ b/edi_exchange_template_oca/tests/test_edi_backend_output.py @@ -1,5 +1,6 @@ # Copyright 2020 ACSONE SA/NV () # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). +import json from lxml import etree @@ -119,6 +120,39 @@ def _setup_records(cls): "type_id": cls.type_out2.id, } cls.record3 = cls.backend.create_record("test_type_out3", vals) + + cls.type_out_json = cls._create_exchange_type( + name="Template output JSON", + direction="output", + code="test_type_out_json", + exchange_file_ext="txt", + exchange_filename_pattern="{record.ref}-{type.code}-{dt}", + ) + model = cls.env["edi.exchange.template.output"] + cls.tmpl_out_json = model.create( + { + "generator": "json", + "name": "Out JSON", + "backend_type_id": cls.backend.backend_type_id.id, + "code": "test_type_out_json", + "type_id": cls.type_out_json.id, + "output_type": "json", + "code_snippet": """ +result = { + 'payload': { + "name": record.name, + "ref": record.ref + } +} + """, + } + ) + vals = { + "model": cls.partner._name, + "res_id": cls.partner.id, + "type_id": cls.tmpl_out_json.id, + } + cls.record_json = cls.backend.create_record("test_type_out_json", vals) return res @@ -135,6 +169,9 @@ def test_get_template(self): self.backend._get_output_template(self.record2, code=self.tmpl_out1.code), self.tmpl_out1, ) + self.assertEqual( + self.backend._get_output_template(self.record_json), self.tmpl_out_json + ) def test_generate_file(self): output = self.backend.exchange_generate(self.record1) @@ -152,6 +189,10 @@ def test_generate_file(self): self.assertEqual(doc.getchildren()[1].tag, "Custom") self.assertEqual(doc.getchildren()[1].text, "2") self.assertEqual(doc.getchildren()[1].attrib, {"bit": "custom_var"}) + self.backend.exchange_generate(self.record_json) + expected = json.dumps({"name": self.partner.name, "ref": self.partner.ref}) + file_content = self.record_json._get_file_content() + self.assertEqual(file_content.strip(), expected) def test_prettify(self): self.tmpl_out2.template_id.arch = ( From 98b8c84220e0ccc4eb962ad036b6df880c192dbf Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Wed, 26 Feb 2025 16:53:18 +0100 Subject: [PATCH 3/3] [IMP] edi_exchange_template: unify postprocessor lookup Rely on specific method as per validator and code executor. --- .../models/edi_exchange_template_output.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/edi_exchange_template_oca/models/edi_exchange_template_output.py b/edi_exchange_template_oca/models/edi_exchange_template_output.py index d0ce7c1bc..eaca0f498 100644 --- a/edi_exchange_template_oca/models/edi_exchange_template_output.py +++ b/edi_exchange_template_oca/models/edi_exchange_template_output.py @@ -121,16 +121,21 @@ def _render_template(self, exchange_record, code, **kw): def _post_process_output(self, output): """Post process generated output.""" + processor = getattr( + self, + f"_post_process_output_{self.generator}", + lambda result: result, + ) + return processor(output) + + def _post_process_output_qweb(self, output): if self.output_type == "xml": - # TODO: lookup for components to handle this dynamically output = xml_purge_nswrapper(output) if self.prettify: output = self._prettify_xml(output) - elif self.generator == "json": - output = self._post_process_json_output(output) return output - def _post_process_json_output(self, output): + def _post_process_output_json(self, output): return json.dumps(output["payload"]) def _prettify_xml(self, xml_string):