diff --git a/openapi_schema_validator/_validators.py b/openapi_schema_validator/_validators.py index 132b3f5..2f25660 100644 --- a/openapi_schema_validator/_validators.py +++ b/openapi_schema_validator/_validators.py @@ -18,7 +18,7 @@ def format(validator, format, instance, schema): try: validator.format_checker.check(instance, format) except FormatError as error: - yield ValidationError(error.message, cause=error.cause) + yield ValidationError(str(error), cause=error.cause) def items(validator, items, instance, schema): @@ -40,11 +40,14 @@ def required(validator, required, instance, schema): return for property in required: if property not in instance: - prop_schema = schema['properties'][property] - read_only = prop_schema.get('readOnly', False) - write_only = prop_schema.get('writeOnly', False) - if validator.write and read_only or validator.read and write_only: - continue + prop_schema = schema.get('properties', {}).get(property) + if prop_schema: + read_only = prop_schema.get('readOnly', False) + write_only = prop_schema.get('writeOnly', False) + if ( + validator.write and read_only or + validator.read and write_only): + continue yield ValidationError("%r is a required property" % property) diff --git a/tests/integration/test_validators.py b/tests/integration/test_validators.py index 246133d..de3b5ab 100644 --- a/tests/integration/test_validators.py +++ b/tests/integration/test_validators.py @@ -146,3 +146,95 @@ def test_string_uuid(self, value): result = validator.validate(value) assert result is None + + def test_allof_required(self): + schema = { + "allOf": [ + {"type": "object", + "properties": { + "some_prop": {"type": "string"}}}, + {"type": "object", "required": ["some_prop"]}, + ] + } + validator = OAS30Validator(schema, format_checker=oas30_format_checker) + with pytest.raises(ValidationError, + match="'some_prop' is a required property"): + validator.validate({"another_prop": "bla"}) + + def test_required(self): + schema = { + "type": "object", + "properties": { + "some_prop": { + "type": "string" + } + }, + "required": ["some_prop"] + } + + validator = OAS30Validator(schema, format_checker=oas30_format_checker) + with pytest.raises(ValidationError, + match="'some_prop' is a required property"): + validator.validate({"another_prop": "bla"}) + assert validator.validate({"some_prop": "hello"}) is None + + def test_required_read_only(self): + schema = { + "type": "object", + "properties": { + "some_prop": { + "type": "string", + "readOnly": True + } + }, + "required": ["some_prop"] + } + + validator = OAS30Validator(schema, format_checker=oas30_format_checker, + read=True) + with pytest.raises(ValidationError, + match="'some_prop' is a required property"): + validator.validate({"another_prop": "hello"}) + validator = OAS30Validator(schema, format_checker=oas30_format_checker, + write=True) + assert validator.validate({"another_prop": "hello"}) is None + + def test_required_write_only(self): + schema = { + "type": "object", + "properties": { + "some_prop": { + "type": "string", + "writeOnly": True + } + }, + "required": ["some_prop"] + } + + validator = OAS30Validator(schema, format_checker=oas30_format_checker, + write=True) + with pytest.raises(ValidationError, + match="'some_prop' is a required property"): + validator.validate({"another_prop": "hello"}) + validator = OAS30Validator(schema, format_checker=oas30_format_checker, + read=True) + assert validator.validate({"another_prop": "hello"}) is None + + def test_oneof_required(self): + instance = { + 'n3IwfId': 'string', + } + schema = { + "type": "object", + "properties": { + "n3IwfId": {"type": "string"}, + "wagfId": {"type": "string"}, + }, + "oneOf": [ + {"required": ["n3IwfId"]}, + {"required": ["wagfId"]}, + ], + } + validator = OAS30Validator(schema, format_checker=oas30_format_checker) + result = validator.validate(instance) + assert result is None