Skip to content

Commit e7d6608

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

File tree

6 files changed

+127
-77
lines changed

6 files changed

+127
-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

+114-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,74 @@ 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(
103126
self,
104127
param_or_header: Spec,
105128
location: Mapping[str, Any],
106129
name: Optional[str] = None,
130+
) -> Any:
131+
try:
132+
raw = self._get_style_value(param_or_header, location, name=name)
133+
except KeyError:
134+
if "schema" not in param_or_header:
135+
raise
136+
schema = param_or_header / "schema"
137+
if "default" not in schema:
138+
raise
139+
raw = schema["default"]
140+
return self._get_param_or_header_value(raw, param_or_header)
141+
142+
def _get_complex(
143+
self,
144+
param_or_header: Spec,
145+
location: Mapping[str, Any],
146+
name: Optional[str] = None,
147+
) -> Any:
148+
content = param_or_header / "content"
149+
try:
150+
raw = self._get_media_type_value(
151+
param_or_header, location, name=name
152+
)
153+
except KeyError:
154+
if "schema" not in param_or_header:
155+
raise
156+
schema = param_or_header / "schema"
157+
if "default" not in schema:
158+
raise
159+
raw = schema["default"]
160+
return self._get_content_value(raw, content)
161+
162+
def _get_param_or_header_value(
163+
self,
164+
raw: Any,
165+
param_or_header: Spec,
107166
) -> Any:
108167
casted, schema = self._get_param_or_header_value_and_schema(
109-
param_or_header, location, name
168+
raw, param_or_header
110169
)
111170
if schema is None:
112171
return casted
113172
self._validate_schema(schema, casted)
114173
return casted
115174

116175
def _get_content_value(
117-
self, raw: Any, mimetype: str, content: Spec
176+
self, raw: Any, content: Spec, mimetype: Optional[str] = None
118177
) -> Any:
119178
casted, schema = self._get_content_value_and_schema(
120-
raw, mimetype, content
179+
raw, content, mimetype
121180
)
122181
if schema is None:
123182
return casted
@@ -126,42 +185,22 @@ def _get_content_value(
126185

127186
def _get_param_or_header_value_and_schema(
128187
self,
188+
raw: Any,
129189
param_or_header: Spec,
130-
location: Mapping[str, Any],
131-
name: Optional[str] = None,
132190
) -> 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)
191+
deserialised = self._deserialise_style(param_or_header, raw)
192+
schema = param_or_header / "schema"
193+
casted = self._cast(schema, deserialised)
158194
return casted, schema
159195

160196
def _get_content_value_and_schema(
161-
self, raw: Any, mimetype: str, content: Spec
197+
self,
198+
raw: Any,
199+
content: Spec,
200+
mimetype: Optional[str] = None,
162201
) -> Tuple[Any, Optional[Spec]]:
163-
media_type, mimetype = self._get_media_type(content, mimetype)
164-
deserialised = self._deserialise_media_type(mimetype, raw)
202+
media_type, mime_type = self._get_media_type(content, mimetype)
203+
deserialised = self._deserialise_media_type(mime_type, raw)
165204
casted = self._cast(media_type, deserialised)
166205

167206
if "schema" not in media_type:
@@ -170,6 +209,45 @@ def _get_content_value_and_schema(
170209
schema = media_type / "schema"
171210
return casted, schema
172211

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

174252
class BaseAPICallValidator(BaseValidator):
175253
@cached_property

0 commit comments

Comments
 (0)