oca-technical/odoo-bringout-oca-rest-framework-base_rest_datamodel/base_rest_datamodel/restapi.py
2025-08-29 15:43:03 +02:00

100 lines
3.4 KiB
Python

# Copyright 2020 ACSONE SA/NV
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
import marshmallow
from apispec.ext.marshmallow.openapi import OpenAPIConverter
from marshmallow.exceptions import ValidationError
from odoo import _
from odoo.exceptions import UserError
from odoo.addons.base_rest import restapi
class Datamodel(restapi.RestMethodParam):
def __init__(self, name, is_list=False, partial=None):
"""
:param name: The datamodel name
:param is_list: Should be set to True if params is a collection so that
the object will be de/serialized from/to a list
:param partial: Whether to ignore missing fields and not require
any fields declared. Propagates down to ``Nested`` fields as well. If
its value is an iterable, only missing fields listed in that iterable
will be ignored. Use dot delimiters to specify nested fields.
"""
self._name = name
self._is_list = is_list
self._partial = partial
def from_params(self, service, params):
ModelClass = service.env.datamodels[self._name]
try:
return ModelClass.load(
params,
many=self._is_list,
unknown=marshmallow.EXCLUDE,
partial=self._partial,
)
except ValidationError as ve:
raise UserError(_("BadRequest %s") % ve.messages) from ve
def to_response(self, service, result):
ModelClass = service.env.datamodels[self._name]
if self._is_list:
json = [i.dump() for i in result]
else:
json = result.dump()
errors = ModelClass.validate(
json, many=self._is_list, unknown=marshmallow.EXCLUDE
)
if errors:
raise SystemError(_("Invalid Response %s") % errors)
return json
def to_openapi_query_parameters(self, service, spec):
converter = self._get_converter()
schema = self._get_schema(service)
return converter.schema2parameters(schema, location="query")
# TODO, we should probably get the spec as parameters. That should
# allows to add the definition of a schema only once into the specs
# and use a reference to the schema into the parameters
def to_openapi_requestbody(self, service, spec):
return {
"content": {
"application/json": {
"schema": self.to_json_schema(service, spec, "input")
}
}
}
def to_openapi_responses(self, service, spec):
return {
"200": {
"content": {
"application/json": {
"schema": self.to_json_schema(service, spec, "output")
}
}
}
}
def to_json_schema(self, service, spec, direction):
converter = self._get_converter()
schema = self._get_schema(service)
return converter.resolve_nested_schema(schema)
def _get_schema(self, service):
return service.env.datamodels[self._name].get_schema(many=self._is_list)
def _get_converter(self):
return OpenAPIConverter("3.0", self._schema_name_resolver, None)
def _schema_name_resolver(self, schema):
# name resolver used by the OpenapiConverter. always return None
# to force nested schema definition
return None
restapi.Datamodel = Datamodel