Skip to content

Commit e213079

Browse files
Andrew Piklerpyckle
Andrew Pikler
authored andcommitted
Fix #20 - nullable semantics with $ref, oneOf, anyOf, and allOf
1 parent 553d606 commit e213079

File tree

2 files changed

+103
-2
lines changed

2 files changed

+103
-2
lines changed

openapi_schema_validator/_validators.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,12 @@ def include_nullable_validator(
2727
"""
2828
_schema = deepcopy(schema)
2929

30-
# append defaults to trigger nullable validator
31-
if "nullable" not in _schema:
30+
# append defaults to trigger nullable validator, except where $ref in the schema checks null
31+
if "nullable" not in _schema \
32+
and "$ref" not in _schema \
33+
and "oneOf" not in _schema \
34+
and "anyOf" not in _schema \
35+
and "allOf" not in _schema:
3236
_schema.update(
3337
{
3438
"nullable": False,

tests/integration/test_validators.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,103 @@ def test_oneof_discriminator(self, schema_type):
442442
result = validator.validate({"discipline": "other"})
443443
assert False
444444

445+
@pytest.mark.parametrize("is_nullable", [True, False])
446+
def test_nullable_ref(self, is_nullable):
447+
"""
448+
Tests that a field that points to a schema reference is null checked based on the $ref schema rather than
449+
on this schema
450+
:param is_nullable: if the schema is marked as nullable. If not, validate an exception is raised on None
451+
"""
452+
schema = {
453+
"$ref": "#/$defs/Pet",
454+
"$defs": {
455+
"NullableText": {
456+
"type": "string",
457+
"nullable": is_nullable
458+
},
459+
"Pet": {
460+
"properties": {
461+
"testfield": {"$ref": "#/$defs/NullableText"},
462+
},
463+
}
464+
},
465+
}
466+
validator = OAS30Validator(
467+
schema,
468+
format_checker=oas30_format_checker,
469+
)
470+
471+
result = validator.validate({"testfield": "John"})
472+
assert result is None
473+
474+
if is_nullable:
475+
result = validator.validate({"testfield": None})
476+
assert result is None
477+
else:
478+
with pytest.raises(
479+
ValidationError,
480+
match="None for not nullable",
481+
):
482+
validator.validate({"testfield": None})
483+
assert False
484+
485+
486+
@pytest.mark.parametrize(
487+
"schema_type, not_nullable_regex",
488+
[
489+
("oneOf", "None is not valid under any of the given schemas"),
490+
("anyOf", "None is not valid under any of the given schemas"),
491+
("allOf", "None for not nullable")
492+
],
493+
)
494+
@pytest.mark.parametrize("is_nullable", [True, False])
495+
def test_nullable_schema_combos(self, is_nullable, schema_type, not_nullable_regex):
496+
"""
497+
This test ensures that nullablilty semantics are correct for oneOf, anyOf and allOf
498+
Specifically, nullable should checked on the children schemas
499+
:param is_nullable: if the schema is marked as nullable. If not, validate an exception is raised on None
500+
:param schema_type: the schema type to validate
501+
:param not_nullable_regex: the expected raised exception if fields are marked as not nullable
502+
"""
503+
schema = {
504+
"$ref": "#/$defs/Pet",
505+
"$defs": {
506+
"NullableText": {
507+
"type": "string",
508+
"nullable": False if schema_type == "oneOf" else is_nullable
509+
},
510+
"NullableEnum": {
511+
"type": "string",
512+
"nullable": is_nullable,
513+
"enum": ["John", "Alice", None]
514+
},
515+
"Pet": {
516+
"properties": {
517+
"testfield": {
518+
schema_type: [
519+
{"$ref": "#/$defs/NullableText"},
520+
{"$ref": "#/$defs/NullableEnum"},
521+
]
522+
}
523+
},
524+
}
525+
},
526+
}
527+
validator = OAS30Validator(
528+
schema,
529+
format_checker=oas30_format_checker,
530+
)
531+
532+
if is_nullable:
533+
result = validator.validate({"testfield": None})
534+
assert result is None
535+
else:
536+
with pytest.raises(
537+
ValidationError,
538+
match=not_nullable_regex
539+
):
540+
validator.validate({"testfield": None})
541+
assert False
445542

446543
class TestOAS31ValidatorValidate:
447544
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)