Skip to content

Commit ff608f7

Browse files
committed
Parameter and header get value refactor
1 parent 0da2a38 commit ff608f7

File tree

6 files changed

+118
-77
lines changed

6 files changed

+118
-77
lines changed

openapi_core/schema/parameters.py

-32
Original file line numberDiff line numberDiff line change
@@ -45,38 +45,6 @@ def get_explode(param_or_header: Spec) -> bool:
4545
return style == "form"
4646

4747

48-
def get_value(
49-
param_or_header: Spec,
50-
location: Mapping[str, Any],
51-
name: Optional[str] = None,
52-
) -> Any:
53-
"""Returns parameter/header value from specific location"""
54-
name = name or param_or_header["name"]
55-
style = get_style(param_or_header)
56-
57-
if name not in location:
58-
# Only check if the name is not in the location if the style of
59-
# the param is deepObject,this is because deepObjects will never be found
60-
# as their key also includes the properties of the object already.
61-
if style != "deepObject":
62-
raise KeyError
63-
keys_str = " ".join(location.keys())
64-
if not re.search(rf"{name}\[\w+\]", keys_str):
65-
raise KeyError
66-
67-
aslist = get_aslist(param_or_header)
68-
explode = get_explode(param_or_header)
69-
if aslist and explode:
70-
if style == "deepObject":
71-
return get_deep_object_value(location, name)
72-
if isinstance(location, SuportsGetAll):
73-
return location.getall(name)
74-
if isinstance(location, SuportsGetList):
75-
return location.getlist(name)
76-
77-
return location[name]
78-
79-
8048
def get_deep_object_value(
8149
location: Mapping[str, Any],
8250
name: Optional[str] = None,

openapi_core/templating/media_types/finders.py

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ class MediaTypeFinder:
1010
def __init__(self, content: Spec):
1111
self.content = content
1212

13+
def get_first(self) -> MediaType:
14+
mimetype, media_type = next(self.content.items())
15+
return MediaType(media_type, mimetype)
16+
1317
def find(self, mimetype: str) -> MediaType:
1418
if mimetype in self.content:
1519
return MediaType(self.content / mimetype, mimetype)

openapi_core/unmarshalling/unmarshallers.py

+4-5
Original file line numberDiff line numberDiff line change
@@ -89,22 +89,21 @@ def _unmarshal_schema(self, schema: Spec, value: Any) -> Any:
8989

9090
def _get_param_or_header_value(
9191
self,
92+
raw: Any,
9293
param_or_header: Spec,
93-
location: Mapping[str, Any],
94-
name: Optional[str] = None,
9594
) -> Any:
9695
casted, schema = self._get_param_or_header_value_and_schema(
97-
param_or_header, location, name
96+
raw, param_or_header
9897
)
9998
if schema is None:
10099
return casted
101100
return self._unmarshal_schema(schema, casted)
102101

103102
def _get_content_value(
104-
self, raw: Any, mimetype: str, content: Spec
103+
self, raw: Any, content: Spec, mimetype: Optional[str] = None
105104
) -> Any:
106105
casted, schema = self._get_content_value_and_schema(
107-
raw, mimetype, content
106+
raw, content, mimetype
108107
)
109108
if schema is None:
110109
return casted

openapi_core/validation/request/validators.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,9 @@ def _get_parameter(
189189

190190
param_location = param["in"]
191191
location = parameters[param_location]
192+
192193
try:
193-
return self._get_param_or_header_value(param, location)
194+
return self._get_param_or_header(param, location, name=name)
194195
except KeyError:
195196
required = param.getkey("required", False)
196197
if required:
@@ -248,7 +249,7 @@ def _get_body(
248249
content = request_body / "content"
249250

250251
raw_body = self._get_body_value(body, request_body)
251-
return self._get_content_value(raw_body, mimetype, content)
252+
return self._get_content_value(raw_body, content, mimetype)
252253

253254
def _get_body_value(self, body: Optional[str], request_body: Spec) -> Any:
254255
if not body:

openapi_core/validation/response/validators.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def _get_data(
114114
content = operation_response / "content"
115115

116116
raw_data = self._get_data_value(data)
117-
return self._get_content_value(raw_data, mimetype, content)
117+
return self._get_content_value(raw_data, content, mimetype)
118118

119119
def _get_data_value(self, data: str) -> Any:
120120
if not data:
@@ -163,7 +163,7 @@ def _get_header(
163163
)
164164

165165
try:
166-
return self._get_param_or_header_value(header, headers, name=name)
166+
return self._get_param_or_header(header, headers, name=name)
167167
except KeyError:
168168
required = header.getkey("required", False)
169169
if required:

openapi_core/validation/validators.py

+105-36
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""OpenAPI core validation validators module"""
2+
import re
23
from functools import cached_property
34
from typing import Any
45
from typing import Mapping
@@ -23,7 +24,12 @@
2324
)
2425
from openapi_core.protocols import Request
2526
from openapi_core.protocols import WebhookRequest
26-
from openapi_core.schema.parameters import get_value
27+
from openapi_core.schema.parameters import get_aslist
28+
from openapi_core.schema.parameters import get_deep_object_value
29+
from openapi_core.schema.parameters import get_explode
30+
from openapi_core.schema.parameters import get_style
31+
from openapi_core.schema.protocols import SuportsGetAll
32+
from openapi_core.schema.protocols import SuportsGetList
2733
from openapi_core.spec import Spec
2834
from openapi_core.templating.media_types.datatypes import MediaType
2935
from openapi_core.templating.paths.datatypes import PathOperationServer
@@ -70,10 +76,14 @@ def __init__(
7076
self.extra_format_validators = extra_format_validators
7177
self.extra_media_type_deserializers = extra_media_type_deserializers
7278

73-
def _get_media_type(self, content: Spec, mimetype: str) -> MediaType:
79+
def _get_media_type(
80+
self, content: Spec, mimetype: Optional[str] = None
81+
) -> MediaType:
7482
from openapi_core.templating.media_types.finders import MediaTypeFinder
7583

7684
finder = MediaTypeFinder(content)
85+
if mimetype is None:
86+
return finder.get_first()
7787
return finder.find(mimetype)
7888

7989
def _deserialise_media_type(self, mimetype: str, value: Any) -> Any:
@@ -99,25 +109,65 @@ def _validate_schema(self, schema: Spec, value: Any) -> None:
99109
)
100110
validator.validate(value)
101111

102-
def _get_param_or_header_value(
112+
def _get_param_or_header(
113+
self,
114+
param_or_header: Spec,
115+
location: Mapping[str, Any],
116+
name: Optional[str] = None,
117+
) -> Any:
118+
# Simple scenario
119+
if "content" not in param_or_header:
120+
return self._get_simple_value(param_or_header, location, name=name)
121+
122+
# Complex scenario
123+
return self._get_complex(param_or_header, location, name=name)
124+
125+
def _get_simple_value(
126+
self,
127+
param_or_header: Spec,
128+
location: Mapping[str, Any],
129+
name: Optional[str] = None,
130+
) -> Any:
131+
try:
132+
raw = self._get_style_value(param_or_header, location, name=name)
133+
except KeyError:
134+
# in simple scenrios schema always exist
135+
schema = param_or_header / "schema"
136+
if "default" not in schema:
137+
raise
138+
raw = schema["default"]
139+
return self._get_param_or_header_value(raw, param_or_header)
140+
141+
def _get_complex(
103142
self,
104143
param_or_header: Spec,
105144
location: Mapping[str, Any],
106145
name: Optional[str] = None,
146+
) -> Any:
147+
content = param_or_header / "content"
148+
# no point to catch KetError
149+
# in complex scenrios schema doesn't exist
150+
raw = self._get_media_type_value(param_or_header, location, name=name)
151+
return self._get_content_value(raw, content)
152+
153+
def _get_param_or_header_value(
154+
self,
155+
raw: Any,
156+
param_or_header: Spec,
107157
) -> Any:
108158
casted, schema = self._get_param_or_header_value_and_schema(
109-
param_or_header, location, name
159+
raw, param_or_header
110160
)
111161
if schema is None:
112162
return casted
113163
self._validate_schema(schema, casted)
114164
return casted
115165

116166
def _get_content_value(
117-
self, raw: Any, mimetype: str, content: Spec
167+
self, raw: Any, content: Spec, mimetype: Optional[str] = None
118168
) -> Any:
119169
casted, schema = self._get_content_value_and_schema(
120-
raw, mimetype, content
170+
raw, content, mimetype
121171
)
122172
if schema is None:
123173
return casted
@@ -126,42 +176,22 @@ def _get_content_value(
126176

127177
def _get_param_or_header_value_and_schema(
128178
self,
179+
raw: Any,
129180
param_or_header: Spec,
130-
location: Mapping[str, Any],
131-
name: Optional[str] = None,
132181
) -> Tuple[Any, Spec]:
133-
try:
134-
raw_value = get_value(param_or_header, location, name=name)
135-
except KeyError:
136-
if "schema" not in param_or_header:
137-
raise
138-
schema = param_or_header / "schema"
139-
if "default" not in schema:
140-
raise
141-
casted = schema["default"]
142-
else:
143-
# Simple scenario
144-
if "content" not in param_or_header:
145-
deserialised = self._deserialise_style(
146-
param_or_header, raw_value
147-
)
148-
schema = param_or_header / "schema"
149-
# Complex scenario
150-
else:
151-
content = param_or_header / "content"
152-
mimetype, media_type = next(content.items())
153-
deserialised = self._deserialise_media_type(
154-
mimetype, raw_value
155-
)
156-
schema = media_type / "schema"
157-
casted = self._cast(schema, deserialised)
182+
deserialised = self._deserialise_style(param_or_header, raw)
183+
schema = param_or_header / "schema"
184+
casted = self._cast(schema, deserialised)
158185
return casted, schema
159186

160187
def _get_content_value_and_schema(
161-
self, raw: Any, mimetype: str, content: Spec
188+
self,
189+
raw: Any,
190+
content: Spec,
191+
mimetype: Optional[str] = None,
162192
) -> Tuple[Any, Optional[Spec]]:
163-
media_type, mimetype = self._get_media_type(content, mimetype)
164-
deserialised = self._deserialise_media_type(mimetype, raw)
193+
media_type, mime_type = self._get_media_type(content, mimetype)
194+
deserialised = self._deserialise_media_type(mime_type, raw)
165195
casted = self._cast(media_type, deserialised)
166196

167197
if "schema" not in media_type:
@@ -170,6 +200,45 @@ def _get_content_value_and_schema(
170200
schema = media_type / "schema"
171201
return casted, schema
172202

203+
def _get_style_value(
204+
self,
205+
param_or_header: Spec,
206+
location: Mapping[str, Any],
207+
name: Optional[str] = None,
208+
) -> Any:
209+
name = name or param_or_header["name"]
210+
style = get_style(param_or_header)
211+
if name not in location:
212+
# Only check if the name is not in the location if the style of
213+
# the param is deepObject,this is because deepObjects will never be found
214+
# as their key also includes the properties of the object already.
215+
if style != "deepObject":
216+
raise KeyError
217+
keys_str = " ".join(location.keys())
218+
if not re.search(rf"{name}\[\w+\]", keys_str):
219+
raise KeyError
220+
221+
aslist = get_aslist(param_or_header)
222+
explode = get_explode(param_or_header)
223+
if aslist and explode:
224+
if style == "deepObject":
225+
return get_deep_object_value(location, name)
226+
if isinstance(location, SuportsGetAll):
227+
return location.getall(name)
228+
if isinstance(location, SuportsGetList):
229+
return location.getlist(name)
230+
231+
return location[name]
232+
233+
def _get_media_type_value(
234+
self,
235+
param_or_header: Spec,
236+
location: Mapping[str, Any],
237+
name: Optional[str] = None,
238+
) -> Any:
239+
name = name or param_or_header["name"]
240+
return location[name]
241+
173242

174243
class BaseAPICallValidator(BaseValidator):
175244
@cached_property

0 commit comments

Comments
 (0)