1
1
"""OpenAPI spec validator validation validators module."""
2
2
import logging
3
3
import string
4
+ import warnings
4
5
from typing import Any
5
- from typing import Callable
6
- from typing import Hashable
7
6
from typing import Iterator
8
7
from typing import List
9
- from typing import Mapping
10
8
from typing import Optional
11
9
from typing import Type
12
10
13
11
from jsonschema ._format import FormatChecker
14
12
from jsonschema .exceptions import ValidationError
15
13
from jsonschema .protocols import Validator
16
- from jsonschema .validators import RefResolver
17
- from jsonschema_spec .accessors import SpecAccessor
18
- from jsonschema_spec .paths import Spec
14
+ from jsonschema_spec .handlers import default_handlers
15
+ from jsonschema_spec .paths import SchemaPath
16
+ from jsonschema_spec .typing import ResolverHandlers
17
+ from jsonschema_spec .typing import Schema
19
18
20
19
from openapi_spec_validator .validation .decorators import ValidationErrorWrapper
21
20
from openapi_spec_validator .validation .exceptions import (
35
34
wraps_errors = ValidationErrorWrapper (OpenAPIValidationError )
36
35
37
36
38
- def is_ref (spec : Any ) -> bool :
39
- return isinstance (spec , dict ) and "$ref" in spec
40
-
41
-
42
37
class SpecValidator :
43
-
44
38
OPERATIONS = [
45
39
"get" ,
46
40
"put" ,
@@ -57,7 +51,7 @@ def __init__(
57
51
schema_validator : Validator ,
58
52
value_validator_class : Type [Validator ],
59
53
value_validator_format_checker : FormatChecker ,
60
- resolver_handlers : Optional [ Mapping [ str , Callable [[ str ], Any ]]] = None ,
54
+ resolver_handlers : ResolverHandlers = default_handlers ,
61
55
):
62
56
self .schema_validator = schema_validator
63
57
self .value_validator_class = value_validator_class
@@ -66,30 +60,48 @@ def __init__(
66
60
67
61
self .operation_ids_registry : Optional [List [str ]] = None
68
62
self .schema_ids_registry : Optional [List [int ]] = None
69
- self .resolver = None
70
63
71
64
def validate (
72
- self , instance : Mapping [Hashable , Any ], spec_url : str = ""
65
+ self ,
66
+ schema : Schema ,
67
+ base_uri : str = "" ,
68
+ spec_url : Optional [str ] = None ,
73
69
) -> None :
74
- for err in self .iter_errors (instance , spec_url = spec_url ):
70
+ for err in self .iter_errors (
71
+ schema ,
72
+ base_uri = base_uri ,
73
+ spec_url = spec_url ,
74
+ ):
75
75
raise err
76
76
77
- def is_valid (self , instance : Mapping [ Hashable , Any ] ) -> bool :
78
- error = next (self .iter_errors (instance ), None )
77
+ def is_valid (self , schema : Schema ) -> bool :
78
+ error = next (self .iter_errors (schema ), None )
79
79
return error is None
80
80
81
81
@wraps_errors
82
82
def iter_errors (
83
- self , instance : Mapping [Hashable , Any ], spec_url : str = ""
83
+ self ,
84
+ schema : Schema ,
85
+ base_uri : str = "" ,
86
+ spec_url : Optional [str ] = None ,
84
87
) -> Iterator [ValidationError ]:
88
+ if spec_url is not None :
89
+ warnings .warn (
90
+ "spec_url parameter is deprecated. " "Use base_uri instead." ,
91
+ DeprecationWarning ,
92
+ )
93
+ base_uri = spec_url
94
+
85
95
self .operation_ids_registry = []
86
96
self .schema_ids_registry = []
87
- self .resolver = self ._get_resolver (spec_url , instance )
88
97
89
- yield from self .schema_validator .iter_errors (instance )
98
+ yield from self .schema_validator .iter_errors (schema )
90
99
91
- accessor = SpecAccessor (instance , self .resolver )
92
- spec = Spec (accessor )
100
+ spec = SchemaPath .from_dict (
101
+ schema ,
102
+ base_uri = base_uri ,
103
+ handlers = self .resolver_handlers ,
104
+ )
93
105
if "paths" in spec :
94
106
paths = spec / "paths"
95
107
yield from self ._iter_paths_errors (paths )
@@ -98,17 +110,14 @@ def iter_errors(
98
110
components = spec / "components"
99
111
yield from self ._iter_components_errors (components )
100
112
101
- def _get_resolver (
102
- self , base_uri : str , referrer : Mapping [Hashable , Any ]
103
- ) -> RefResolver :
104
- return RefResolver (base_uri , referrer , handlers = self .resolver_handlers )
105
-
106
- def _iter_paths_errors (self , paths : Spec ) -> Iterator [ValidationError ]:
113
+ def _iter_paths_errors (
114
+ self , paths : SchemaPath
115
+ ) -> Iterator [ValidationError ]:
107
116
for url , path_item in paths .items ():
108
117
yield from self ._iter_path_errors (url , path_item )
109
118
110
119
def _iter_path_errors (
111
- self , url : str , path_item : Spec
120
+ self , url : str , path_item : SchemaPath
112
121
) -> Iterator [ValidationError ]:
113
122
parameters = None
114
123
if "parameters" in path_item :
@@ -127,8 +136,8 @@ def _iter_operation_errors(
127
136
self ,
128
137
url : str ,
129
138
name : str ,
130
- operation : Spec ,
131
- path_parameters : Optional [Spec ],
139
+ operation : SchemaPath ,
140
+ path_parameters : Optional [SchemaPath ],
132
141
) -> Iterator [ValidationError ]:
133
142
assert self .operation_ids_registry is not None
134
143
@@ -168,13 +177,13 @@ def _iter_operation_errors(
168
177
return
169
178
170
179
def _iter_responses_errors (
171
- self , responses : Spec
180
+ self , responses : SchemaPath
172
181
) -> Iterator [ValidationError ]:
173
182
for response_code , response in responses .items ():
174
183
yield from self ._iter_response_errors (response_code , response )
175
184
176
185
def _iter_response_errors (
177
- self , response_code : str , response : Spec
186
+ self , response_code : str , response : SchemaPath
178
187
) -> Iterator [ValidationError ]:
179
188
# openapi 2
180
189
if "schema" in response :
@@ -185,18 +194,20 @@ def _iter_response_errors(
185
194
content = response / "content"
186
195
yield from self ._iter_content_errors (content )
187
196
188
- def _iter_content_errors (self , content : Spec ) -> Iterator [ValidationError ]:
197
+ def _iter_content_errors (
198
+ self , content : SchemaPath
199
+ ) -> Iterator [ValidationError ]:
189
200
for mimetype , media_type in content .items ():
190
201
yield from self ._iter_media_type_errors (mimetype , media_type )
191
202
192
203
def _iter_media_type_errors (
193
- self , mimetype : str , media_type : Spec
204
+ self , mimetype : str , media_type : SchemaPath
194
205
) -> Iterator [ValidationError ]:
195
206
if "schema" in media_type :
196
207
schema = media_type / "schema"
197
208
yield from self ._iter_schema_errors (schema )
198
209
199
- def _get_path_param_names (self , params : Spec ) -> Iterator [str ]:
210
+ def _get_path_param_names (self , params : SchemaPath ) -> Iterator [str ]:
200
211
for param in params :
201
212
if param ["in" ] == "path" :
202
213
yield param ["name" ]
@@ -207,7 +218,7 @@ def _get_path_params_from_url(self, url: str) -> Iterator[str]:
207
218
return filter (None , path_params )
208
219
209
220
def _iter_parameters_errors (
210
- self , parameters : Spec
221
+ self , parameters : SchemaPath
211
222
) -> Iterator [ValidationError ]:
212
223
seen = set ()
213
224
for parameter in parameters :
@@ -221,7 +232,7 @@ def _iter_parameters_errors(
221
232
seen .add (key )
222
233
223
234
def _iter_parameter_errors (
224
- self , parameter : Spec
235
+ self , parameter : SchemaPath
225
236
) -> Iterator [ValidationError ]:
226
237
if "schema" in parameter :
227
238
schema = parameter / "schema"
@@ -234,18 +245,18 @@ def _iter_parameter_errors(
234
245
yield from self ._iter_value_errors (parameter , default )
235
246
236
247
def _iter_value_errors (
237
- self , schema : Spec , value : Any
248
+ self , schema : SchemaPath , value : Any
238
249
) -> Iterator [ValidationError ]:
239
- with schema .open () as content :
250
+ with schema .resolve () as resolved :
240
251
validator = self .value_validator_class (
241
- content ,
242
- resolver = self .resolver ,
252
+ resolved . contents ,
253
+ _resolver = resolved .resolver ,
243
254
format_checker = self .value_validator_format_checker ,
244
255
)
245
256
yield from validator .iter_errors (value )
246
257
247
258
def _iter_schema_errors (
248
- self , schema : Spec , require_properties : bool = True
259
+ self , schema : SchemaPath , require_properties : bool = True
249
260
) -> Iterator [ValidationError ]:
250
261
if not hasattr (schema .content (), "__getitem__" ):
251
262
return
@@ -329,11 +340,13 @@ def _iter_schema_errors(
329
340
yield from self ._iter_value_errors (schema , default )
330
341
331
342
def _iter_components_errors (
332
- self , components : Spec
343
+ self , components : SchemaPath
333
344
) -> Iterator [ValidationError ]:
334
345
schemas = components .get ("schemas" , {})
335
346
yield from self ._iter_schemas_errors (schemas )
336
347
337
- def _iter_schemas_errors (self , schemas : Spec ) -> Iterator [ValidationError ]:
348
+ def _iter_schemas_errors (
349
+ self , schemas : SchemaPath
350
+ ) -> Iterator [ValidationError ]:
338
351
for _ , schema in schemas .items ():
339
352
yield from self ._iter_schema_errors (schema )
0 commit comments