24
24
DEFAULT_OPENAPI_TITLE ,
25
25
DEFAULT_OPENAPI_VERSION ,
26
26
)
27
- from aws_lambda_powertools .event_handler .openapi .exceptions import RequestValidationError , SchemaValidationError
27
+ from aws_lambda_powertools .event_handler .openapi .exceptions import (
28
+ RequestValidationError ,
29
+ ResponseValidationError ,
30
+ SchemaValidationError ,
31
+ )
28
32
from aws_lambda_powertools .event_handler .openapi .types import (
29
33
COMPONENT_REF_PREFIX ,
30
34
METHODS_WITH_BODY ,
@@ -1501,6 +1505,7 @@ def __init__(
1501
1505
serializer : Callable [[dict ], str ] | None = None ,
1502
1506
strip_prefixes : list [str | Pattern ] | None = None ,
1503
1507
enable_validation : bool = False ,
1508
+ response_validation_error_http_code : HTTPStatus | int | None = None ,
1504
1509
):
1505
1510
"""
1506
1511
Parameters
@@ -1520,6 +1525,8 @@ def __init__(
1520
1525
Each prefix can be a static string or a compiled regex pattern
1521
1526
enable_validation: bool | None
1522
1527
Enables validation of the request body against the route schema, by default False.
1528
+ response_validation_error_http_code
1529
+ Sets the returned status code if response is not validated. enable_validation must be True.
1523
1530
"""
1524
1531
self ._proxy_type = proxy_type
1525
1532
self ._dynamic_routes : list [Route ] = []
@@ -1536,6 +1543,11 @@ def __init__(
1536
1543
self .processed_stack_frames = []
1537
1544
self ._response_builder_class = ResponseBuilder [BaseProxyEvent ]
1538
1545
self .openapi_config = OpenAPIConfig () # starting an empty dataclass
1546
+ self ._has_response_validation_error = response_validation_error_http_code is not None
1547
+ self ._response_validation_error_http_code = self ._validate_response_validation_error_http_code (
1548
+ response_validation_error_http_code ,
1549
+ enable_validation ,
1550
+ )
1539
1551
1540
1552
# Allow for a custom serializer or a concise json serialization
1541
1553
self ._serializer = serializer or partial (json .dumps , separators = ("," , ":" ), cls = Encoder )
@@ -1545,7 +1557,36 @@ def __init__(
1545
1557
1546
1558
# Note the serializer argument: only use custom serializer if provided by the caller
1547
1559
# Otherwise, fully rely on the internal Pydantic based mechanism to serialize responses for validation.
1548
- self .use ([OpenAPIValidationMiddleware (validation_serializer = serializer )])
1560
+ self .use (
1561
+ [
1562
+ OpenAPIValidationMiddleware (
1563
+ validation_serializer = serializer ,
1564
+ has_response_validation_error = self ._has_response_validation_error ,
1565
+ ),
1566
+ ],
1567
+ )
1568
+
1569
+ def _validate_response_validation_error_http_code (
1570
+ self ,
1571
+ response_validation_error_http_code : HTTPStatus | int | None ,
1572
+ enable_validation : bool ,
1573
+ ) -> HTTPStatus :
1574
+ if response_validation_error_http_code and not enable_validation :
1575
+ msg = "'response_validation_error_http_code' cannot be set when enable_validation is False."
1576
+ raise ValueError (msg )
1577
+
1578
+ if (
1579
+ not isinstance (response_validation_error_http_code , HTTPStatus )
1580
+ and response_validation_error_http_code is not None
1581
+ ):
1582
+
1583
+ try :
1584
+ response_validation_error_http_code = HTTPStatus (response_validation_error_http_code )
1585
+ except ValueError :
1586
+ msg = f"'{ response_validation_error_http_code } ' must be an integer representing an HTTP status code."
1587
+ raise ValueError (msg ) from None
1588
+
1589
+ return response_validation_error_http_code or HTTPStatus .UNPROCESSABLE_ENTITY
1549
1590
1550
1591
def get_openapi_schema (
1551
1592
self ,
@@ -2484,6 +2525,21 @@ def _call_exception_handler(self, exp: Exception, route: Route) -> ResponseBuild
2484
2525
route = route ,
2485
2526
)
2486
2527
2528
+ # OpenAPIValidationMiddleware will only raise ResponseValidationError when
2529
+ # 'self._response_validation_error_http_code' is not None
2530
+ if isinstance (exp , ResponseValidationError ):
2531
+ http_code = self ._response_validation_error_http_code
2532
+ errors = [{"loc" : e ["loc" ], "type" : e ["type" ]} for e in exp .errors ()]
2533
+ return self ._response_builder_class (
2534
+ response = Response (
2535
+ status_code = http_code .value ,
2536
+ content_type = content_types .APPLICATION_JSON ,
2537
+ body = {"statusCode" : self ._response_validation_error_http_code , "detail" : errors },
2538
+ ),
2539
+ serializer = self ._serializer ,
2540
+ route = route ,
2541
+ )
2542
+
2487
2543
if isinstance (exp , ServiceError ):
2488
2544
return self ._response_builder_class (
2489
2545
response = Response (
@@ -2696,6 +2752,7 @@ def __init__(
2696
2752
serializer : Callable [[dict ], str ] | None = None ,
2697
2753
strip_prefixes : list [str | Pattern ] | None = None ,
2698
2754
enable_validation : bool = False ,
2755
+ response_validation_error_http_code : HTTPStatus | int | None = None ,
2699
2756
):
2700
2757
"""Amazon API Gateway REST and HTTP API v1 payload resolver"""
2701
2758
super ().__init__ (
@@ -2705,6 +2762,7 @@ def __init__(
2705
2762
serializer ,
2706
2763
strip_prefixes ,
2707
2764
enable_validation ,
2765
+ response_validation_error_http_code ,
2708
2766
)
2709
2767
2710
2768
def _get_base_path (self ) -> str :
@@ -2778,6 +2836,7 @@ def __init__(
2778
2836
serializer : Callable [[dict ], str ] | None = None ,
2779
2837
strip_prefixes : list [str | Pattern ] | None = None ,
2780
2838
enable_validation : bool = False ,
2839
+ response_validation_error_http_code : HTTPStatus | int | None = None ,
2781
2840
):
2782
2841
"""Amazon API Gateway HTTP API v2 payload resolver"""
2783
2842
super ().__init__ (
@@ -2787,6 +2846,7 @@ def __init__(
2787
2846
serializer ,
2788
2847
strip_prefixes ,
2789
2848
enable_validation ,
2849
+ response_validation_error_http_code ,
2790
2850
)
2791
2851
2792
2852
def _get_base_path (self ) -> str :
@@ -2815,9 +2875,18 @@ def __init__(
2815
2875
serializer : Callable [[dict ], str ] | None = None ,
2816
2876
strip_prefixes : list [str | Pattern ] | None = None ,
2817
2877
enable_validation : bool = False ,
2878
+ response_validation_error_http_code : HTTPStatus | int | None = None ,
2818
2879
):
2819
2880
"""Amazon Application Load Balancer (ALB) resolver"""
2820
- super ().__init__ (ProxyEventType .ALBEvent , cors , debug , serializer , strip_prefixes , enable_validation )
2881
+ super ().__init__ (
2882
+ ProxyEventType .ALBEvent ,
2883
+ cors ,
2884
+ debug ,
2885
+ serializer ,
2886
+ strip_prefixes ,
2887
+ enable_validation ,
2888
+ response_validation_error_http_code ,
2889
+ )
2821
2890
2822
2891
def _get_base_path (self ) -> str :
2823
2892
# ALB doesn't have a stage variable, so we just return an empty string
0 commit comments