[FIX] tools: fix filename resolution in XSD imports

With PRs odoo/89145 and enterprise/26393 came new methods for
retrieving XSD files and using them for XML validation.

The new retrieval method expects modules to provide a 'prefix' that
is prepended to the XSD's filename. For example, l10n_cl_edi will
name its XSD files 'l10n_cl_edi.<filename>.xsd'.

However, this messes things up when one XSD file needs to import
another. For example, l10n_cl_edi.DTE_v10.xsd has the statement
'<xs:include schemaLocation="SiiTypes_v10.xsd"/>'

Currently, the filename resolver has no way of knowing that this
should resolve to 'l10n_cl_edi.SiiTypes_v10.xsd', not
'SiiTypes_v10.xsd'.

In addition, the new retrieval method saves the ZIP archives
received over the network under the '<filename.xsd>'. Thus
'SiiTypes_v10.xsd' might actually be a ZIP-encoded file.

So, we need to do something to fix the imports.
Here are two possible solutions:
1. We scrap this 'prefix' stuff and either save the ZIP files under
   a different name, or we just don't save them.
2. Or, we provide a mechanism for indicating a prefix to the filename
   resolver.

Personally, I don't see the point in saving the ZIP files, and this
'prefix' stuff seems pointless. So I prefer solution 1.

But, because I assume there must be a reason to all of that 'prefix'
stuff, here is an implementation of solution 2.

I'd be keen to know the reason, btw.

EDIT:
In addition to the first issue described above, we have the second
issue that some XSD files returned by the Chilean SII are encoded
using ISO-8859-1 encoding (e.g. SiiTypes_v10.xsd). If we leave them
in this encoding, then LXML isn't able to parse them when performing
imports.

closes odoo/odoo#102601

Solution: convert the files to UTF-8 before storing them.
X-original-commit: 75555df56475b457331938453657c1f73d231e33
Related: odoo/enterprise#32482
Signed-off-by: Josse Colpaert <jco@odoo.com>
This commit is contained in:
Antoine Dupuis
2022-10-07 14:37:14 +02:00
parent bf8aee73b3
commit 5a09449ddc
+12 -8
View File
@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
"""Utilities for generating, parsing and checking XML/XSD files on top of the lxml.etree module."""
import base64
import logging
import requests
import zipfile
@@ -20,18 +19,20 @@ class odoo_resolver(etree.Resolver):
It will search filenames in the ir.attachments
"""
def __init__(self, env):
def __init__(self, env, prefix):
super().__init__()
self.env = env
self.prefix = prefix
def resolve(self, url, id, context):
"""Search url in ``ir.attachment`` and return the resolved content."""
attachment = self.env['ir.attachment'].search([('name', '=', url)])
attachment_name = f'{self.prefix}.{url}' if self.prefix else url
attachment = self.env['ir.attachment'].search([('name', '=', attachment_name)])
if attachment:
return self.resolve_string(base64.b64decode(attachment.datas), context)
return self.resolve_string(attachment.raw, context)
def _check_with_xsd(tree_or_str, stream, env=None):
def _check_with_xsd(tree_or_str, stream, env=None, prefix=None):
"""Check an XML against an XSD schema.
This will raise a UserError if the XML file is not valid according to the
@@ -42,12 +43,15 @@ def _check_with_xsd(tree_or_str, stream, env=None):
If env is given, it can also be the name of an attachment in the filestore
:param odoo.api.Environment env: If it is given, it enables resolving the
imports of the schema in the filestore with ir.attachments.
:param str prefix: if given, provides a prefix to try when
resolving the imports of the schema. e.g. prefix='l10n_cl_edi' will
enable 'SiiTypes_v10.xsd' to be resolved to 'l10n_cl_edi.SiiTypes_v10.xsd'.
"""
if not isinstance(tree_or_str, etree._Element):
tree_or_str = etree.fromstring(tree_or_str)
parser = etree.XMLParser()
if env:
parser.resolvers.add(odoo_resolver(env))
parser.resolvers.add(odoo_resolver(env, prefix))
if isinstance(stream, str) and stream.endswith('.xsd'):
attachment = env['ir.attachment'].search([('name', '=', stream)])
if not attachment:
@@ -259,7 +263,7 @@ def load_xsd_files_from_url(env, url, file_name, force_reload=False,
return fetched_attachment
def validate_xml_from_attachment(env, xml_content, xsd_name, reload_files_function=None):
def validate_xml_from_attachment(env, xml_content, xsd_name, reload_files_function=None, prefix=None):
"""Try and validate the XML content with an XSD attachment.
If the XSD attachment cannot be found in database, (re)load it.
@@ -275,7 +279,7 @@ def validate_xml_from_attachment(env, xml_content, xsd_name, reload_files_functi
if env.context.get('skip_xsd', False):
return
try:
_check_with_xsd(xml_content, xsd_name, env)
_check_with_xsd(xml_content, xsd_name, env, prefix)
except FileNotFoundError:
if not reload_files_function:
_logger.warning("You need to provide a function used to (re)load XSD files")