Skip to content

Commit 860ca0a

Browse files
committed
Use openapi-spec-validator spec version finder
1 parent 0f5ac8e commit 860ca0a

File tree

4 files changed

+105
-24
lines changed

4 files changed

+105
-24
lines changed

Diff for: openapi_core/shortcuts.py

+17-7
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,17 @@
55
from typing import Union
66

77
from jsonschema_path import SchemaPath
8+
from openapi_spec_validator.versions import consts as versions
9+
from openapi_spec_validator.versions.datatypes import SpecVersion
10+
from openapi_spec_validator.versions.exceptions import OpenAPIVersionNotFound
11+
from openapi_spec_validator.versions.shortcuts import get_spec_version
812

913
from openapi_core.exceptions import SpecError
10-
from openapi_core.finders import SpecClasses
11-
from openapi_core.finders import SpecFinder
12-
from openapi_core.finders import SpecVersion
1314
from openapi_core.protocols import Request
1415
from openapi_core.protocols import Response
1516
from openapi_core.protocols import WebhookRequest
1617
from openapi_core.spec import Spec
18+
from openapi_core.types import SpecClasses
1719
from openapi_core.unmarshalling.request import V30RequestUnmarshaller
1820
from openapi_core.unmarshalling.request import V31RequestUnmarshaller
1921
from openapi_core.unmarshalling.request import V31WebhookRequestUnmarshaller
@@ -63,8 +65,8 @@
6365

6466
AnyRequest = Union[Request, WebhookRequest]
6567

66-
SPECS: Dict[SpecVersion, SpecClasses] = {
67-
SpecVersion("openapi", "3.0"): SpecClasses(
68+
SPEC2CLASSES: Dict[SpecVersion, SpecClasses] = {
69+
versions.OPENAPIV30: SpecClasses(
6870
V30RequestValidator,
6971
V30ResponseValidator,
7072
None,
@@ -74,7 +76,7 @@
7476
None,
7577
None,
7678
),
77-
SpecVersion("openapi", "3.1"): SpecClasses(
79+
versions.OPENAPIV31: SpecClasses(
7880
V31RequestValidator,
7981
V31ResponseValidator,
8082
V31WebhookRequestValidator,
@@ -88,7 +90,15 @@
8890

8991

9092
def get_classes(spec: SchemaPath) -> SpecClasses:
91-
return SpecFinder(SPECS).get_classes(spec)
93+
try:
94+
spec_version = get_spec_version(spec.contents())
95+
# backward compatibility
96+
except OpenAPIVersionNotFound:
97+
raise SpecError("Spec schema version not detected")
98+
try:
99+
return SPEC2CLASSES[spec_version]
100+
except KeyError:
101+
raise SpecError("Spec schema version not supported")
92102

93103

94104
def unmarshal_apicall_request(

Diff for: openapi_core/finders.py renamed to openapi_core/types.py

+3-17
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from dataclasses import dataclass
12
from typing import Mapping
23
from typing import NamedTuple
34
from typing import Optional
@@ -21,12 +22,8 @@
2122
from openapi_core.validation.validators import BaseValidator
2223

2324

24-
class SpecVersion(NamedTuple):
25-
name: str
26-
version: str
27-
28-
29-
class SpecClasses(NamedTuple):
25+
@dataclass
26+
class SpecClasses:
3027
request_validator_cls: RequestValidatorType
3128
response_validator_cls: ResponseValidatorType
3229
webhook_request_validator_cls: Optional[WebhookRequestValidatorType]
@@ -37,14 +34,3 @@ class SpecClasses(NamedTuple):
3734
webhook_response_unmarshaller_cls: Optional[
3835
WebhookResponseUnmarshallerType
3936
]
40-
41-
42-
class SpecFinder:
43-
def __init__(self, specs: Mapping[SpecVersion, SpecClasses]) -> None:
44-
self.specs = specs
45-
46-
def get_classes(self, spec: SchemaPath) -> SpecClasses:
47-
for v, classes in self.specs.items():
48-
if v.name in spec and spec[v.name].startswith(v.version):
49-
return classes
50-
raise SpecError("Spec schema version not detected")

Diff for: tests/unit/conftest.py

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
from jsonschema_path import SchemaPath
33

44

5+
@pytest.fixture
6+
def spec_v20():
7+
return SchemaPath.from_dict({"swagger": "2.0"})
8+
9+
510
@pytest.fixture
611
def spec_v30():
712
return SchemaPath.from_dict({"openapi": "3.0.0"})

Diff for: tests/unit/test_shortcuts.py

+80
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,12 @@ def test_spec_not_detected(self, spec_invalid):
9797
with pytest.raises(SpecError):
9898
unmarshal_apicall_request(request, spec=spec_invalid)
9999

100+
def test_spec_not_supported(self, spec_v20):
101+
request = mock.Mock(spec=Request)
102+
103+
with pytest.raises(SpecError):
104+
unmarshal_apicall_request(request, spec=spec_v20)
105+
100106
def test_request_type_invalid(self, spec_v31):
101107
request = mock.sentinel.request
102108

@@ -124,6 +130,12 @@ def test_spec_not_detected(self, spec_invalid):
124130
with pytest.raises(SpecError):
125131
unmarshal_webhook_request(request, spec=spec_invalid)
126132

133+
def test_spec_not_supported(self, spec_v20):
134+
request = mock.Mock(spec=WebhookRequest)
135+
136+
with pytest.raises(SpecError):
137+
unmarshal_webhook_request(request, spec=spec_v20)
138+
127139
def test_request_type_invalid(self, spec_v31):
128140
request = mock.sentinel.request
129141

@@ -169,6 +181,12 @@ def test_spec_not_detected(self, spec_invalid):
169181
with pytest.raises(SpecError):
170182
unmarshal_request(request, spec=spec_invalid)
171183

184+
def test_spec_not_supported(self, spec_v20):
185+
request = mock.Mock(spec=Request)
186+
187+
with pytest.raises(SpecError):
188+
unmarshal_request(request, spec=spec_v20)
189+
172190
def test_request_type_invalid(self, spec_v31):
173191
request = mock.sentinel.request
174192

@@ -257,6 +275,13 @@ def test_spec_not_detected(self, spec_invalid):
257275
with pytest.raises(SpecError):
258276
unmarshal_apicall_response(request, response, spec=spec_invalid)
259277

278+
def test_spec_not_supported(self, spec_v20):
279+
request = mock.Mock(spec=Request)
280+
response = mock.Mock(spec=Response)
281+
282+
with pytest.raises(SpecError):
283+
unmarshal_apicall_response(request, response, spec=spec_v20)
284+
260285
def test_request_type_invalid(self, spec_v31):
261286
request = mock.sentinel.request
262287
response = mock.Mock(spec=Response)
@@ -297,6 +322,13 @@ def test_spec_not_detected(self, spec_invalid):
297322
with pytest.raises(SpecError):
298323
unmarshal_response(request, response, spec=spec_invalid)
299324

325+
def test_spec_not_supported(self, spec_v20):
326+
request = mock.Mock(spec=Request)
327+
response = mock.Mock(spec=Response)
328+
329+
with pytest.raises(SpecError):
330+
unmarshal_response(request, response, spec=spec_v20)
331+
300332
def test_request_type_invalid(self, spec_v31):
301333
request = mock.sentinel.request
302334
response = mock.Mock(spec=Response)
@@ -404,6 +436,13 @@ def test_spec_not_detected(self, spec_invalid):
404436
with pytest.raises(SpecError):
405437
unmarshal_webhook_response(request, response, spec=spec_invalid)
406438

439+
def test_spec_not_supported(self, spec_v20):
440+
request = mock.Mock(spec=WebhookRequest)
441+
response = mock.Mock(spec=Response)
442+
443+
with pytest.raises(SpecError):
444+
unmarshal_webhook_response(request, response, spec=spec_v20)
445+
407446
def test_request_type_invalid(self, spec_v31):
408447
request = mock.sentinel.request
409448
response = mock.Mock(spec=Response)
@@ -463,6 +502,12 @@ def test_spec_not_detected(self, spec_invalid):
463502
with pytest.raises(SpecError):
464503
validate_apicall_request(request, spec=spec_invalid)
465504

505+
def test_spec_not_supported(self, spec_v20):
506+
request = mock.Mock(spec=Request)
507+
508+
with pytest.raises(SpecError):
509+
validate_apicall_request(request, spec=spec_v20)
510+
466511
def test_request_type_invalid(self, spec_v31):
467512
request = mock.sentinel.request
468513

@@ -502,6 +547,12 @@ def test_spec_not_detected(self, spec_invalid):
502547
with pytest.raises(SpecError):
503548
validate_webhook_request(request, spec=spec_invalid)
504549

550+
def test_spec_not_supported(self, spec_v20):
551+
request = mock.Mock(spec=WebhookRequest)
552+
553+
with pytest.raises(SpecError):
554+
validate_webhook_request(request, spec=spec_v20)
555+
505556
def test_request_type_invalid(self, spec_v31):
506557
request = mock.sentinel.request
507558

@@ -548,6 +599,13 @@ def test_spec_not_detected(self, spec_invalid):
548599
with pytest.warns(DeprecationWarning):
549600
validate_request(request, spec=spec_invalid)
550601

602+
def test_spec_not_detected(self, spec_v20):
603+
request = mock.Mock(spec=Request)
604+
605+
with pytest.raises(SpecError):
606+
with pytest.warns(DeprecationWarning):
607+
validate_request(request, spec=spec_v20)
608+
551609
def test_request_type_invalid(self, spec_v31):
552610
request = mock.sentinel.request
553611

@@ -705,6 +763,13 @@ def test_spec_not_detected(self, spec_invalid):
705763
with pytest.raises(SpecError):
706764
validate_apicall_response(request, response, spec=spec_invalid)
707765

766+
def test_spec_not_supported(self, spec_v20):
767+
request = mock.Mock(spec=Request)
768+
response = mock.Mock(spec=Response)
769+
770+
with pytest.raises(SpecError):
771+
validate_apicall_response(request, response, spec=spec_v20)
772+
708773
def test_request_type_invalid(self, spec_v31):
709774
request = mock.sentinel.request
710775
response = mock.Mock(spec=Response)
@@ -758,6 +823,13 @@ def test_spec_not_detected(self, spec_invalid):
758823
with pytest.raises(SpecError):
759824
validate_webhook_response(request, response, spec=spec_invalid)
760825

826+
def test_spec_not_supported(self, spec_v20):
827+
request = mock.Mock(spec=WebhookRequest)
828+
response = mock.Mock(spec=Response)
829+
830+
with pytest.raises(SpecError):
831+
validate_webhook_response(request, response, spec=spec_v20)
832+
761833
def test_request_type_invalid(self, spec_v31):
762834
request = mock.sentinel.request
763835
response = mock.Mock(spec=Response)
@@ -819,6 +891,14 @@ def test_spec_not_detected(self, spec_invalid):
819891
with pytest.warns(DeprecationWarning):
820892
validate_response(request, response, spec=spec_invalid)
821893

894+
def test_spec_not_supported(self, spec_v20):
895+
request = mock.Mock(spec=Request)
896+
response = mock.Mock(spec=Response)
897+
898+
with pytest.raises(SpecError):
899+
with pytest.warns(DeprecationWarning):
900+
validate_response(request, response, spec=spec_v20)
901+
822902
def test_request_type_invalid(self, spec_v31):
823903
request = mock.sentinel.request
824904
response = mock.Mock(spec=Response)

0 commit comments

Comments
 (0)