Skip to content

Commit 29ad281

Browse files
authored
Merge pull request #39 from p1c2u/dtkav-consistent_errors
Validation errors wrapper
2 parents 7642146 + aae4963 commit 29ad281

File tree

5 files changed

+47
-17
lines changed

5 files changed

+47
-17
lines changed

openapi_spec_validator/decorators.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""OpenAPI spec validator decorators module."""
2+
from functools import wraps
23
import logging
34

45
from openapi_spec_validator.managers import VisitingManager
@@ -43,3 +44,21 @@ def _attach_scope(self, instance):
4344
return
4445

4546
instance['x-scope'] = list(self.instance_resolver._scopes_stack)
47+
48+
49+
class ValidationErrorWrapper(object):
50+
51+
def __init__(self, error_class):
52+
self.error_class = error_class
53+
54+
def __call__(self, f):
55+
@wraps(f)
56+
def wrapper(*args, **kwds):
57+
errors = f(*args, **kwds)
58+
for err in errors:
59+
if not isinstance(err, self.error_class):
60+
# wrap other exceptions with library specific version
61+
yield self.error_class.create_from(err)
62+
else:
63+
yield err
64+
return wrapper

openapi_spec_validator/validators.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@
66

77
from openapi_spec_validator.exceptions import (
88
ParameterDuplicateError, ExtraParametersError, UnresolvableParameterError,
9+
OpenAPIValidationError
910
)
11+
from openapi_spec_validator.decorators import ValidationErrorWrapper
1012
from openapi_spec_validator.factories import Draft4ExtendedValidatorFactory
1113
from openapi_spec_validator.managers import ResolverManager
1214

1315
log = logging.getLogger(__name__)
1416

17+
wraps_errors = ValidationErrorWrapper(OpenAPIValidationError)
18+
1519

1620
def is_ref(spec):
1721
return isinstance(spec, dict) and '$ref' in spec
@@ -43,6 +47,7 @@ def validate(self, spec, spec_url=''):
4347
for err in self.iter_errors(spec, spec_url=spec_url):
4448
raise err
4549

50+
@wraps_errors
4651
def iter_errors(self, spec, spec_url=''):
4752
spec_resolver = self._get_resolver(spec_url, spec)
4853
dereferencer = self._get_dereferencer(spec_resolver)
@@ -81,6 +86,7 @@ class ComponentsValidator(object):
8186
def __init__(self, dereferencer):
8287
self.dereferencer = dereferencer
8388

89+
@wraps_errors
8490
def iter_errors(self, components):
8591
components_deref = self.dereferencer.dereference(components)
8692

@@ -97,6 +103,7 @@ class SchemasValidator(object):
97103
def __init__(self, dereferencer):
98104
self.dereferencer = dereferencer
99105

106+
@wraps_errors
100107
def iter_errors(self, schemas):
101108
schemas_deref = self.dereferencer.dereference(schemas)
102109
for name, schema in iteritems(schemas_deref):
@@ -112,6 +119,7 @@ class SchemaValidator(object):
112119
def __init__(self, dereferencer):
113120
self.dereferencer = dereferencer
114121

122+
@wraps_errors
115123
def iter_errors(self, schema, require_properties=True):
116124
schema_deref = self.dereferencer.dereference(schema)
117125

@@ -152,6 +160,7 @@ class PathsValidator(object):
152160
def __init__(self, dereferencer):
153161
self.dereferencer = dereferencer
154162

163+
@wraps_errors
155164
def iter_errors(self, paths):
156165
paths_deref = self.dereferencer.dereference(paths)
157166
for url, path_item in iteritems(paths_deref):
@@ -167,6 +176,7 @@ class PathValidator(object):
167176
def __init__(self, dereferencer):
168177
self.dereferencer = dereferencer
169178

179+
@wraps_errors
170180
def iter_errors(self, url, path_item):
171181
path_item_deref = self.dereferencer.dereference(path_item)
172182

@@ -186,6 +196,7 @@ class PathItemValidator(object):
186196
def __init__(self, dereferencer):
187197
self.dereferencer = dereferencer
188198

199+
@wraps_errors
189200
def iter_errors(self, url, path_item):
190201
path_item_deref = self.dereferencer.dereference(path_item)
191202

@@ -214,6 +225,7 @@ class OperationValidator(object):
214225
def __init__(self, dereferencer):
215226
self.dereferencer = dereferencer
216227

228+
@wraps_errors
217229
def iter_errors(self, url, name, operation, path_parameters=None):
218230
path_parameters = path_parameters or []
219231
operation_deref = self.dereferencer.dereference(operation)
@@ -255,6 +267,7 @@ class ParametersValidator(object):
255267
def __init__(self, dereferencer):
256268
self.dereferencer = dereferencer
257269

270+
@wraps_errors
258271
def iter_errors(self, parameters):
259272
seen = set()
260273
for parameter in parameters:
@@ -278,6 +291,7 @@ class ParameterValidator(object):
278291
def __init__(self, dereferencer):
279292
self.dereferencer = dereferencer
280293

294+
@wraps_errors
281295
def iter_errors(self, parameter):
282296
if 'schema' in parameter:
283297
schema = parameter['schema']

tests/integration/test_shortcuts.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import pytest
22

3-
from jsonschema.exceptions import ValidationError
4-
53
from openapi_spec_validator import validate_spec, validate_spec_url
64
from openapi_spec_validator import validate_v2_spec, validate_v2_spec_url
5+
from openapi_spec_validator.exceptions import OpenAPIValidationError
76

87

98
class BaseTestValidValidteV2Spec:
@@ -15,7 +14,7 @@ def test_valid(self, spec):
1514
class BaseTestFaliedValidateV2Spec:
1615

1716
def test_failed(self, spec):
18-
with pytest.raises(ValidationError):
17+
with pytest.raises(OpenAPIValidationError):
1918
validate_v2_spec(spec)
2019

2120

@@ -28,7 +27,7 @@ def test_valid(self, spec):
2827
class BaseTestFaliedValidateSpec:
2928

3029
def test_failed(self, spec):
31-
with pytest.raises(ValidationError):
30+
with pytest.raises(OpenAPIValidationError):
3231
validate_spec(spec)
3332

3433

@@ -41,7 +40,7 @@ def test_valid(self, spec_url):
4140
class BaseTestFaliedValidateV2SpecUrl:
4241

4342
def test_failed(self, spec_url):
44-
with pytest.raises(ValidationError):
43+
with pytest.raises(OpenAPIValidationError):
4544
validate_v2_spec_url(spec_url)
4645

4746

@@ -54,7 +53,7 @@ def test_valid(self, spec_url):
5453
class BaseTestFaliedValidateSpecUrl:
5554

5655
def test_failed(self, spec_url):
57-
with pytest.raises(ValidationError):
56+
with pytest.raises(OpenAPIValidationError):
5857
validate_spec_url(spec_url)
5958

6059

tests/integration/test_validate.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pytest
22

3-
from jsonschema.exceptions import ValidationError
3+
from openapi_spec_validator.exceptions import OpenAPIValidationError
44

55

66
class BaseTestValidOpeAPIv3Validator(object):
@@ -20,7 +20,7 @@ def spec_url(self):
2020
return ''
2121

2222
def test_failed(self, validator, spec, spec_url):
23-
with pytest.raises(ValidationError):
23+
with pytest.raises(OpenAPIValidationError):
2424
validator.validate(spec, spec_url=spec_url)
2525

2626

tests/integration/test_validators.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
from jsonschema.exceptions import ValidationError
2-
31
from openapi_spec_validator.exceptions import (
4-
ExtraParametersError, UnresolvableParameterError,
2+
ExtraParametersError, UnresolvableParameterError, OpenAPIValidationError,
53
)
64

75

@@ -13,11 +11,11 @@ def test_empty(self, validator):
1311
errors = validator.iter_errors(spec)
1412

1513
errors_list = list(errors)
16-
assert errors_list[0].__class__ == ValidationError
14+
assert errors_list[0].__class__ == OpenAPIValidationError
1715
assert errors_list[0].message == "'openapi' is a required property"
18-
assert errors_list[1].__class__ == ValidationError
16+
assert errors_list[1].__class__ == OpenAPIValidationError
1917
assert errors_list[1].message == "'info' is a required property"
20-
assert errors_list[2].__class__ == ValidationError
18+
assert errors_list[2].__class__ == OpenAPIValidationError
2119
assert errors_list[2].message == "'paths' is a required property"
2220

2321
def test_info_empty(self, validator):
@@ -30,7 +28,7 @@ def test_info_empty(self, validator):
3028
errors = validator.iter_errors(spec)
3129

3230
errors_list = list(errors)
33-
assert errors_list[0].__class__ == ValidationError
31+
assert errors_list[0].__class__ == OpenAPIValidationError
3432
assert errors_list[0].message == "'title' is a required property"
3533

3634
def test_minimalistic(self, validator):
@@ -200,7 +198,7 @@ def test_default_value_wrong_type(self, validator):
200198

201199
errors_list = list(errors)
202200
assert len(errors_list) == 1
203-
assert errors_list[0].__class__ == ValidationError
201+
assert errors_list[0].__class__ == OpenAPIValidationError
204202
assert errors_list[0].message == (
205203
"'invaldtype' is not of type 'integer'"
206204
)
@@ -235,7 +233,7 @@ def test_parameter_default_value_wrong_type(self, validator):
235233

236234
errors_list = list(errors)
237235
assert len(errors_list) == 1
238-
assert errors_list[0].__class__ == ValidationError
236+
assert errors_list[0].__class__ == OpenAPIValidationError
239237
assert errors_list[0].message == (
240238
"'invaldtype' is not of type 'integer'"
241239
)

0 commit comments

Comments
 (0)