Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(api-gateway): add debug mode #507

Merged
merged 12 commits into from
Jul 9, 2021
95 changes: 65 additions & 30 deletions aws_lambda_powertools/event_handler/api_gateway.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import base64
import json
import logging
import os
import re
import traceback
import zlib
from enum import Enum
from http import HTTPStatus
from typing import Any, Callable, Dict, List, Optional, Set, Union

from aws_lambda_powertools.event_handler import content_types
from aws_lambda_powertools.event_handler.exceptions import ServiceError
from aws_lambda_powertools.shared import constants
from aws_lambda_powertools.shared.functions import resolve_truthy_env_var_choice
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.common import BaseProxyEvent
Expand All @@ -28,43 +32,46 @@ class ProxyEventType(Enum):
class CORSConfig(object):
"""CORS Config


Examples
--------

Simple cors example using the default permissive cors, not this should only be used during early prototyping

from aws_lambda_powertools.event_handler.api_gateway import ApiGatewayResolver
```python
from aws_lambda_powertools.event_handler.api_gateway import ApiGatewayResolver

app = ApiGatewayResolver()
app = ApiGatewayResolver()

@app.get("/my/path", cors=True)
def with_cors():
return {"message": "Foo"}
@app.get("/my/path", cors=True)
def with_cors():
return {"message": "Foo"}
```

Using a custom CORSConfig where `with_cors` used the custom provided CORSConfig and `without_cors`
do not include any cors headers.

from aws_lambda_powertools.event_handler.api_gateway import (
ApiGatewayResolver, CORSConfig
)

cors_config = CORSConfig(
allow_origin="https://wwww.example.com/",
expose_headers=["x-exposed-response-header"],
allow_headers=["x-custom-request-header"],
max_age=100,
allow_credentials=True,
)
app = ApiGatewayResolver(cors=cors_config)

@app.get("/my/path")
def with_cors():
return {"message": "Foo"}
```python
from aws_lambda_powertools.event_handler.api_gateway import (
ApiGatewayResolver, CORSConfig
)

cors_config = CORSConfig(
allow_origin="https://wwww.example.com/",
expose_headers=["x-exposed-response-header"],
allow_headers=["x-custom-request-header"],
max_age=100,
allow_credentials=True,
)
app = ApiGatewayResolver(cors=cors_config)

@app.get("/my/path")
def with_cors():
return {"message": "Foo"}

@app.get("/another-one", cors=False)
def without_cors():
return {"message": "Foo"}
@app.get("/another-one", cors=False)
def without_cors():
return {"message": "Foo"}
```
"""

_REQUIRED_HEADERS = ["Authorization", "Content-Type", "X-Amz-Date", "X-Api-Key", "X-Amz-Security-Token"]
Expand Down Expand Up @@ -240,20 +247,31 @@ def lambda_handler(event, context):
current_event: BaseProxyEvent
lambda_context: LambdaContext

def __init__(self, proxy_type: Enum = ProxyEventType.APIGatewayProxyEvent, cors: CORSConfig = None):
def __init__(
self,
proxy_type: Enum = ProxyEventType.APIGatewayProxyEvent,
cors: CORSConfig = None,
debug: Optional[bool] = None,
):
"""
Parameters
----------
proxy_type: ProxyEventType
Proxy request type, defaults to API Gateway V1
cors: CORSConfig
Optionally configure and enabled CORS. Not each route will need to have to cors=True
debug: Optional[bool]
Enables debug mode, by default False. Can be also be enabled by "POWERTOOLS_EVENT_HANDLER_DEBUG"
environment variable
"""
self._proxy_type = proxy_type
self._routes: List[Route] = []
self._cors = cors
self._cors_enabled: bool = cors is not None
self._cors_methods: Set[str] = {"OPTIONS"}
self._debug = resolve_truthy_env_var_choice(
choice=debug, env=os.getenv(constants.EVENT_HANDLER_DEBUG_ENV, "false")
)

def get(self, rule: str, cors: bool = None, compress: bool = False, cache_control: str = None):
"""Get route decorator with GET `method`
Expand Down Expand Up @@ -416,6 +434,8 @@ def resolve(self, event, context) -> Dict[str, Any]:
dict
Returns the dict response
"""
if self._debug:
print(self._json_dump(event))
self.current_event = self._to_proxy_event(event)
self.lambda_context = context
return self._resolve().build(self.current_event, self._cors)
Expand Down Expand Up @@ -489,6 +509,19 @@ def _call_route(self, route: Route, args: Dict[str, str]) -> ResponseBuilder:
),
route,
)
except Exception:
if self._debug:
# If the user has turned on debug mode,
# we'll let the original exception propagate so
# they get more information about what went wrong.
return ResponseBuilder(
Response(
status_code=500,
content_type=content_types.TEXT_PLAIN,
body="".join(traceback.format_exc()),
)
)
raise

def _to_response(self, result: Union[Dict, Response]) -> Response:
"""Convert the route's result to a Response
Expand All @@ -509,7 +542,9 @@ def _to_response(self, result: Union[Dict, Response]) -> Response:
body=self._json_dump(result),
)

@staticmethod
def _json_dump(obj: Any) -> str:
"""Does a concise json serialization"""
return json.dumps(obj, separators=(",", ":"), cls=Encoder)
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)
3 changes: 2 additions & 1 deletion aws_lambda_powertools/event_handler/content_types.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# use mimetypes library to be certain, e.g., mimetypes.types_map[".json"]

APPLICATION_JSON = "application/json"
PLAIN_TEXT = "text/plain"
TEXT_PLAIN = "text/plain"
TEXT_HTML = "text/html"
2 changes: 2 additions & 0 deletions aws_lambda_powertools/shared/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@

XRAY_SDK_MODULE = "aws_xray_sdk"
XRAY_SDK_CORE_MODULE = "aws_xray_sdk.core"

EVENT_HANDLER_DEBUG_ENV: str = "POWERTOOLS_EVENT_HANDLER_DEBUG"
heitorlessa marked this conversation as resolved.
Show resolved Hide resolved
Loading