1
1
import base64
2
2
import json
3
3
import logging
4
+ import os
4
5
import re
6
+ import traceback
5
7
import zlib
6
8
from enum import Enum
7
9
from http import HTTPStatus
8
10
from typing import Any , Callable , Dict , List , Optional , Set , Union
9
11
10
12
from aws_lambda_powertools .event_handler import content_types
11
13
from 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
12
16
from aws_lambda_powertools .shared .json_encoder import Encoder
13
17
from aws_lambda_powertools .utilities .data_classes import ALBEvent , APIGatewayProxyEvent , APIGatewayProxyEventV2
14
18
from aws_lambda_powertools .utilities .data_classes .common import BaseProxyEvent
@@ -28,43 +32,46 @@ class ProxyEventType(Enum):
28
32
class CORSConfig (object ):
29
33
"""CORS Config
30
34
31
-
32
35
Examples
33
36
--------
34
37
35
38
Simple cors example using the default permissive cors, not this should only be used during early prototyping
36
39
37
- from aws_lambda_powertools.event_handler.api_gateway import ApiGatewayResolver
40
+ ```python
41
+ from aws_lambda_powertools.event_handler.api_gateway import ApiGatewayResolver
38
42
39
- app = ApiGatewayResolver()
43
+ app = ApiGatewayResolver()
40
44
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
+ ```
44
49
45
50
Using a custom CORSConfig where `with_cors` used the custom provided CORSConfig and `without_cors`
46
51
do not include any cors headers.
47
52
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"}
64
70
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
+ ```
68
75
"""
69
76
70
77
_REQUIRED_HEADERS = ["Authorization" , "Content-Type" , "X-Amz-Date" , "X-Api-Key" , "X-Amz-Security-Token" ]
@@ -240,20 +247,31 @@ def lambda_handler(event, context):
240
247
current_event : BaseProxyEvent
241
248
lambda_context : LambdaContext
242
249
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
+ ):
244
256
"""
245
257
Parameters
246
258
----------
247
259
proxy_type: ProxyEventType
248
260
Proxy request type, defaults to API Gateway V1
249
261
cors: CORSConfig
250
262
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
251
266
"""
252
267
self ._proxy_type = proxy_type
253
268
self ._routes : List [Route ] = []
254
269
self ._cors = cors
255
270
self ._cors_enabled : bool = cors is not None
256
271
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
+ )
257
275
258
276
def get (self , rule : str , cors : bool = None , compress : bool = False , cache_control : str = None ):
259
277
"""Get route decorator with GET `method`
@@ -416,6 +434,8 @@ def resolve(self, event, context) -> Dict[str, Any]:
416
434
dict
417
435
Returns the dict response
418
436
"""
437
+ if self ._debug :
438
+ print (self ._json_dump (event ))
419
439
self .current_event = self ._to_proxy_event (event )
420
440
self .lambda_context = context
421
441
return self ._resolve ().build (self .current_event , self ._cors )
@@ -489,6 +509,19 @@ def _call_route(self, route: Route, args: Dict[str, str]) -> ResponseBuilder:
489
509
),
490
510
route ,
491
511
)
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
492
525
493
526
def _to_response (self , result : Union [Dict , Response ]) -> Response :
494
527
"""Convert the route's result to a Response
@@ -509,7 +542,9 @@ def _to_response(self, result: Union[Dict, Response]) -> Response:
509
542
body = self ._json_dump (result ),
510
543
)
511
544
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