Skip to content

Commit 5140b73

Browse files
committed
Specification validation as part of shortcuts
1 parent 0abae09 commit 5140b73

File tree

9 files changed

+108
-32
lines changed

9 files changed

+108
-32
lines changed

Diff for: docs/customizations.rst

+6-7
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,17 @@ Customizations
44
Specification validation
55
------------------------
66

7-
By default, the provided specification is validated on ``Spec`` object creation time.
7+
By default, the specified specification is also validated.
88

99
If you know you have a valid specification already, disabling the validator can improve the performance.
1010

1111
.. code-block:: python
12-
:emphasize-lines: 5
12+
:emphasize-lines: 4
1313
14-
from openapi_core import Spec
15-
16-
spec = Spec.from_dict(
17-
spec_dict,
18-
validator=None,
14+
validate_request(
15+
request,
16+
spec=spec,
17+
spec_validator_cls=None,
1918
)
2019
2120
Media type deserializers

Diff for: openapi_core/shortcuts.py

+8
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ def unmarshal_apicall_request(
108108
if not issubclass(cls, RequestUnmarshaller):
109109
raise TypeError("'cls' argument is not type of RequestUnmarshaller")
110110
v = cls(spec, base_url=base_url, **unmarshaller_kwargs)
111+
v.check_spec(spec)
111112
result = v.unmarshal(request)
112113
result.raise_for_errors()
113114
return result
@@ -134,6 +135,7 @@ def unmarshal_webhook_request(
134135
"'cls' argument is not type of WebhookRequestUnmarshaller"
135136
)
136137
v = cls(spec, base_url=base_url, **unmarshaller_kwargs)
138+
v.check_spec(spec)
137139
result = v.unmarshal(request)
138140
result.raise_for_errors()
139141
return result
@@ -198,6 +200,7 @@ def unmarshal_apicall_response(
198200
if not issubclass(cls, ResponseUnmarshaller):
199201
raise TypeError("'cls' argument is not type of ResponseUnmarshaller")
200202
v = cls(spec, base_url=base_url, **unmarshaller_kwargs)
203+
v.check_spec(spec)
201204
result = v.unmarshal(request, response)
202205
result.raise_for_errors()
203206
return result
@@ -227,6 +230,7 @@ def unmarshal_webhook_response(
227230
"'cls' argument is not type of WebhookResponseUnmarshaller"
228231
)
229232
v = cls(spec, base_url=base_url, **unmarshaller_kwargs)
233+
v.check_spec(spec)
230234
result = v.unmarshal(request, response)
231235
result.raise_for_errors()
232236
return result
@@ -378,6 +382,7 @@ def validate_apicall_request(
378382
if not issubclass(cls, RequestValidator):
379383
raise TypeError("'cls' argument is not type of RequestValidator")
380384
v = cls(spec, base_url=base_url, **validator_kwargs)
385+
v.check_spec(spec)
381386
return v.validate(request)
382387

383388

@@ -402,6 +407,7 @@ def validate_webhook_request(
402407
"'cls' argument is not type of WebhookRequestValidator"
403408
)
404409
v = cls(spec, base_url=base_url, **validator_kwargs)
410+
v.check_spec(spec)
405411
return v.validate(request)
406412

407413

@@ -425,6 +431,7 @@ def validate_apicall_response(
425431
if not issubclass(cls, ResponseValidator):
426432
raise TypeError("'cls' argument is not type of ResponseValidator")
427433
v = cls(spec, base_url=base_url, **validator_kwargs)
434+
v.check_spec(spec)
428435
return v.validate(request, response)
429436

430437

@@ -452,4 +459,5 @@ def validate_webhook_response(
452459
"'cls' argument is not type of WebhookResponseValidator"
453460
)
454461
v = cls(spec, base_url=base_url, **validator_kwargs)
462+
v.check_spec(spec)
455463
return v.validate(request, response)

Diff for: openapi_core/validation/request/validators.py

+14
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
from typing import Optional
77

88
from jsonschema_path import SchemaPath
9+
from openapi_spec_validator import OpenAPIV30SpecValidator
10+
from openapi_spec_validator import OpenAPIV31SpecValidator
911

1012
from openapi_core.casting.schemas import schema_casters_factory
1113
from openapi_core.casting.schemas.factories import SchemaCastersFactory
@@ -387,53 +389,65 @@ def iter_errors(self, request: WebhookRequest) -> Iterator[Exception]:
387389

388390

389391
class V30RequestBodyValidator(APICallRequestBodyValidator):
392+
spec_validator_cls = OpenAPIV30SpecValidator
390393
schema_validators_factory = oas30_write_schema_validators_factory
391394

392395

393396
class V30RequestParametersValidator(APICallRequestParametersValidator):
397+
spec_validator_cls = OpenAPIV30SpecValidator
394398
schema_validators_factory = oas30_write_schema_validators_factory
395399

396400

397401
class V30RequestSecurityValidator(APICallRequestSecurityValidator):
402+
spec_validator_cls = OpenAPIV30SpecValidator
398403
schema_validators_factory = oas30_write_schema_validators_factory
399404

400405

401406
class V30RequestValidator(APICallRequestValidator):
407+
spec_validator_cls = OpenAPIV30SpecValidator
402408
schema_validators_factory = oas30_write_schema_validators_factory
403409

404410

405411
class V31RequestBodyValidator(APICallRequestBodyValidator):
412+
spec_validator_cls = OpenAPIV31SpecValidator
406413
schema_validators_factory = oas31_schema_validators_factory
407414

408415

409416
class V31RequestParametersValidator(APICallRequestParametersValidator):
417+
spec_validator_cls = OpenAPIV31SpecValidator
410418
schema_validators_factory = oas31_schema_validators_factory
411419

412420

413421
class V31RequestSecurityValidator(APICallRequestSecurityValidator):
422+
spec_validator_cls = OpenAPIV31SpecValidator
414423
schema_validators_factory = oas31_schema_validators_factory
415424

416425

417426
class V31RequestValidator(APICallRequestValidator):
427+
spec_validator_cls = OpenAPIV31SpecValidator
418428
schema_validators_factory = oas31_schema_validators_factory
419429
path_finder_cls = WebhookPathFinder
420430

421431

422432
class V31WebhookRequestBodyValidator(WebhookRequestBodyValidator):
433+
spec_validator_cls = OpenAPIV31SpecValidator
423434
schema_validators_factory = oas31_schema_validators_factory
424435
path_finder_cls = WebhookPathFinder
425436

426437

427438
class V31WebhookRequestParametersValidator(WebhookRequestParametersValidator):
439+
spec_validator_cls = OpenAPIV31SpecValidator
428440
schema_validators_factory = oas31_schema_validators_factory
429441
path_finder_cls = WebhookPathFinder
430442

431443

432444
class V31WebhookRequestSecurityValidator(WebhookRequestSecurityValidator):
445+
spec_validator_cls = OpenAPIV31SpecValidator
433446
schema_validators_factory = oas31_schema_validators_factory
434447
path_finder_cls = WebhookPathFinder
435448

436449

437450
class V31WebhookRequestValidator(WebhookRequestValidator):
451+
spec_validator_cls = OpenAPIV31SpecValidator
438452
schema_validators_factory = oas31_schema_validators_factory
439453
path_finder_cls = WebhookPathFinder

Diff for: openapi_core/validation/response/validators.py

+11
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
from typing import Mapping
88

99
from jsonschema_path import SchemaPath
10+
from openapi_spec_validator import OpenAPIV30SpecValidator
11+
from openapi_spec_validator import OpenAPIV31SpecValidator
1012

1113
from openapi_core.exceptions import OpenAPIError
1214
from openapi_core.protocols import Request
@@ -330,36 +332,45 @@ def iter_errors(
330332

331333

332334
class V30ResponseDataValidator(APICallResponseDataValidator):
335+
spec_validator_cls = OpenAPIV30SpecValidator
333336
schema_validators_factory = oas30_read_schema_validators_factory
334337

335338

336339
class V30ResponseHeadersValidator(APICallResponseHeadersValidator):
340+
spec_validator_cls = OpenAPIV30SpecValidator
337341
schema_validators_factory = oas30_read_schema_validators_factory
338342

339343

340344
class V30ResponseValidator(APICallResponseValidator):
345+
spec_validator_cls = OpenAPIV30SpecValidator
341346
schema_validators_factory = oas30_read_schema_validators_factory
342347

343348

344349
class V31ResponseDataValidator(APICallResponseDataValidator):
350+
spec_validator_cls = OpenAPIV31SpecValidator
345351
schema_validators_factory = oas31_schema_validators_factory
346352

347353

348354
class V31ResponseHeadersValidator(APICallResponseHeadersValidator):
355+
spec_validator_cls = OpenAPIV31SpecValidator
349356
schema_validators_factory = oas31_schema_validators_factory
350357

351358

352359
class V31ResponseValidator(APICallResponseValidator):
360+
spec_validator_cls = OpenAPIV31SpecValidator
353361
schema_validators_factory = oas31_schema_validators_factory
354362

355363

356364
class V31WebhookResponseDataValidator(WebhookResponseDataValidator):
365+
spec_validator_cls = OpenAPIV31SpecValidator
357366
schema_validators_factory = oas31_schema_validators_factory
358367

359368

360369
class V31WebhookResponseHeadersValidator(WebhookResponseHeadersValidator):
370+
spec_validator_cls = OpenAPIV31SpecValidator
361371
schema_validators_factory = oas31_schema_validators_factory
362372

363373

364374
class V31WebhookResponseValidator(WebhookResponseValidator):
375+
spec_validator_cls = OpenAPIV31SpecValidator
365376
schema_validators_factory = oas31_schema_validators_factory

Diff for: openapi_core/validation/validators.py

+16
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from urllib.parse import urljoin
99

1010
from jsonschema_path import SchemaPath
11+
from openapi_spec_validator.validation.types import SpecValidatorType
1112

1213
from openapi_core.casting.schemas import schema_casters_factory
1314
from openapi_core.casting.schemas.factories import SchemaCastersFactory
@@ -43,6 +44,7 @@
4344

4445
class BaseValidator:
4546
schema_validators_factory: SchemaValidatorsFactory = NotImplemented
47+
spec_validator_cls: SpecValidatorType = NotImplemented
4648

4749
def __init__(
4850
self,
@@ -52,6 +54,7 @@ def __init__(
5254
style_deserializers_factory: StyleDeserializersFactory = style_deserializers_factory,
5355
media_type_deserializers_factory: MediaTypeDeserializersFactory = media_type_deserializers_factory,
5456
schema_validators_factory: Optional[SchemaValidatorsFactory] = None,
57+
spec_validator_cls: Optional[SpecValidatorType] = None,
5558
format_validators: Optional[FormatValidatorsDict] = None,
5659
extra_format_validators: Optional[FormatValidatorsDict] = None,
5760
extra_media_type_deserializers: Optional[
@@ -73,10 +76,23 @@ def __init__(
7376
raise NotImplementedError(
7477
"schema_validators_factory is not assigned"
7578
)
79+
self.spec_validator_cls = spec_validator_cls or self.spec_validator_cls
80+
if self.spec_validator_cls is NotImplemented:
81+
raise NotImplementedError("spec_validator_cls is not assigned")
7682
self.format_validators = format_validators
7783
self.extra_format_validators = extra_format_validators
7884
self.extra_media_type_deserializers = extra_media_type_deserializers
7985

86+
def check_spec(self, spec: SchemaPath) -> None:
87+
if self.spec_validator_cls is NotImplemented:
88+
raise NotImplementedError
89+
90+
if self.spec_validator_cls is None:
91+
return
92+
93+
validator = self.spec_validator_cls(spec)
94+
validator.validate()
95+
8096
def _find_media_type(
8197
self, content: SchemaPath, mimetype: Optional[str] = None
8298
) -> MediaType:

Diff for: poetry.lock

+5-22
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ isodate = "*"
6969
more-itertools = "*"
7070
parse = "*"
7171
openapi-schema-validator = "^0.6.0"
72-
openapi-spec-validator = ">=0.6.0,<0.8.0"
72+
openapi-spec-validator = "^0.7.1"
7373
requests = {version = "*", optional = true}
7474
werkzeug = "*"
7575
jsonschema-path = "^0.3.1"

Diff for: tests/unit/conftest.py

+11-2
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,21 @@
44

55
@pytest.fixture
66
def spec_v30():
7-
return SchemaPath.from_dict({"openapi": "3.0"})
7+
return SchemaPath.from_dict({"openapi": "3.0.0"})
88

99

1010
@pytest.fixture
1111
def spec_v31():
12-
return SchemaPath.from_dict({"openapi": "3.1"})
12+
return SchemaPath.from_dict(
13+
{
14+
"openapi": "3.1.0",
15+
"info": {
16+
"title": "Spec",
17+
"version": "0.0.1",
18+
},
19+
"paths": {},
20+
}
21+
)
1322

1423

1524
@pytest.fixture

0 commit comments

Comments
 (0)