11import base64
22import json
33import logging
4+ import os
45import re
6+ import traceback
57import zlib
68from enum import Enum
79from http import HTTPStatus
810from typing import Any , Callable , Dict , List , Optional , Set , Union
911
1012from aws_lambda_powertools .event_handler import content_types
1113from aws_lambda_powertools .event_handler .exceptions import ServiceError
14+ from aws_lambda_powertools .shared import constants
15+ from aws_lambda_powertools .shared .functions import resolve_truthy_env_var_choice
1216from aws_lambda_powertools .shared .json_encoder import Encoder
1317from aws_lambda_powertools .utilities .data_classes import ALBEvent , APIGatewayProxyEvent , APIGatewayProxyEventV2
1418from aws_lambda_powertools .utilities .data_classes .common import BaseProxyEvent
@@ -28,43 +32,46 @@ class ProxyEventType(Enum):
2832class CORSConfig (object ):
2933 """CORS Config
3034
31-
3235 Examples
3336 --------
3437
3538 Simple cors example using the default permissive cors, not this should only be used during early prototyping
3639
37- from aws_lambda_powertools.event_handler.api_gateway import ApiGatewayResolver
40+ ```python
41+ from aws_lambda_powertools.event_handler.api_gateway import ApiGatewayResolver
3842
39- app = ApiGatewayResolver()
43+ app = ApiGatewayResolver()
4044
41- @app.get("/my/path", cors=True)
42- def with_cors():
43- return {"message": "Foo"}
45+ @app.get("/my/path", cors=True)
46+ def with_cors():
47+ return {"message": "Foo"}
48+ ```
4449
4550 Using a custom CORSConfig where `with_cors` used the custom provided CORSConfig and `without_cors`
4651 do not include any cors headers.
4752
48- from aws_lambda_powertools.event_handler.api_gateway import (
49- ApiGatewayResolver, CORSConfig
50- )
51-
52- cors_config = CORSConfig(
53- allow_origin="https://wwww.example.com/",
54- expose_headers=["x-exposed-response-header"],
55- allow_headers=["x-custom-request-header"],
56- max_age=100,
57- allow_credentials=True,
58- )
59- app = ApiGatewayResolver(cors=cors_config)
60-
61- @app.get("/my/path")
62- def with_cors():
63- return {"message": "Foo"}
53+ ```python
54+ from aws_lambda_powertools.event_handler.api_gateway import (
55+ ApiGatewayResolver, CORSConfig
56+ )
57+
58+ cors_config = CORSConfig(
59+ allow_origin="https://wwww.example.com/",
60+ expose_headers=["x-exposed-response-header"],
61+ allow_headers=["x-custom-request-header"],
62+ max_age=100,
63+ allow_credentials=True,
64+ )
65+ app = ApiGatewayResolver(cors=cors_config)
66+
67+ @app.get("/my/path")
68+ def with_cors():
69+ return {"message": "Foo"}
6470
65- @app.get("/another-one", cors=False)
66- def without_cors():
67- return {"message": "Foo"}
71+ @app.get("/another-one", cors=False)
72+ def without_cors():
73+ return {"message": "Foo"}
74+ ```
6875 """
6976
7077 _REQUIRED_HEADERS = ["Authorization" , "Content-Type" , "X-Amz-Date" , "X-Api-Key" , "X-Amz-Security-Token" ]
@@ -240,20 +247,31 @@ def lambda_handler(event, context):
240247 current_event : BaseProxyEvent
241248 lambda_context : LambdaContext
242249
243- def __init__ (self , proxy_type : Enum = ProxyEventType .APIGatewayProxyEvent , cors : CORSConfig = None ):
250+ def __init__ (
251+ self ,
252+ proxy_type : Enum = ProxyEventType .APIGatewayProxyEvent ,
253+ cors : CORSConfig = None ,
254+ debug : Optional [bool ] = None ,
255+ ):
244256 """
245257 Parameters
246258 ----------
247259 proxy_type: ProxyEventType
248260 Proxy request type, defaults to API Gateway V1
249261 cors: CORSConfig
250262 Optionally configure and enabled CORS. Not each route will need to have to cors=True
263+ debug: Optional[bool]
264+ Enables debug mode, by default False. Can be also be enabled by "POWERTOOLS_EVENT_HANDLER_DEBUG"
265+ environment variable
251266 """
252267 self ._proxy_type = proxy_type
253268 self ._routes : List [Route ] = []
254269 self ._cors = cors
255270 self ._cors_enabled : bool = cors is not None
256271 self ._cors_methods : Set [str ] = {"OPTIONS" }
272+ self ._debug = resolve_truthy_env_var_choice (
273+ choice = debug , env = os .getenv (constants .EVENT_HANDLER_DEBUG_ENV , "false" )
274+ )
257275
258276 def get (self , rule : str , cors : bool = None , compress : bool = False , cache_control : str = None ):
259277 """Get route decorator with GET `method`
@@ -416,6 +434,8 @@ def resolve(self, event, context) -> Dict[str, Any]:
416434 dict
417435 Returns the dict response
418436 """
437+ if self ._debug :
438+ print (self ._json_dump (event ))
419439 self .current_event = self ._to_proxy_event (event )
420440 self .lambda_context = context
421441 return self ._resolve ().build (self .current_event , self ._cors )
@@ -489,6 +509,19 @@ def _call_route(self, route: Route, args: Dict[str, str]) -> ResponseBuilder:
489509 ),
490510 route ,
491511 )
512+ except Exception :
513+ if self ._debug :
514+ # If the user has turned on debug mode,
515+ # we'll let the original exception propagate so
516+ # they get more information about what went wrong.
517+ return ResponseBuilder (
518+ Response (
519+ status_code = 500 ,
520+ content_type = content_types .TEXT_PLAIN ,
521+ body = "" .join (traceback .format_exc ()),
522+ )
523+ )
524+ raise
492525
493526 def _to_response (self , result : Union [Dict , Response ]) -> Response :
494527 """Convert the route's result to a Response
@@ -509,7 +542,9 @@ def _to_response(self, result: Union[Dict, Response]) -> Response:
509542 body = self ._json_dump (result ),
510543 )
511544
512- @staticmethod
513- def _json_dump (obj : Any ) -> str :
514- """Does a concise json serialization"""
515- return json .dumps (obj , separators = ("," , ":" ), cls = Encoder )
545+ def _json_dump (self , obj : Any ) -> str :
546+ """Does a concise json serialization or pretty print when in debug mode"""
547+ if self ._debug :
548+ return json .dumps (obj , indent = 4 , cls = Encoder )
549+ else :
550+ return json .dumps (obj , separators = ("," , ":" ), cls = Encoder )
0 commit comments