Skip to content

Commit 1487af2

Browse files
Change the name + add examples
1 parent 695b433 commit 1487af2

File tree

8 files changed

+83
-38
lines changed

8 files changed

+83
-38
lines changed

aws_lambda_powertools/event_handler/api_gateway.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1579,7 +1579,7 @@ def __init__(
15791579
strip_prefixes: list[str | Pattern] | None = None,
15801580
enable_validation: bool = False,
15811581
response_validation_error_http_code: HTTPStatus | int | None = None,
1582-
deserializer: Callable[[str], dict] | None = None,
1582+
json_body_deserializer: Callable[[str], dict] | None = None,
15831583
):
15841584
"""
15851585
Parameters
@@ -1601,7 +1601,7 @@ def __init__(
16011601
Enables validation of the request body against the route schema, by default False.
16021602
response_validation_error_http_code
16031603
Sets the returned status code if response is not validated. enable_validation must be True.
1604-
deserializer: Callable[[str], dict], optional
1604+
json_body_deserializer: Callable[[str], dict], optional
16051605
function to deserialize `str`, `bytes`, `bytearray` containing a JSON document to a Python `dict`,
16061606
by default json.loads
16071607
"""
@@ -1629,7 +1629,7 @@ def __init__(
16291629

16301630
# Allow for a custom serializer or a concise json serialization
16311631
self._serializer = serializer or partial(json.dumps, separators=(",", ":"), cls=Encoder)
1632-
self._deserializer = deserializer
1632+
self._json_body_deserializer = json_body_deserializer
16331633

16341634
if self._enable_validation:
16351635
from aws_lambda_powertools.event_handler.middlewares.openapi_validation import OpenAPIValidationMiddleware
@@ -2441,24 +2441,24 @@ def _to_proxy_event(self, event: dict) -> BaseProxyEvent: # noqa: PLR0911 # ig
24412441
"""Convert the event dict to the corresponding data class"""
24422442
if self._proxy_type == ProxyEventType.APIGatewayProxyEvent:
24432443
logger.debug("Converting event to API Gateway REST API contract")
2444-
return APIGatewayProxyEvent(event, self._deserializer)
2444+
return APIGatewayProxyEvent(event, self._json_body_deserializer)
24452445
if self._proxy_type == ProxyEventType.APIGatewayProxyEventV2:
24462446
logger.debug("Converting event to API Gateway HTTP API contract")
2447-
return APIGatewayProxyEventV2(event, self._deserializer)
2447+
return APIGatewayProxyEventV2(event, self._json_body_deserializer)
24482448
if self._proxy_type == ProxyEventType.BedrockAgentEvent:
24492449
logger.debug("Converting event to Bedrock Agent contract")
2450-
return BedrockAgentEvent(event, self._deserializer)
2450+
return BedrockAgentEvent(event, self._json_body_deserializer)
24512451
if self._proxy_type == ProxyEventType.LambdaFunctionUrlEvent:
24522452
logger.debug("Converting event to Lambda Function URL contract")
2453-
return LambdaFunctionUrlEvent(event, self._deserializer)
2453+
return LambdaFunctionUrlEvent(event, self._json_body_deserializer)
24542454
if self._proxy_type == ProxyEventType.VPCLatticeEvent:
24552455
logger.debug("Converting event to VPC Lattice contract")
2456-
return VPCLatticeEvent(event, self._deserializer)
2456+
return VPCLatticeEvent(event, self._json_body_deserializer)
24572457
if self._proxy_type == ProxyEventType.VPCLatticeEventV2:
24582458
logger.debug("Converting event to VPC LatticeV2 contract")
2459-
return VPCLatticeEventV2(event, self._deserializer)
2459+
return VPCLatticeEventV2(event, self._json_body_deserializer)
24602460
logger.debug("Converting event to ALB contract")
2461-
return ALBEvent(event, self._deserializer)
2461+
return ALBEvent(event, self._json_body_deserializer)
24622462

24632463
def _resolve(self) -> ResponseBuilder:
24642464
"""Resolves the response or return the not found response"""
@@ -2875,7 +2875,7 @@ def __init__(
28752875
strip_prefixes: list[str | Pattern] | None = None,
28762876
enable_validation: bool = False,
28772877
response_validation_error_http_code: HTTPStatus | int | None = None,
2878-
deserializer: Callable[[str], dict] | None = None,
2878+
json_body_deserializer: Callable[[str], dict] | None = None,
28792879
):
28802880
"""Amazon API Gateway REST and HTTP API v1 payload resolver"""
28812881
super().__init__(
@@ -2886,7 +2886,7 @@ def __init__(
28862886
strip_prefixes,
28872887
enable_validation,
28882888
response_validation_error_http_code,
2889-
deserializer,
2889+
json_body_deserializer=json_body_deserializer,
28902890
)
28912891

28922892
def _get_base_path(self) -> str:
@@ -2963,6 +2963,7 @@ def __init__(
29632963
strip_prefixes: list[str | Pattern] | None = None,
29642964
enable_validation: bool = False,
29652965
response_validation_error_http_code: HTTPStatus | int | None = None,
2966+
json_body_deserializer: Callable[[str], dict] | None = None,
29662967
):
29672968
"""Amazon API Gateway HTTP API v2 payload resolver"""
29682969
super().__init__(
@@ -2973,6 +2974,7 @@ def __init__(
29732974
strip_prefixes,
29742975
enable_validation,
29752976
response_validation_error_http_code,
2977+
json_body_deserializer=json_body_deserializer,
29762978
)
29772979

29782980
def _get_base_path(self) -> str:
@@ -3002,6 +3004,7 @@ def __init__(
30023004
strip_prefixes: list[str | Pattern] | None = None,
30033005
enable_validation: bool = False,
30043006
response_validation_error_http_code: HTTPStatus | int | None = None,
3007+
json_body_deserializer: Callable[[str], dict] | None = None,
30053008
):
30063009
"""Amazon Application Load Balancer (ALB) resolver"""
30073010
super().__init__(
@@ -3012,6 +3015,7 @@ def __init__(
30123015
strip_prefixes,
30133016
enable_validation,
30143017
response_validation_error_http_code,
3018+
json_body_deserializer=json_body_deserializer,
30153019
)
30163020

30173021
def _get_base_path(self) -> str:

aws_lambda_powertools/event_handler/bedrock_agent.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ def __init__(self, debug: bool = False, enable_validation: bool = True):
103103
serializer=None,
104104
strip_prefixes=None,
105105
enable_validation=enable_validation,
106+
json_body_deserializer=None,
106107
)
107108
self._response_builder_class = BedrockResponseBuilder
108109

aws_lambda_powertools/event_handler/lambda_function_url.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ def __init__(
6161
strip_prefixes: list[str | Pattern] | None = None,
6262
enable_validation: bool = False,
6363
response_validation_error_http_code: HTTPStatus | int | None = None,
64+
json_body_deserializer: Callable[[str], dict] | None = None,
6465
):
6566
super().__init__(
6667
ProxyEventType.LambdaFunctionUrlEvent,
@@ -70,6 +71,7 @@ def __init__(
7071
strip_prefixes,
7172
enable_validation,
7273
response_validation_error_http_code,
74+
json_body_deserializer=json_body_deserializer,
7375
)
7476

7577
def _get_base_path(self) -> str:

aws_lambda_powertools/event_handler/vpc_lattice.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ def __init__(
5757
strip_prefixes: list[str | Pattern] | None = None,
5858
enable_validation: bool = False,
5959
response_validation_error_http_code: HTTPStatus | int | None = None,
60+
json_body_deserializer: Callable[[str], dict] | None = None,
6061
):
6162
"""Amazon VPC Lattice resolver"""
6263
super().__init__(
@@ -67,6 +68,7 @@ def __init__(
6768
strip_prefixes,
6869
enable_validation,
6970
response_validation_error_http_code,
71+
json_body_deserializer=json_body_deserializer,
7072
)
7173

7274
def _get_base_path(self) -> str:
@@ -115,6 +117,7 @@ def __init__(
115117
strip_prefixes: list[str | Pattern] | None = None,
116118
enable_validation: bool = False,
117119
response_validation_error_http_code: HTTPStatus | int | None = None,
120+
json_body_deserializer: Callable[[str], dict] | None = None,
118121
):
119122
"""Amazon VPC Lattice resolver"""
120123
super().__init__(
@@ -125,6 +128,7 @@ def __init__(
125128
strip_prefixes,
126129
enable_validation,
127130
response_validation_error_http_code,
131+
json_body_deserializer=json_body_deserializer,
128132
)
129133

130134
def _get_base_path(self) -> str:

docs/core/event_handler/api_gateway.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,6 +1182,14 @@ You can instruct event handler to use a custom serializer to best suit your need
11821182
--8<-- "examples/event_handler_rest/src/custom_serializer.py"
11831183
```
11841184

1185+
### Custom body deserializer
1186+
1187+
You can customize how the integrated [Event Source Data Classes](https://docs.powertools.aws.dev/lambda/python/latest/utilities/data_classes/#api-gateway-proxy) parse the JSON request body by providing your own deserializer function. By default it is `json.loads`
1188+
1189+
```python hl_lines="15" title="Using a custom JSON deserializer for body"
1190+
--8<-- "examples/event_handler_rest/src/custom_json_deserializer.py"
1191+
```
1192+
11851193
### Split routes with Router
11861194

11871195
As you grow the number of routes a given Lambda function should handle, it is natural to either break into smaller Lambda functions, or split routes into separate files to ease maintenance - that's where the `Router` feature is useful.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import json
2+
from decimal import Decimal
3+
from functools import partial
4+
5+
from aws_lambda_powertools import Logger, Tracer
6+
from aws_lambda_powertools.event_handler import APIGatewayRestResolver
7+
from aws_lambda_powertools.logging import correlation_paths
8+
from aws_lambda_powertools.utilities.typing import LambdaContext
9+
10+
tracer = Tracer()
11+
logger = Logger()
12+
app = APIGatewayRestResolver()
13+
14+
15+
app = APIGatewayRestResolver(json_body_deserializer=partial(json.loads, parse_float=Decimal))
16+
17+
18+
@app.get("/body")
19+
def get_body():
20+
return app.current_event.json_body
21+
22+
23+
# You can continue to use other utilities just as before
24+
@logger.inject_lambda_context(correlation_id_path=correlation_paths.API_GATEWAY_REST)
25+
@tracer.capture_lambda_handler
26+
def lambda_handler(event: dict, context: LambdaContext) -> dict:
27+
return app.resolve(event, context)

tests/functional/event_handler/_pydantic/test_api_gateway.py

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
from __future__ import annotations
22

3-
import json
4-
from decimal import Decimal
5-
from functools import partial
6-
73
from pydantic import BaseModel
84

95
from aws_lambda_powertools.event_handler import content_types
@@ -84,24 +80,3 @@ def get_lambda(param: int): ...
8480
assert result["statusCode"] == 422
8581
assert result["multiValueHeaders"]["Content-Type"] == [content_types.APPLICATION_JSON]
8682
assert "missing" in result["body"]
87-
88-
89-
def test_api_gateway_resolver_numeric_value():
90-
# GIVEN a basic API Gateway resolver
91-
app = ApiGatewayResolver(deserializer=partial(json.loads, parse_float=Decimal))
92-
93-
@app.post("/my/path")
94-
def test_handler():
95-
return app.current_event.json_body
96-
97-
# WHEN calling the event handler
98-
event = {}
99-
event.update(LOAD_GW_EVENT)
100-
event["body"] = '{"amount": 2.2999999999999998}'
101-
event["httpMethod"] = "POST"
102-
103-
result = app(event, {})
104-
# THEN process event correctly
105-
assert result["statusCode"] == 200
106-
assert result["multiValueHeaders"]["Content-Type"] == [content_types.APPLICATION_JSON]
107-
assert result["body"] == '{"amount":"2.2999999999999998"}'

tests/functional/event_handler/required_dependencies/test_api_gateway.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@
88
from copy import deepcopy
99
from decimal import Decimal
1010
from enum import Enum
11+
from functools import partial
1112
from json import JSONEncoder
1213
from pathlib import Path
1314

1415
import pytest
1516

16-
from aws_lambda_powertools.event_handler import content_types
17+
from aws_lambda_powertools.event_handler import (
18+
content_types,
19+
)
1720
from aws_lambda_powertools.event_handler.api_gateway import (
1821
ALBResolver,
1922
APIGatewayHttpResolver,
@@ -1968,3 +1971,24 @@ def opa():
19681971
# THEN body should be converted to an empty string
19691972
assert result["statusCode"] == 200
19701973
assert result["body"] == ""
1974+
1975+
1976+
def test_api_gateway_resolver_with_custom_deserializer():
1977+
# GIVEN a basic API Gateway resolver
1978+
app = ApiGatewayResolver(json_body_deserializer=partial(json.loads, parse_float=Decimal))
1979+
1980+
@app.post("/my/path")
1981+
def test_handler():
1982+
return app.current_event.json_body
1983+
1984+
# WHEN calling the event handler
1985+
event = {}
1986+
event.update(LOAD_GW_EVENT)
1987+
event["body"] = '{"amount": 2.2999999999999998}'
1988+
event["httpMethod"] = "POST"
1989+
1990+
result = app(event, {})
1991+
# THEN process event correctly
1992+
assert result["statusCode"] == 200
1993+
assert result["multiValueHeaders"]["Content-Type"] == [content_types.APPLICATION_JSON]
1994+
assert result["body"] == '{"amount":"2.2999999999999998"}'

0 commit comments

Comments
 (0)