Newer
Older
##############################################################################
#
# Converter Odoo module
# Copyright © 2020, 2024 XCG Consulting <https://xcg-consulting.fr>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

Vincent Hatakeyama
committed
import logging

Vincent Hatakeyama
committed
from collections.abc import Callable

Vincent Hatakeyama
committed
from importlib import import_module
from odoo.exceptions import UserError # type: ignore[import-untyped]
_fastjsonschema: None | Exception = None
try:
import fastjsonschema # type: ignore[import-untyped]
except ImportError as e:
_fastjsonschema = e

Vincent Hatakeyama
committed
_logger = logging.getLogger(__name__)
"""Type of validation"""
SKIP = "skip"
SOFT = "soft"
STRICT = "strict"
class NotInitialized(Exception):
pass

Vincent Hatakeyama
committed
def _add_schema(schemas, schema):
if "$id" in schema:
schemas[schema["$id"]] = schema
else:
_logger.warning("Schema without $id (schema ignored)")
class Validator:
def __init__(
self,

Vincent Hatakeyama
committed
package_name: str,
default_url_pattern: str,

Vincent Hatakeyama
committed
directory: str | None = None,
):

Vincent Hatakeyama
committed
"""
:param package_name: Package where the schema can be found
:param default_url_pattern: pattern for url ({} will be replaced by $id)
:param directory: directory to search for json, not used if a get_schema is
provided in the package.
"""
self.package_name = package_name
# exemple "https://annonces-legales.fr/xbus/schemas/v1/{}.schema.json"
self.default_url_pattern = default_url_pattern

Vincent Hatakeyama
committed
self.validators: dict[LiteralString, Callable] = {}
self.initialized = False
self.encoding = "UTF-8"

Vincent Hatakeyama
committed
self.directory = directory

Vincent Hatakeyama
committed
def initialize(self) -> None:

Vincent Hatakeyama
committed
if self.initialized:
return
schemas: dict[LiteralString, Any] = {}
module = import_module(self.package_name)
if hasattr(module, "get_schemas"):
for schema in module.get_schemas():
_add_schema(schemas, schema)
else:
if module.__file__ is None:
# XXX maybe not the best type of error
raise UserError("Module %s has no file", self.package_name)

Vincent Hatakeyama
committed
# Fallback on searching schema json files
schema_search_path = os.path.dirname(module.__file__)
schema_search_path = os.path.abspath(
os.path.join(schema_search_path, self.directory)
if self.directory is not None
else schema_search_path
)
for root, _dirs, files in os.walk(schema_search_path):
for fname in files:
fpath = os.path.join(root, fname)
if fpath.endswith((".json",)):
with open(fpath, encoding=self.encoding) as schema_fd:
schema = json.load(schema_fd)
_add_schema(schemas, schema)
# Prepare validators for each schema. We add an HTTPS handler that
# points back to our schema definition cache built above.
for schema_id, schema in schemas.items():
if _fastjsonschema is not None:
raise _fastjsonschema
self.validators[schema_id] = fastjsonschema.compile(
schema,
handlers={"https": lambda uri: schemas[uri]},
use_default=False,
)
self.initialized = True
if not self.initialized:
raise NotInitialized("please call the initialize() method")
self.validators[self.default_url_pattern.format(schema_id)](payload)