forked from OCA/rest-framework
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrestapi.py
100 lines (83 loc) · 3.42 KB
/
restapi.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# 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)
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