@@ -204,31 +204,75 @@ def build(self, event: BaseProxyEvent, cors: CORSConfig = None) -> Dict[str, Any
204
204
205
205
206
206
class ApiGatewayResolver :
207
+ """API Gateway and ALB proxy resolver
208
+
209
+ Examples
210
+ --------
211
+ Simple example with a custom lambda handler using the Tracer capture_lambda_handler decorator
212
+
213
+ >>> from aws_lambda_powertools import Tracer
214
+ >>> from aws_lambda_powertools.event_handler.api_gateway import (
215
+ >>> ApiGatewayResolver
216
+ >>> )
217
+ >>>
218
+ >>> tracer = Tracer()
219
+ >>> app = ApiGatewayResolver()
220
+ >>>
221
+ >>> @app.get("/get-call")
222
+ >>> def simple_get():
223
+ >>> return {"message": "Foo"}
224
+ >>>
225
+ >>> @app.post("/post-call")
226
+ >>> def simple_post():
227
+ >>> post_data: dict = app.current_event.json_body
228
+ >>> return {"message": post_data["value"]}
229
+ >>>
230
+ >>> @tracer.capture_lambda_handler
231
+ >>> def lambda_handler(event, context):
232
+ >>> return app.resolve(event, context)
233
+
234
+ """
235
+
207
236
current_event : BaseProxyEvent
208
237
lambda_context : LambdaContext
209
238
210
239
def __init__ (self , proxy_type : Enum = ProxyEventType .http_api_v1 , cors : CORSConfig = None ):
240
+ """
241
+ Parameters
242
+ ----------
243
+ proxy_type: ProxyEventType
244
+ Proxy request type, defaults to API Gateway V1
245
+ cors: CORSConfig
246
+ Optionally configure and enabled CORS. Not each route will need to have to cors=True
247
+ """
211
248
self ._proxy_type = proxy_type
212
249
self ._routes : List [Route ] = []
213
250
self ._cors = cors
214
251
self ._cors_methods : Set [str ] = {"OPTIONS" }
215
252
216
253
def get (self , rule : str , cors : bool = False , compress : bool = False , cache_control : str = None ):
254
+ """Get route decorator with GET `method`"""
217
255
return self .route (rule , "GET" , cors , compress , cache_control )
218
256
219
257
def post (self , rule : str , cors : bool = False , compress : bool = False , cache_control : str = None ):
258
+ """Post route decorator with POST `method`"""
220
259
return self .route (rule , "POST" , cors , compress , cache_control )
221
260
222
261
def put (self , rule : str , cors : bool = False , compress : bool = False , cache_control : str = None ):
262
+ """Put route decorator with PUT `method`"""
223
263
return self .route (rule , "PUT" , cors , compress , cache_control )
224
264
225
265
def delete (self , rule : str , cors : bool = False , compress : bool = False , cache_control : str = None ):
266
+ """Delete route decorator with DELETE `method`"""
226
267
return self .route (rule , "DELETE" , cors , compress , cache_control )
227
268
228
269
def patch (self , rule : str , cors : bool = False , compress : bool = False , cache_control : str = None ):
270
+ """Patch route decorator with PATCH `method`"""
229
271
return self .route (rule , "PATCH" , cors , compress , cache_control )
230
272
231
273
def route (self , rule : str , method : str , cors : bool = False , compress : bool = False , cache_control : str = None ):
274
+ """Route decorator includes parameter `method`"""
275
+
232
276
def register_resolver (func : Callable ):
233
277
self ._routes .append (Route (method , self ._compile_regex (rule ), func , cors , compress , cache_control ))
234
278
if cors :
@@ -238,9 +282,22 @@ def register_resolver(func: Callable):
238
282
return register_resolver
239
283
240
284
def resolve (self , event , context ) -> Dict [str , Any ]:
241
- self .current_event = self ._to_data_class (event )
285
+ """Resolves the response based on the provide event and decorator routes
286
+
287
+ Parameters
288
+ ----------
289
+ event: Dict[str, Any]
290
+ Event
291
+ context: LambdaContext
292
+ Lambda context
293
+ Returns
294
+ -------
295
+ dict
296
+ Returns the dict response
297
+ """
298
+ self .current_event = self ._to_proxy_event (event )
242
299
self .lambda_context = context
243
- return self ._resolve_response ().build (self .current_event , self ._cors )
300
+ return self ._resolve ().build (self .current_event , self ._cors )
244
301
245
302
def __call__ (self , event , context ) -> Any :
246
303
return self .resolve (event , context )
@@ -251,16 +308,16 @@ def _compile_regex(rule: str):
251
308
rule_regex : str = re .sub (r"(<\w+>)" , r"(?P\1.+)" , rule )
252
309
return re .compile ("^{}$" .format (rule_regex ))
253
310
254
- def _to_data_class (self , event : Dict ) -> BaseProxyEvent :
311
+ def _to_proxy_event (self , event : Dict ) -> BaseProxyEvent :
255
312
"""Convert the event dict to the corresponding data class"""
256
313
if self ._proxy_type == ProxyEventType .http_api_v1 :
257
314
return APIGatewayProxyEvent (event )
258
315
if self ._proxy_type == ProxyEventType .http_api_v2 :
259
316
return APIGatewayProxyEventV2 (event )
260
317
return ALBEvent (event )
261
318
262
- def _resolve_response (self ) -> ResponseBuilder :
263
- """Resolve the response or return the not found response"""
319
+ def _resolve (self ) -> ResponseBuilder :
320
+ """Resolves the response or return the not found response"""
264
321
method = self .current_event .http_method .upper ()
265
322
path = self .current_event .path
266
323
for route in self ._routes :
@@ -273,19 +330,21 @@ def _resolve_response(self) -> ResponseBuilder:
273
330
return self ._not_found (method , path )
274
331
275
332
def _not_found (self , method : str , path : str ) -> ResponseBuilder :
276
- """No matching route was found, includes support for the cors preflight response"""
333
+ """Called when no matching route was found and includes support for the cors preflight response"""
277
334
headers = {}
278
335
if self ._cors :
279
336
headers .update (self ._cors .to_dict ())
337
+
280
338
if method == "OPTIONS" : # Preflight
281
339
headers ["Access-Control-Allow-Methods" ] = "," .join (sorted (self ._cors_methods ))
282
- return ResponseBuilder (Response (status_code = 204 , content_type = None , body = None , headers = headers ))
340
+ return ResponseBuilder (Response (status_code = 204 , content_type = None , headers = headers , body = None ))
341
+
283
342
return ResponseBuilder (
284
343
Response (
285
344
status_code = 404 ,
286
345
content_type = "application/json" ,
287
- body = json .dumps ({"message" : f"No route found for '{ method } .{ path } '" }),
288
346
headers = headers ,
347
+ body = json .dumps ({"message" : f"No route found for '{ method } .{ path } '" }),
289
348
)
290
349
)
291
350
@@ -295,7 +354,15 @@ def _call_route(self, route: Route, args: Dict[str, str]) -> ResponseBuilder:
295
354
296
355
@staticmethod
297
356
def _to_response (result : Union [Tuple [int , str , Union [bytes , str ]], Dict , Response ]) -> Response :
298
- """Convert the route result to a Response"""
357
+ """Convert the route's result to a Response
358
+
359
+ 3 main result types are supported:
360
+
361
+ - Tuple[int, str, bytes] and Tuple[int, str, str]: status code, content-type and body (str|bytes)
362
+ - Dict[str, Any]: Rest api response with just the Dict to json stringify and content-type is set to
363
+ application/json
364
+ - Response: returned as is, and allows for more flexibility
365
+ """
299
366
if isinstance (result , Response ):
300
367
return result
301
368
elif isinstance (result , dict ):
0 commit comments