Skip to content

Commit d084df1

Browse files
Yarn-ep1c2u
authored andcommitted
Add support for deepObject parameter style
1 parent 868c081 commit d084df1

File tree

3 files changed

+57
-3
lines changed

3 files changed

+57
-3
lines changed

openapi_core/deserializing/parameters/factories.py

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import re
12
from functools import partial
23
from typing import Dict
34

@@ -25,6 +26,7 @@ class ParameterDeserializersFactory:
2526
"simple": partial(split, separator=","),
2627
"spaceDelimited": partial(split, separator=" "),
2728
"pipeDelimited": partial(split, separator="|"),
29+
"deepObject": partial(re.split, pattern=r"\[|\]"),
2830
}
2931

3032
def create(self, param_or_header: Spec) -> BaseParameterDeserializer:

openapi_core/schema/parameters.py

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import re
12
from typing import Any
23
from typing import Dict
34
from typing import Optional
@@ -53,13 +54,28 @@ def get_value(
5354
) -> Any:
5455
"""Returns parameter/header value from specific location"""
5556
name = name or param_or_header["name"]
57+
style = get_style(param_or_header)
5658

57-
if name not in location:
59+
# Only check if the name is not in the location if the style of the param
60+
# is not deepObject, this is because deepObjects will never be found
61+
# as their key also includes the properties of the object already.
62+
if style != "deepObject" and name not in location:
5863
raise KeyError
5964

6065
aslist = get_aslist(param_or_header)
6166
explode = get_explode(param_or_header)
6267
if aslist and explode:
68+
if style == "deepObject":
69+
values = {}
70+
for key, value in location.items():
71+
# Split the key from the brackets.
72+
key_split = re.split(pattern=r"\[|\]", string=key)
73+
if key_split[0] == name:
74+
values[key_split[1]] = value
75+
# If we don't find any values raise a KeyError as this means that the key is missing.
76+
if not values:
77+
raise KeyError
78+
return values
6379
if isinstance(location, SuportsGetAll):
6480
return location.getall(name)
6581
if isinstance(location, SuportsGetList):

tests/integration/validation/test_validators.py

+38-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828

2929

3030
class TestRequestValidator:
31-
3231
host_url = "http://petstore.swagger.io"
3332

3433
api_key = "12345"
@@ -528,9 +527,46 @@ def test_request_override_param_uniqueness(self, spec, spec_dict):
528527
assert result.body is None
529528
assert result.parameters == Parameters()
530529

530+
def test_request_object_deepObject_params(self, spec_dict):
531+
# override path parameter on operation
532+
spec_dict["paths"]["/resource"]["parameters"] = [
533+
{
534+
# full valid parameter object required
535+
"name": "paramObj",
536+
"in": "query",
537+
"required": True,
538+
"schema": {
539+
"type": "object",
540+
"properties": {
541+
"count": {"type": "integer"},
542+
"name": {"type": "string"},
543+
},
544+
},
545+
"explode": True,
546+
"style": "deepObject",
547+
}
548+
]
549+
550+
validator = RequestValidator(
551+
create_spec(spec_dict),
552+
base_url="http://example.com",
553+
)
554+
request = MockRequest(
555+
"http://example.com",
556+
"get",
557+
"/resource",
558+
args={"paramObj[count]": 2, "paramObj[name]": "John"},
559+
)
560+
result = validator.validate(request)
561+
562+
assert len(result.errors) == 0
563+
assert result.body is None
564+
assert result.parameters == Parameters(
565+
query={"paramObj": {"count": 2, "name": "John"}}
566+
)
531567

532-
class TestResponseValidator:
533568

569+
class TestResponseValidator:
534570
host_url = "http://petstore.swagger.io"
535571

536572
@pytest.fixture

0 commit comments

Comments
 (0)