Repository: aws-powertools/powertools-lambda-python

Issue:
Bug: event_source decorator with APIGatewayProxyEventV2 causes TypeError in debug mode
### Expected Behaviour

Using the event source decorator with APIGatewayProxyEventV2 will run Lambda function and not fail with TypeError

### Current Behaviour

When activating debug mode through either `APIGatewayHttpResolver(debug=True)` or `POWERTOOLS_EVENT_HANDLER_DEBUG=true` causes `TypeError: Object of type APIGatewayProxyEventV2 is not JSON serializable` and no further debug entries get logged.

### Code snippet

```python
from aws_lambda_powertools.utilities.data_classes import (
    APIGatewayProxyEventV2,
    event_source,
)
from aws_lambda_powertools.logging import correlation_paths
from aws_lambda_powertools import Logger
from aws_lambda_powertools.event_handler import APIGatewayHttpResolver
from aws_lambda_powertools.utilities.typing import LambdaContext

logger = Logger()
app = APIGatewayHttpResolver(debug=True)


@logger.inject_lambda_context(correlation_id_path=correlation_paths.API_GATEWAY_HTTP)
@event_source(data_class=APIGatewayProxyEventV2)
def lambda_handler(event: APIGatewayProxyEventV2, context: LambdaContext):
    return app.resolve(event, context)

@app.get("/my/path")
def get_hello_universe():
    return {"message": "hello universe"}
```


### Possible Solution

**Workaround:** Enable debug mode and comment event source out

### Steps to Reproduce

Create a Lambda function accepting APIGatewayProxyEventV2 requests. Decorate the handler method with logger inject and event source and add a simple route that will just return a small JSON response. Then enable debug mode by one of the supported ways and watch the lambda function fail

### AWS Lambda Powertools for Python version

latest (Layer version 18)

### AWS Lambda function runtime

3.9

### Packaging format used

Lambda Layers

### Debugging logs

```python
[ERROR] TypeError: Object of type APIGatewayProxyEventV2 is not JSON serializable
Traceback (most recent call last):
  File "/opt/python/aws_lambda_powertools/logging/logger.py", line 354, in decorate
    return lambda_handler(event, context)
  File "/opt/python/aws_lambda_powertools/middleware_factory/factory.py", line 134, in wrapper
    response = middleware()
  File "/opt/python/aws_lambda_powertools/utilities/data_classes/event_source.py", line 39, in event_source
    return handler(data_class(event), context)
  File "/var/task/app.py", line 28, in lambda_handler
    return app.resolve(event, context)
  File "/opt/python/aws_lambda_powertools/event_handler/api_gateway.py", line 495, in resolve
    print(self._json_dump(event), end="")
  File "/opt/python/aws_lambda_powertools/event_handler/api_gateway.py", line 688, in _json_dump
    return self._serializer(obj)
  File "/var/lang/lib/python3.9/json/__init__.py", line 234, in dumps
    return cls(
  File "/var/lang/lib/python3.9/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/var/lang/lib/python3.9/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/opt/python/aws_lambda_powertools/shared/json_encoder.py", line 16, in default
    return super().default(obj)
  File "/var/lang/lib/python3.9/json/encoder.py", line 179, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
```



Discussion:
Hey @sthuber90 tha is for reporting it - we'll look into it as soon as we can.

Any particular reason you're using event_source with Event Handler?

We already do that for you upon a Lambda invocation, and make it available under app.current_event property.

https://awslabs.github.io/aws-lambda-powertools-python/latest/core/event_handler/api_gateway/#accessing-request-details

Thanks!!
Thank you. No, no particular reason combining `@event_source` and `APIGatewayHttpResolver`. It's perfectly fine for me to remove the event source decorator. Are the resolver and event source incompatible? If so, I didn't see it in the docs
Ah great, I was mostly curious if you knew. For the decorator, I need to
double check in the custom serializer part but IIRC it should work, as we
reuse it elsewhere.

On Mon, 25 Apr 2022 at 19:30, Stephan ***@***.***> wrote:

> Thank you. No, no particular reason combining @event_source and
> APIGatewayHttpResolver. It's perfectly fine for me to remove the event
> source decorator. Are the resolver and event source incompatible? If so, I
> didn't see it in the docs
>
> —
> Reply to this email directly, view it on GitHub
> <https://github.com/awslabs/aws-lambda-powertools-python/issues/1152#issuecomment-1108847569>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AAZPQBDJ3ZAIEHHNN2MFAO3VG3JC5ANCNFSM5UJG3LXA>
> .
> You are receiving this because you commented.Message ID:
> ***@***.***>
>


Resolving patch:
diff --git a/aws_lambda_powertools/event_handler/api_gateway.py b/aws_lambda_powertools/event_handler/api_gateway.py
--- a/aws_lambda_powertools/event_handler/api_gateway.py
+++ b/aws_lambda_powertools/event_handler/api_gateway.py
@@ -491,6 +491,11 @@ def resolve(self, event, context) -> Dict[str, Any]:
         dict
             Returns the dict response
         """
+        if isinstance(event, BaseProxyEvent):
+            warnings.warn(
+                "You don't need to serialize event to Event Source Data Class when using Event Handler; see issue #1152"
+            )
+            event = event.raw_event
         if self._debug:
             print(self._json_dump(event), end="")
         BaseRouter.current_event = self._to_proxy_event(event)


Test patch:
diff --git a/tests/functional/event_handler/test_api_gateway.py b/tests/functional/event_handler/test_api_gateway.py
--- a/tests/functional/event_handler/test_api_gateway.py
+++ b/tests/functional/event_handler/test_api_gateway.py
@@ -31,7 +31,12 @@
 )
 from aws_lambda_powertools.shared import constants
 from aws_lambda_powertools.shared.json_encoder import Encoder
-from aws_lambda_powertools.utilities.data_classes import ALBEvent, APIGatewayProxyEvent, APIGatewayProxyEventV2
+from aws_lambda_powertools.utilities.data_classes import (
+    ALBEvent,
+    APIGatewayProxyEvent,
+    APIGatewayProxyEventV2,
+    event_source,
+)
 from tests.functional.utils import load_event
 
 
@@ -1210,3 +1215,23 @@ def handle_not_found(_) -> Response:
 
     # THEN call the @app.not_found() function
     assert result["statusCode"] == 404
+
+
+def test_event_source_compatibility():
+    # GIVEN
+    app = APIGatewayHttpResolver()
+
+    @app.post("/my/path")
+    def my_path():
+        assert isinstance(app.current_event, APIGatewayProxyEventV2)
+        return {}
+
+    # WHEN
+    @event_source(data_class=APIGatewayProxyEventV2)
+    def handler(event: APIGatewayProxyEventV2, context):
+        assert isinstance(event, APIGatewayProxyEventV2)
+        return app.resolve(event, context)
+
+    # THEN
+    result = handler(load_event("apiGatewayProxyV2Event.json"), None)
+    assert result["statusCode"] == 200