diff --git a/openapi_core/deserializing/parameters/factories.py b/openapi_core/deserializing/parameters/factories.py index f937446f..3d87589c 100644 --- a/openapi_core/deserializing/parameters/factories.py +++ b/openapi_core/deserializing/parameters/factories.py @@ -1,3 +1,4 @@ +import re from functools import partial from typing import Dict @@ -25,6 +26,7 @@ class ParameterDeserializersFactory: "simple": partial(split, separator=","), "spaceDelimited": partial(split, separator=" "), "pipeDelimited": partial(split, separator="|"), + "deepObject": partial(re.split, pattern=r"\[|\]"), } def create(self, param_or_header: Spec) -> BaseParameterDeserializer: diff --git a/openapi_core/schema/parameters.py b/openapi_core/schema/parameters.py index 30195c67..45baf229 100644 --- a/openapi_core/schema/parameters.py +++ b/openapi_core/schema/parameters.py @@ -1,3 +1,4 @@ +import re from typing import Any from typing import Dict from typing import Optional @@ -53,16 +54,39 @@ def get_value( ) -> Any: """Returns parameter/header value from specific location""" name = name or param_or_header["name"] + style = get_style(param_or_header) if name not in location: - raise KeyError + # Only check if the name is not in the location if the style of + # the param is deepObject,this is because deepObjects will never be found + # as their key also includes the properties of the object already. + if style != "deepObject": + raise KeyError + keys_str = " ".join(location.keys()) + if not re.search(rf"{name}\[\w+\]", keys_str): + raise KeyError aslist = get_aslist(param_or_header) explode = get_explode(param_or_header) if aslist and explode: + if style == "deepObject": + return get_deep_object_value(location, name) if isinstance(location, SuportsGetAll): return location.getall(name) if isinstance(location, SuportsGetList): return location.getlist(name) return location[name] + + +def get_deep_object_value( + location: Union[Headers, Dict[str, Any]], + name: Optional[str] = None, +) -> Dict[str, Any]: + values = {} + for key, value in location.items(): + # Split the key from the brackets. + key_split = re.split(pattern=r"\[|\]", string=key) + if key_split[0] == name: + values[key_split[1]] = value + return values diff --git a/tests/integration/validation/test_validators.py b/tests/integration/validation/test_validators.py index 220d3ede..4bb00c0e 100644 --- a/tests/integration/validation/test_validators.py +++ b/tests/integration/validation/test_validators.py @@ -28,7 +28,6 @@ class TestRequestValidator: - host_url = "http://petstore.swagger.io" api_key = "12345" @@ -528,9 +527,45 @@ def test_request_override_param_uniqueness(self, spec, spec_dict): assert result.body is None assert result.parameters == Parameters() + def test_request_object_deep_object_params(self, spec, spec_dict): + # override path parameter on operation + spec_dict["paths"]["/resource"]["parameters"] = [ + { + # full valid parameter object required + "name": "paramObj", + "in": "query", + "required": True, + "schema": { + "type": "object", + "properties": { + "count": {"type": "integer"}, + "name": {"type": "string"}, + }, + }, + "explode": True, + "style": "deepObject", + } + ] + + request = MockRequest( + "http://example.com", + "get", + "/resource", + args={"paramObj[count]": 2, "paramObj[name]": "John"}, + ) + result = openapi_request_validator.validate( + spec, request, base_url="http://example.com" + ) + + assert len(result.errors) == 0 + assert result.body is None + assert len(result.parameters.query) == 1 + assert is_dataclass(result.parameters.query["paramObj"]) + assert result.parameters.query["paramObj"].count == 2 + assert result.parameters.query["paramObj"].name == "John" -class TestResponseValidator: +class TestResponseValidator: host_url = "http://petstore.swagger.io" @pytest.fixture pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy