From b2c491542b1e2ee421a1bdcfb467e3c333bf4c4a Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Tue, 27 Jul 2021 09:38:48 -0700 Subject: [PATCH 1/4] feat(event-handler): allow for a custom serializer --- .../event_handler/api_gateway.py | 16 +++++--- .../event_handler/test_api_gateway.py | 38 +++++++++++++++++++ 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/aws_lambda_powertools/event_handler/api_gateway.py b/aws_lambda_powertools/event_handler/api_gateway.py index 44d3f2b07de..d7187b33297 100644 --- a/aws_lambda_powertools/event_handler/api_gateway.py +++ b/aws_lambda_powertools/event_handler/api_gateway.py @@ -6,6 +6,7 @@ import traceback import zlib from enum import Enum +from functools import partial from http import HTTPStatus from typing import Any, Callable, Dict, List, Optional, Set, Union @@ -263,6 +264,7 @@ def __init__( proxy_type: Enum = ProxyEventType.APIGatewayProxyEvent, cors: Optional[CORSConfig] = None, debug: Optional[bool] = None, + serializer: Optional[Callable[[Dict], str]] = None, ): """ Parameters @@ -284,6 +286,14 @@ def __init__( env=os.getenv(constants.EVENT_HANDLER_DEBUG_ENV, "false"), choice=debug ) + if serializer: + self._serializer = serializer + elif self._debug: + """Does a concise json serialization or pretty print when in debug mode""" + self._serializer = partial(json.dumps, indent=4, cls=Encoder) + else: + self._serializer = partial(json.dumps, separators=(",", ":"), cls=Encoder) + def get(self, rule: str, cors: Optional[bool] = None, compress: bool = False, cache_control: Optional[str] = None): """Get route decorator with GET `method` @@ -592,8 +602,4 @@ def _to_response(self, result: Union[Dict, Response]) -> Response: ) def _json_dump(self, obj: Any) -> str: - """Does a concise json serialization or pretty print when in debug mode""" - if self._debug: - return json.dumps(obj, indent=4, cls=Encoder) - else: - return json.dumps(obj, separators=(",", ":"), cls=Encoder) + return self._serializer(obj) diff --git a/tests/functional/event_handler/test_api_gateway.py b/tests/functional/event_handler/test_api_gateway.py index f16086ba634..5d44f68f2af 100644 --- a/tests/functional/event_handler/test_api_gateway.py +++ b/tests/functional/event_handler/test_api_gateway.py @@ -3,6 +3,8 @@ import zlib from copy import deepcopy from decimal import Decimal +from enum import Enum +from json import JSONEncoder from pathlib import Path from typing import Dict @@ -728,3 +730,39 @@ def get_account(account_id: str): ret = app.resolve(event, None) assert ret["statusCode"] == 200 + + +def test_custom_serializer(): + class Color(Enum): + RED = 1 + BLUE = 2 + + class CustomEncoder(JSONEncoder): + def default(self, data): + if isinstance(data, Enum): + return data.value + try: + iterable = iter(data) + except TypeError: + pass + else: + return list(iterable) + return JSONEncoder.default(self, data) + + def custom_serializer(data) -> str: + return json.dumps(data, cls=CustomEncoder) + + app = ApiGatewayResolver(serializer=custom_serializer) + + @app.get("/colors") + def get_color() -> Dict: + return { + "color": Color.RED, + "variations": {"light", "dark"}, + } + + response = app({"httpMethod": "GET", "path": "/colors"}, None) + + body = response["body"] + expected = '{"color": 1, "variations": ["light", "dark"]}' + assert expected == body From f5f4123a0f6e300f4bc068d7409b2a6f1ba6e57f Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Tue, 27 Jul 2021 09:54:08 -0700 Subject: [PATCH 2/4] test(event-handler): fix apigw test --- .../functional/event_handler/test_api_gateway.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/functional/event_handler/test_api_gateway.py b/tests/functional/event_handler/test_api_gateway.py index 5d44f68f2af..1272125da8b 100644 --- a/tests/functional/event_handler/test_api_gateway.py +++ b/tests/functional/event_handler/test_api_gateway.py @@ -733,10 +733,7 @@ def get_account(account_id: str): def test_custom_serializer(): - class Color(Enum): - RED = 1 - BLUE = 2 - + # GIVEN a custom serializer to handle enums and sets class CustomEncoder(JSONEncoder): def default(self, data): if isinstance(data, Enum): @@ -746,7 +743,7 @@ def default(self, data): except TypeError: pass else: - return list(iterable) + return sorted(iterable) return JSONEncoder.default(self, data) def custom_serializer(data) -> str: @@ -754,6 +751,10 @@ def custom_serializer(data) -> str: app = ApiGatewayResolver(serializer=custom_serializer) + class Color(Enum): + RED = 1 + BLUE = 2 + @app.get("/colors") def get_color() -> Dict: return { @@ -761,8 +762,10 @@ def get_color() -> Dict: "variations": {"light", "dark"}, } + # WHEN calling handler response = app({"httpMethod": "GET", "path": "/colors"}, None) + # THEN then use the custom serializer body = response["body"] - expected = '{"color": 1, "variations": ["light", "dark"]}' + expected = '{"color": 1, "variations": ["dark", "light"]}' assert expected == body From fbfe6d9deea8303a4f2777be76f4e8601ba3f0d9 Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Fri, 30 Jul 2021 02:39:44 -0700 Subject: [PATCH 3/4] Update aws_lambda_powertools/event_handler/api_gateway.py Co-authored-by: Heitor Lessa --- aws_lambda_powertools/event_handler/api_gateway.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/aws_lambda_powertools/event_handler/api_gateway.py b/aws_lambda_powertools/event_handler/api_gateway.py index d7187b33297..fe9e6f6c628 100644 --- a/aws_lambda_powertools/event_handler/api_gateway.py +++ b/aws_lambda_powertools/event_handler/api_gateway.py @@ -286,13 +286,11 @@ def __init__( env=os.getenv(constants.EVENT_HANDLER_DEBUG_ENV, "false"), choice=debug ) - if serializer: - self._serializer = serializer - elif self._debug: + self._serializer = serializer or partial(json.dumps, separators=(",", ":"), cls=Encoder) + + if self._debug: """Does a concise json serialization or pretty print when in debug mode""" self._serializer = partial(json.dumps, indent=4, cls=Encoder) - else: - self._serializer = partial(json.dumps, separators=(",", ":"), cls=Encoder) def get(self, rule: str, cors: Optional[bool] = None, compress: bool = False, cache_control: Optional[str] = None): """Get route decorator with GET `method` From f70a0577d18424797abdb7f50c49849dc995bad1 Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Fri, 30 Jul 2021 02:52:54 -0700 Subject: [PATCH 4/4] docs: update docs --- aws_lambda_powertools/event_handler/api_gateway.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aws_lambda_powertools/event_handler/api_gateway.py b/aws_lambda_powertools/event_handler/api_gateway.py index fe9e6f6c628..7bf364695da 100644 --- a/aws_lambda_powertools/event_handler/api_gateway.py +++ b/aws_lambda_powertools/event_handler/api_gateway.py @@ -286,10 +286,11 @@ def __init__( env=os.getenv(constants.EVENT_HANDLER_DEBUG_ENV, "false"), choice=debug ) + # Allow for a custom serializer or a concise json serialization self._serializer = serializer or partial(json.dumps, separators=(",", ":"), cls=Encoder) if self._debug: - """Does a concise json serialization or pretty print when in debug mode""" + # Always does a pretty print when in debug mode self._serializer = partial(json.dumps, indent=4, cls=Encoder) def get(self, rule: str, cors: Optional[bool] = None, compress: bool = False, cache_control: Optional[str] = None):