-
-
Notifications
You must be signed in to change notification settings - Fork 313
/
Copy pathtools.py
147 lines (125 loc) · 4.64 KB
/
tools.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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# Copyright 2018 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import inspect
import logging
from collections import OrderedDict
_logger = logging.getLogger(__name__)
# Decorator attribute added on a route function (cfr Odoo's route)
ROUTING_DECORATOR_ATTR = "original_routing"
SUPPORTED_META = ["title", "description", "example", "examples"]
def cerberus_to_json(schema):
"""Convert a Cerberus schema to a JSON schema"""
result = OrderedDict()
required = []
properties = OrderedDict()
result["type"] = "object"
result["required"] = required
result["properties"] = properties
for field, spec in list(schema.items()):
props = _get_field_props(spec)
properties[field] = props
if spec.get("required"):
required.append(field)
# sort required to get the same order on each run and easy comparison into
# the tests
required.sort()
return result
def _get_field_props(spec): # noqa: C901
resp = OrderedDict()
# dictionary of tuple (json type, json fromat) by cerberus type for
# cerberus types requiring a specific mapping to a json type/format
type_map = {
"dict": ("object",),
"list": ("array",),
"objectid": ("string", "objectid"),
"datetime": ("string", "date-time"),
"float": ("number", "float"),
}
_type = spec.get("type")
if _type is None:
return resp
if "description" in spec:
resp["description"] = spec["description"]
if "meta" in spec:
for key in SUPPORTED_META:
value = spec["meta"].get(key)
if value:
resp[key] = value
if "allowed" in spec:
resp["enum"] = spec["allowed"]
if "default" in spec:
resp["default"] = spec["default"]
if "minlength" in spec:
if _type == "string":
resp["minLength"] = spec["minlength"]
elif _type == "list":
resp["minItems"] = spec["minlength"]
if "maxlength" in spec:
if _type == "string":
resp["maxLength"] = spec["maxlength"]
elif _type == "list":
resp["maxItems"] = spec["maxlength"]
if "min" in spec:
if _type in ["number", "integer", "float"]:
resp["minimum"] = spec["min"]
if "max" in spec:
if _type in ["number", "integer", "float"]:
resp["maximum"] = spec["max"]
if "readonly" in spec:
resp["readOnly"] = spec["readonly"]
if "regex" in spec:
resp["pattern"] = spec["regex"]
if "nullable" in spec:
resp["nullable"] = spec["nullable"]
if "allowed" in spec:
resp["enum"] = spec["allowed"]
json_type = type_map.get(_type, (_type,))
resp["type"] = json_type[0]
if json_type[0] == "object":
if "schema" in spec:
resp.update(cerberus_to_json(spec["schema"]))
additional_properties = {}
if "keysrules" in spec:
rule_value_type = spec["keysrules"].get("type", "string")
if rule_value_type != "string":
_logger.debug(
"Openapi only support key/value mapping definition where"
" the keys are strings. Received %s",
rule_value_type,
)
if "valuesrules" in spec:
values_rules = spec["valuesrules"]
rule_value_type = values_rules.get("type", "string")
additional_properties["type"] = rule_value_type
if "schema" in values_rules:
additional_properties.update(cerberus_to_json(values_rules["schema"]))
if additional_properties:
resp["additionalProperties"] = additional_properties
elif json_type[0] == "array":
if "schema" in spec:
resp["items"] = _get_field_props(spec["schema"])
else:
resp["items"] = {"type": "string"}
else:
try:
resp["format"] = json_type[1]
# pylint:disable=except-pass
except IndexError:
pass
return resp
def _inspect_methods(cls):
"""Return all methods of a given class as (name, value) pairs sorted by
name.
inspect.getmembers was initially used. Unfortunately, instance's properties
was accessed into the loop and could raise some exception since we are
into the startup process and all the resources are not yet initialized.
"""
results = []
for attribute in inspect.classify_class_attrs(cls):
if attribute.kind != "method":
continue
name = attribute.name
method = getattr(cls, name)
results.append((name, method))
results.sort(key=lambda pair: pair[0])
return results