Skip to content

Commit

Permalink
feat(api-gateway): add debug mode (#507)
Browse files Browse the repository at this point in the history
Co-authored-by: Heitor Lessa <heitor.lessa@hotmail.com>
  • Loading branch information
Michael Brewer and heitorlessa authored Jul 9, 2021
1 parent f61b02f commit 43b828e
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 53 deletions.
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"
7 changes: 4 additions & 3 deletions aws_lambda_powertools/shared/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@

METRICS_NAMESPACE_ENV: str = "POWERTOOLS_METRICS_NAMESPACE"

EVENT_HANDLER_DEBUG_ENV: str = "POWERTOOLS_EVENT_HANDLER_DEBUG"

SAM_LOCAL_ENV: str = "AWS_SAM_LOCAL"
CHALICE_LOCAL_ENV: str = "AWS_CHALICE_CLI_MODE"
SERVICE_NAME_ENV: str = "POWERTOOLS_SERVICE_NAME"
XRAY_TRACE_ID_ENV: str = "_X_AMZN_TRACE_ID"


XRAY_SDK_MODULE = "aws_xray_sdk"
XRAY_SDK_CORE_MODULE = "aws_xray_sdk.core"
XRAY_SDK_MODULE: str = "aws_xray_sdk"
XRAY_SDK_CORE_MODULE: str = "aws_xray_sdk.core"
Loading

0 comments on commit 43b828e

Please sign in to comment.