Skip to content

Commit 25bc5f0

Browse files
authored
Merge pull request #508 from p1c2u/refactor/unmarshallers-format-validators-refactor-2
Unmarshallers and validators refactor
2 parents d22aaf9 + 9a57fd6 commit 25bc5f0

File tree

93 files changed

+5271
-3215
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

93 files changed

+5271
-3215
lines changed

docs/customizations.rst

+33-22
Original file line numberDiff line numberDiff line change
@@ -42,43 +42,54 @@ Pass custom defined media type deserializers dictionary with supported mimetypes
4242
media_type_deserializers_factory=media_type_deserializers_factory,
4343
)
4444
45-
Formats
46-
-------
45+
Format validators
46+
-----------------
4747

4848
OpenAPI defines a ``format`` keyword that hints at how a value should be interpreted, e.g. a ``string`` with the type ``date`` should conform to the RFC 3339 date format.
4949

50-
Openapi-core comes with a set of built-in formatters, but it's also possible to add custom formatters in `SchemaUnmarshallersFactory` and pass it to `RequestValidator` or `ResponseValidator`.
50+
OpenAPI comes with a set of built-in format validators, but it's also possible to add custom ones.
5151

5252
Here's how you could add support for a ``usdate`` format that handles dates of the form MM/DD/YYYY:
5353

5454
.. code-block:: python
5555
56-
from openapi_core.unmarshalling.schemas.factories import SchemaUnmarshallersFactory
57-
from openapi_schema_validator import OAS30Validator
58-
from datetime import datetime
5956
import re
6057
61-
class USDateFormatter:
62-
def validate(self, value) -> bool:
63-
return bool(re.match(r"^\d{1,2}/\d{1,2}/\d{4}$", value))
64-
65-
def format(self, value):
66-
return datetime.strptime(value, "%m/%d/%y").date
67-
58+
def validate_usdate(value):
59+
return bool(re.match(r"^\d{1,2}/\d{1,2}/\d{4}$", value))
6860
69-
custom_formatters = {
70-
'usdate': USDateFormatter(),
61+
extra_format_validators = {
62+
'usdate': validate_usdate,
7163
}
72-
schema_unmarshallers_factory = SchemaUnmarshallersFactory(
73-
OAS30Validator,
74-
custom_formatters=custom_formatters,
75-
context=ValidationContext.RESPONSE,
76-
)
7764
7865
result = validate_response(
7966
request, response,
8067
spec=spec,
81-
cls=ResponseValidator,
82-
schema_unmarshallers_factory=schema_unmarshallers_factory,
68+
extra_format_validators=extra_format_validators,
8369
)
8470
71+
Format unmarshallers
72+
--------------------
73+
74+
Based on ``format`` keyword, openapi-core can also unmarshal values to specific formats.
75+
76+
Openapi-core comes with a set of built-in format unmarshallers, but it's also possible to add custom ones.
77+
78+
Here's an example with the ``usdate`` format that converts a value to date object:
79+
80+
.. code-block:: python
81+
82+
from datetime import datetime
83+
84+
def unmarshal_usdate(value):
85+
return datetime.strptime(value, "%m/%d/%y").date
86+
87+
extra_format_unmarshallers = {
88+
'usdate': unmarshal_usdate,
89+
}
90+
91+
result = unmarshal_response(
92+
request, response,
93+
spec=spec,
94+
extra_format_unmarshallers=extra_format_unmarshallers,
95+
)

openapi_core/__init__.py

+43-23
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,41 @@
11
"""OpenAPI core module"""
2+
from openapi_core.shortcuts import unmarshal_request
3+
from openapi_core.shortcuts import unmarshal_response
4+
from openapi_core.shortcuts import unmarshal_webhook_request
5+
from openapi_core.shortcuts import unmarshal_webhook_response
6+
from openapi_core.shortcuts import validate_request
7+
from openapi_core.shortcuts import validate_response
28
from openapi_core.spec import Spec
9+
from openapi_core.unmarshalling.request import RequestValidator
10+
from openapi_core.unmarshalling.request import V3RequestUnmarshaller
11+
from openapi_core.unmarshalling.request import V3WebhookRequestUnmarshaller
12+
from openapi_core.unmarshalling.request import V30RequestUnmarshaller
13+
from openapi_core.unmarshalling.request import V31RequestUnmarshaller
14+
from openapi_core.unmarshalling.request import V31WebhookRequestUnmarshaller
15+
from openapi_core.unmarshalling.request import openapi_request_validator
16+
from openapi_core.unmarshalling.request import openapi_v3_request_validator
17+
from openapi_core.unmarshalling.request import openapi_v30_request_validator
18+
from openapi_core.unmarshalling.request import openapi_v31_request_validator
19+
from openapi_core.unmarshalling.response import ResponseValidator
20+
from openapi_core.unmarshalling.response import V3ResponseUnmarshaller
21+
from openapi_core.unmarshalling.response import V3WebhookResponseUnmarshaller
22+
from openapi_core.unmarshalling.response import V30ResponseUnmarshaller
23+
from openapi_core.unmarshalling.response import V31ResponseUnmarshaller
24+
from openapi_core.unmarshalling.response import V31WebhookResponseUnmarshaller
25+
from openapi_core.unmarshalling.response import openapi_response_validator
26+
from openapi_core.unmarshalling.response import openapi_v3_response_validator
27+
from openapi_core.unmarshalling.response import openapi_v30_response_validator
28+
from openapi_core.unmarshalling.response import openapi_v31_response_validator
329
from openapi_core.validation.request import V3RequestValidator
430
from openapi_core.validation.request import V3WebhookRequestValidator
531
from openapi_core.validation.request import V30RequestValidator
632
from openapi_core.validation.request import V31RequestValidator
733
from openapi_core.validation.request import V31WebhookRequestValidator
8-
from openapi_core.validation.request import openapi_request_body_validator
9-
from openapi_core.validation.request import (
10-
openapi_request_parameters_validator,
11-
)
12-
from openapi_core.validation.request import openapi_request_security_validator
13-
from openapi_core.validation.request import openapi_request_validator
14-
from openapi_core.validation.request import openapi_v3_request_validator
15-
from openapi_core.validation.request import openapi_v30_request_validator
16-
from openapi_core.validation.request import openapi_v31_request_validator
1734
from openapi_core.validation.response import V3ResponseValidator
1835
from openapi_core.validation.response import V3WebhookResponseValidator
1936
from openapi_core.validation.response import V30ResponseValidator
2037
from openapi_core.validation.response import V31ResponseValidator
2138
from openapi_core.validation.response import V31WebhookResponseValidator
22-
from openapi_core.validation.response import openapi_response_data_validator
23-
from openapi_core.validation.response import openapi_response_headers_validator
24-
from openapi_core.validation.response import openapi_response_validator
25-
from openapi_core.validation.response import openapi_v3_response_validator
26-
from openapi_core.validation.response import openapi_v30_response_validator
27-
from openapi_core.validation.response import openapi_v31_response_validator
28-
from openapi_core.validation.shortcuts import validate_request
29-
from openapi_core.validation.shortcuts import validate_response
3039

3140
__author__ = "Artur Maciag"
3241
__email__ = "maciag.artur@gmail.com"
@@ -36,29 +45,40 @@
3645

3746
__all__ = [
3847
"Spec",
48+
"unmarshal_request",
49+
"unmarshal_response",
50+
"unmarshal_webhook_request",
51+
"unmarshal_webhook_response",
3952
"validate_request",
4053
"validate_response",
54+
"V30RequestUnmarshaller",
55+
"V30ResponseUnmarshaller",
56+
"V31RequestUnmarshaller",
57+
"V31ResponseUnmarshaller",
58+
"V31WebhookRequestUnmarshaller",
59+
"V31WebhookResponseUnmarshaller",
60+
"V3RequestUnmarshaller",
61+
"V3ResponseUnmarshaller",
62+
"V3WebhookRequestUnmarshaller",
63+
"V3WebhookResponseUnmarshaller",
4164
"V30RequestValidator",
42-
"V31RequestValidator",
4365
"V30ResponseValidator",
66+
"V31RequestValidator",
4467
"V31ResponseValidator",
4568
"V31WebhookRequestValidator",
4669
"V31WebhookResponseValidator",
4770
"V3RequestValidator",
4871
"V3ResponseValidator",
4972
"V3WebhookRequestValidator",
5073
"V3WebhookResponseValidator",
74+
"RequestValidator",
75+
"ResponseValidator",
5176
"openapi_v3_request_validator",
5277
"openapi_v30_request_validator",
5378
"openapi_v31_request_validator",
54-
"openapi_request_body_validator",
55-
"openapi_request_parameters_validator",
56-
"openapi_request_security_validator",
5779
"openapi_request_validator",
5880
"openapi_v3_response_validator",
5981
"openapi_v30_response_validator",
6082
"openapi_v31_response_validator",
61-
"openapi_response_data_validator",
62-
"openapi_response_headers_validator",
6383
"openapi_response_validator",
6484
]

openapi_core/contrib/django/middlewares.py

+10-8
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@
1010
from openapi_core.contrib.django.handlers import DjangoOpenAPIErrorsHandler
1111
from openapi_core.contrib.django.requests import DjangoOpenAPIRequest
1212
from openapi_core.contrib.django.responses import DjangoOpenAPIResponse
13-
from openapi_core.validation.processors import OpenAPIProcessor
14-
from openapi_core.validation.request.datatypes import RequestValidationResult
15-
from openapi_core.validation.response.datatypes import ResponseValidationResult
13+
from openapi_core.unmarshalling.processors import UnmarshallingProcessor
14+
from openapi_core.unmarshalling.request.datatypes import RequestUnmarshalResult
15+
from openapi_core.unmarshalling.response.datatypes import (
16+
ResponseUnmarshalResult,
17+
)
1618

1719

1820
class DjangoOpenAPIMiddleware:
@@ -26,19 +28,19 @@ def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]):
2628
if not hasattr(settings, "OPENAPI_SPEC"):
2729
raise ImproperlyConfigured("OPENAPI_SPEC not defined in settings")
2830

29-
self.validation_processor = OpenAPIProcessor(settings.OPENAPI_SPEC)
31+
self.processor = UnmarshallingProcessor(settings.OPENAPI_SPEC)
3032

3133
def __call__(self, request: HttpRequest) -> HttpResponse:
3234
openapi_request = self._get_openapi_request(request)
33-
req_result = self.validation_processor.process_request(openapi_request)
35+
req_result = self.processor.process_request(openapi_request)
3436
if req_result.errors:
3537
response = self._handle_request_errors(req_result, request)
3638
else:
3739
request.openapi = req_result
3840
response = self.get_response(request)
3941

4042
openapi_response = self._get_openapi_response(response)
41-
resp_result = self.validation_processor.process_response(
43+
resp_result = self.processor.process_response(
4244
openapi_request, openapi_response
4345
)
4446
if resp_result.errors:
@@ -47,13 +49,13 @@ def __call__(self, request: HttpRequest) -> HttpResponse:
4749
return response
4850

4951
def _handle_request_errors(
50-
self, request_result: RequestValidationResult, req: HttpRequest
52+
self, request_result: RequestUnmarshalResult, req: HttpRequest
5153
) -> JsonResponse:
5254
return self.errors_handler.handle(request_result.errors, req, None)
5355

5456
def _handle_response_errors(
5557
self,
56-
response_result: ResponseValidationResult,
58+
response_result: ResponseUnmarshalResult,
5759
req: HttpRequest,
5860
resp: HttpResponse,
5961
) -> JsonResponse:

openapi_core/contrib/django/requests.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from werkzeug.datastructures import Headers
77
from werkzeug.datastructures import ImmutableMultiDict
88

9-
from openapi_core.validation.request.datatypes import RequestParameters
9+
from openapi_core.datatypes import RequestParameters
1010

1111
# https://docs.djangoproject.com/en/stable/topics/http/urls/
1212
#

openapi_core/contrib/falcon/middlewares.py

+18-16
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,33 @@
1010
from openapi_core.contrib.falcon.requests import FalconOpenAPIRequest
1111
from openapi_core.contrib.falcon.responses import FalconOpenAPIResponse
1212
from openapi_core.spec import Spec
13-
from openapi_core.validation.processors import OpenAPIProcessor
14-
from openapi_core.validation.request.datatypes import RequestValidationResult
15-
from openapi_core.validation.request.protocols import RequestValidator
16-
from openapi_core.validation.response.datatypes import ResponseValidationResult
17-
from openapi_core.validation.response.protocols import ResponseValidator
13+
from openapi_core.unmarshalling.processors import UnmarshallingProcessor
14+
from openapi_core.unmarshalling.request.datatypes import RequestUnmarshalResult
15+
from openapi_core.unmarshalling.request.types import RequestUnmarshallerType
16+
from openapi_core.unmarshalling.response.datatypes import (
17+
ResponseUnmarshalResult,
18+
)
19+
from openapi_core.unmarshalling.response.types import ResponseUnmarshallerType
1820

1921

20-
class FalconOpenAPIMiddleware(OpenAPIProcessor):
22+
class FalconOpenAPIMiddleware(UnmarshallingProcessor):
2123
request_class = FalconOpenAPIRequest
2224
response_class = FalconOpenAPIResponse
2325
errors_handler = FalconOpenAPIErrorsHandler()
2426

2527
def __init__(
2628
self,
2729
spec: Spec,
28-
request_validator_cls: Optional[Type[RequestValidator]] = None,
29-
response_validator_cls: Optional[Type[ResponseValidator]] = None,
30+
request_unmarshaller_cls: Optional[RequestUnmarshallerType] = None,
31+
response_unmarshaller_cls: Optional[ResponseUnmarshallerType] = None,
3032
request_class: Type[FalconOpenAPIRequest] = FalconOpenAPIRequest,
3133
response_class: Type[FalconOpenAPIResponse] = FalconOpenAPIResponse,
3234
errors_handler: Optional[FalconOpenAPIErrorsHandler] = None,
3335
):
3436
super().__init__(
3537
spec,
36-
request_validator_cls=request_validator_cls,
37-
response_validator_cls=response_validator_cls,
38+
request_unmarshaller_cls=request_unmarshaller_cls,
39+
response_unmarshaller_cls=response_unmarshaller_cls,
3840
)
3941
self.request_class = request_class or self.request_class
4042
self.response_class = response_class or self.response_class
@@ -44,16 +46,16 @@ def __init__(
4446
def from_spec(
4547
cls,
4648
spec: Spec,
47-
request_validator_cls: Optional[Type[RequestValidator]] = None,
48-
response_validator_cls: Optional[Type[ResponseValidator]] = None,
49+
request_unmarshaller_cls: Optional[RequestUnmarshallerType] = None,
50+
response_unmarshaller_cls: Optional[ResponseUnmarshallerType] = None,
4951
request_class: Type[FalconOpenAPIRequest] = FalconOpenAPIRequest,
5052
response_class: Type[FalconOpenAPIResponse] = FalconOpenAPIResponse,
5153
errors_handler: Optional[FalconOpenAPIErrorsHandler] = None,
5254
) -> "FalconOpenAPIMiddleware":
5355
return cls(
5456
spec,
55-
request_validator_cls=request_validator_cls,
56-
response_validator_cls=response_validator_cls,
57+
request_unmarshaller_cls=request_unmarshaller_cls,
58+
response_unmarshaller_cls=response_unmarshaller_cls,
5759
request_class=request_class,
5860
response_class=response_class,
5961
errors_handler=errors_handler,
@@ -82,15 +84,15 @@ def _handle_request_errors(
8284
self,
8385
req: Request,
8486
resp: Response,
85-
request_result: RequestValidationResult,
87+
request_result: RequestUnmarshalResult,
8688
) -> None:
8789
return self.errors_handler.handle(req, resp, request_result.errors)
8890

8991
def _handle_response_errors(
9092
self,
9193
req: Request,
9294
resp: Response,
93-
response_result: ResponseValidationResult,
95+
response_result: ResponseUnmarshalResult,
9496
) -> None:
9597
return self.errors_handler.handle(req, resp, response_result.errors)
9698

openapi_core/contrib/falcon/requests.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from werkzeug.datastructures import Headers
1010
from werkzeug.datastructures import ImmutableMultiDict
1111

12-
from openapi_core.validation.request.datatypes import RequestParameters
12+
from openapi_core.datatypes import RequestParameters
1313

1414

1515
class FalconOpenAPIRequest:

0 commit comments

Comments
 (0)