Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion openapi_core/deserializing/media_types/deserializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,11 @@ def iter_composed_all_of_schemas(
assert self.schema_validator is not None

if not self.mimetype.startswith("multipart"):
return list(self.schema_validator.iter_all_of_schemas(location))
return list(
self.schema_validator.iter_all_of_schemas(
location, caster=self.schema_caster
)
)

if self.schema is None or "allOf" not in self.schema:
return []
Expand Down
19 changes: 18 additions & 1 deletion openapi_core/validation/schemas/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,7 @@ def iter_any_of_schemas(
def iter_all_of_schemas(
self,
value: Any,
caster: Optional["SchemaCaster"] = None,
) -> Iterator[SchemaPath]:
if "allOf" not in self.schema:
return
Expand All @@ -438,7 +439,23 @@ def iter_all_of_schemas(
continue
validator = self.evolve(subschema)
try:
validator.validate(value)
test_value = value
# Only cast if caster provided (opt-in behavior). Useful for
# form-encoded data where scalar values arrive as strings and
# need casting before validation against non-string schemas.
if caster is not None:
try:
# Convert to dict if it's not exactly a plain dict
if type(value) is not dict:
test_value = dict(value)
else:
test_value = value
test_value = caster.evolve(subschema).cast(test_value)
except (ValueError, TypeError, Exception):
# If casting fails, validate with the original value
test_value = value

validator.validate(test_value)
except ValidateError:
log.warning("invalid allOf schema found")
else:
Expand Down
38 changes: 38 additions & 0 deletions tests/unit/deserializing/test_media_types_deserializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,44 @@ def test_urlencoded_oneof_string_field(self, spec, deserializer_factory):
"fieldA": "value",
}

def test_urlencoded_allof_non_string_field(
self, spec, deserializer_factory
):
"""Test issue #1212: allOf with urlencoded must not drop a subschema
that has a non-string (e.g. boolean) field."""
mimetype = "application/x-www-form-urlencoded"
schema_dict = {
"allOf": [
{
"type": "object",
"properties": {
"enabled": {"type": "boolean"},
"label": {"type": "string"},
},
},
{
"type": "object",
"properties": {"name": {"type": "string"}},
},
]
}
schema = SchemaPath.from_dict(schema_dict)
schema_validator = oas31_schema_validators_factory.create(
spec, schema
)
deserializer = deserializer_factory(
mimetype, schema=schema, schema_validator=schema_validator
)
value = b"name=widget&label=hello&enabled=true"

result = deserializer.deserialize(value)

assert result == {
"name": "widget",
"label": "hello",
"enabled": True,
}

def test_urlencoded_anyof_with_types(self, spec, deserializer_factory):
"""Test anyOf with urlencoded and type coercion"""
mimetype = "application/x-www-form-urlencoded"
Expand Down