Skip to content

Commit 397152b

Browse files
committed
Merge branch 'develop' of https://github.com/awslabs/aws-lambda-powertools-python into docs/event-handler-http
* 'develop' of https://github.com/awslabs/aws-lambda-powertools-python: refactor(event-handler): api gateway handler review changes (#420)
2 parents 0d97c6e + 59b3adf commit 397152b

File tree

3 files changed

+51
-47
lines changed

3 files changed

+51
-47
lines changed

aws_lambda_powertools/event_handler/api_gateway.py

+13-17
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import re
44
import zlib
55
from enum import Enum
6-
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union
6+
from typing import Any, Callable, Dict, List, Optional, Set, Union
77

88
from aws_lambda_powertools.shared.json_encoder import Encoder
99
from aws_lambda_powertools.utilities.data_classes import ALBEvent, APIGatewayProxyEvent, APIGatewayProxyEventV2
@@ -12,14 +12,11 @@
1212

1313

1414
class ProxyEventType(Enum):
15-
"""An enumerations of the supported proxy event types.
15+
"""An enumerations of the supported proxy event types."""
1616

17-
**NOTE:** api_gateway is an alias of http_api_v1"""
18-
19-
http_api_v1 = "APIGatewayProxyEvent"
20-
http_api_v2 = "APIGatewayProxyEventV2"
21-
alb_event = "ALBEvent"
22-
api_gateway = http_api_v1
17+
APIGatewayProxyEvent = "APIGatewayProxyEvent"
18+
APIGatewayProxyEventV2 = "APIGatewayProxyEventV2"
19+
ALBEvent = "ALBEvent"
2320

2421

2522
class CORSConfig(object):
@@ -236,7 +233,7 @@ class ApiGatewayResolver:
236233
current_event: BaseProxyEvent
237234
lambda_context: LambdaContext
238235

239-
def __init__(self, proxy_type: Enum = ProxyEventType.http_api_v1, cors: CORSConfig = None):
236+
def __init__(self, proxy_type: Enum = ProxyEventType.APIGatewayProxyEvent, cors: CORSConfig = None):
240237
"""
241238
Parameters
242239
----------
@@ -310,9 +307,9 @@ def _compile_regex(rule: str):
310307

311308
def _to_proxy_event(self, event: Dict) -> BaseProxyEvent:
312309
"""Convert the event dict to the corresponding data class"""
313-
if self._proxy_type == ProxyEventType.http_api_v1:
310+
if self._proxy_type == ProxyEventType.APIGatewayProxyEvent:
314311
return APIGatewayProxyEvent(event)
315-
if self._proxy_type == ProxyEventType.http_api_v2:
312+
if self._proxy_type == ProxyEventType.APIGatewayProxyEventV2:
316313
return APIGatewayProxyEventV2(event)
317314
return ALBEvent(event)
318315

@@ -327,9 +324,9 @@ def _resolve(self) -> ResponseBuilder:
327324
if match:
328325
return self._call_route(route, match.groupdict())
329326

330-
return self._not_found(method, path)
327+
return self._not_found(method)
331328

332-
def _not_found(self, method: str, path: str) -> ResponseBuilder:
329+
def _not_found(self, method: str) -> ResponseBuilder:
333330
"""Called when no matching route was found and includes support for the cors preflight response"""
334331
headers = {}
335332
if self._cors:
@@ -344,7 +341,7 @@ def _not_found(self, method: str, path: str) -> ResponseBuilder:
344341
status_code=404,
345342
content_type="application/json",
346343
headers=headers,
347-
body=json.dumps({"message": f"No route found for '{method}.{path}'"}),
344+
body=json.dumps({"message": "Not found"}),
348345
)
349346
)
350347

@@ -353,12 +350,11 @@ def _call_route(self, route: Route, args: Dict[str, str]) -> ResponseBuilder:
353350
return ResponseBuilder(self._to_response(route.func(**args)), route)
354351

355352
@staticmethod
356-
def _to_response(result: Union[Tuple[int, str, Union[bytes, str]], Dict, Response]) -> Response:
353+
def _to_response(result: Union[Dict, Response]) -> Response:
357354
"""Convert the route's result to a Response
358355
359-
3 main result types are supported:
356+
2 main result types are supported:
360357
361-
- Tuple[int, str, bytes] and Tuple[int, str, str]: status code, content-type and body (str|bytes)
362358
- Dict[str, Any]: Rest api response with just the Dict to json stringify and content-type is set to
363359
application/json
364360
- Response: returned as is, and allows for more flexibility

docs/core/event_handler/api_gateway.md

+10-2
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ from aws_lambda_powertools.event_handler.api_gateway import (
265265
tracer = Tracer()
266266
# Other supported proxy_types: "APIGatewayProxyEvent", "APIGatewayProxyEventV2", "ALBEvent"
267267
app = ApiGatewayResolver(
268-
proxy_type=ProxyEventType.http_api_v1,
268+
proxy_type=ProxyEventType.APIGatewayProxyEvent,
269269
cors=CORSConfig(
270270
allow_origin="https://www.example.com/",
271271
expose_headers=["x-exposed-response-header"],
@@ -275,24 +275,28 @@ app = ApiGatewayResolver(
275275
)
276276
)
277277

278+
278279
@app.get("/foo", compress=True)
279280
def get_foo() -> Tuple[int, str, str]:
280281
# Matches on http GET and proxy path "/foo"
281282
# and return status code: 200, content-type: text/html and body: Hello
282283
return 200, "text/html", "Hello"
283284

285+
284286
@app.get("/logo.png")
285287
def get_logo() -> Tuple[int, str, bytes]:
286288
# Base64 encodes the return bytes body automatically
287289
logo: bytes = load_logo()
288290
return 200, "image/png", logo
289291

292+
290293
@app.post("/make_foo", cors=True)
291294
def make_foo() -> Tuple[int, str, str]:
292295
# Matches on http POST and proxy path "/make_foo"
293-
post_data: dict = app. current_event.json_body
296+
post_data: dict = app.current_event.json_body
294297
return 200, "application/json", json.dumps(post_data["value"])
295298

299+
296300
@app.delete("/delete/<uid>")
297301
def delete_foo(uid: str) -> Tuple[int, str, str]:
298302
# Matches on http DELETE and proxy path starting with "/delete/"
@@ -301,16 +305,19 @@ def delete_foo(uid: str) -> Tuple[int, str, str]:
301305
assert app.current_event.request_context.authorizer.claims["username"] == "Mike"
302306
return 200, "application/json", json.dumps({"id": uid})
303307

308+
304309
@app.get("/hello/<username>")
305310
def hello_user(username: str) -> Tuple[int, str, str]:
306311
return 200, "text/html", f"Hello {username}!"
307312

313+
308314
@app.get("/rest")
309315
def rest_fun() -> Dict:
310316
# Returns a statusCode: 200, Content-Type: application/json and json.dumps dict
311317
# and handles the serialization of decimals to json string
312318
return {"message": "Example", "second": Decimal("100.01")}
313319

320+
314321
@app.get("/foo3")
315322
def foo3() -> Response:
316323
return Response(
@@ -320,6 +327,7 @@ def foo3() -> Response:
320327
body=json.dumps({"message": "Foo3"}),
321328
)
322329

330+
323331
@tracer.capture_lambda_handler
324332
def lambda_handler(event, context) -> Dict:
325333
return app.resolve(event, context)

tests/functional/event_handler/test_api_gateway.py

+28-28
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import zlib
44
from decimal import Decimal
55
from pathlib import Path
6-
from typing import Dict, Tuple
6+
from typing import Dict
77

88
from aws_lambda_powertools.event_handler.api_gateway import (
99
ApiGatewayResolver,
@@ -29,10 +29,10 @@ def read_media(file_name: str) -> bytes:
2929

3030
def test_alb_event():
3131
# GIVEN a Application Load Balancer proxy type event
32-
app = ApiGatewayResolver(proxy_type=ProxyEventType.alb_event)
32+
app = ApiGatewayResolver(proxy_type=ProxyEventType.ALBEvent)
3333

3434
@app.get("/lambda")
35-
def foo() -> Tuple[int, str, str]:
35+
def foo():
3636
assert isinstance(app.current_event, ALBEvent)
3737
assert app.lambda_context == {}
3838
return 200, TEXT_HTML, "foo"
@@ -49,13 +49,13 @@ def foo() -> Tuple[int, str, str]:
4949

5050
def test_api_gateway_v1():
5151
# GIVEN a Http API V1 proxy type event
52-
app = ApiGatewayResolver(proxy_type=ProxyEventType.http_api_v1)
52+
app = ApiGatewayResolver(proxy_type=ProxyEventType.APIGatewayProxyEvent)
5353

5454
@app.get("/my/path")
55-
def get_lambda() -> Tuple[int, str, str]:
55+
def get_lambda() -> Response:
5656
assert isinstance(app.current_event, APIGatewayProxyEvent)
5757
assert app.lambda_context == {}
58-
return 200, APPLICATION_JSON, json.dumps({"foo": "value"})
58+
return Response(200, APPLICATION_JSON, json.dumps({"foo": "value"}))
5959

6060
# WHEN calling the event handler
6161
result = app(LOAD_GW_EVENT, {})
@@ -68,12 +68,12 @@ def get_lambda() -> Tuple[int, str, str]:
6868

6969
def test_api_gateway():
7070
# GIVEN a Rest API Gateway proxy type event
71-
app = ApiGatewayResolver(proxy_type=ProxyEventType.api_gateway)
71+
app = ApiGatewayResolver(proxy_type=ProxyEventType.APIGatewayProxyEvent)
7272

7373
@app.get("/my/path")
74-
def get_lambda() -> Tuple[int, str, str]:
74+
def get_lambda() -> Response:
7575
assert isinstance(app.current_event, APIGatewayProxyEvent)
76-
return 200, TEXT_HTML, "foo"
76+
return Response(200, TEXT_HTML, "foo")
7777

7878
# WHEN calling the event handler
7979
result = app(LOAD_GW_EVENT, {})
@@ -87,13 +87,13 @@ def get_lambda() -> Tuple[int, str, str]:
8787

8888
def test_api_gateway_v2():
8989
# GIVEN a Http API V2 proxy type event
90-
app = ApiGatewayResolver(proxy_type=ProxyEventType.http_api_v2)
90+
app = ApiGatewayResolver(proxy_type=ProxyEventType.APIGatewayProxyEventV2)
9191

9292
@app.post("/my/path")
93-
def my_path() -> Tuple[int, str, str]:
93+
def my_path() -> Response:
9494
assert isinstance(app.current_event, APIGatewayProxyEventV2)
9595
post_data = app.current_event.json_body
96-
return 200, "plain/text", post_data["username"]
96+
return Response(200, "plain/text", post_data["username"])
9797

9898
# WHEN calling the event handler
9999
result = app(load_event("apiGatewayProxyV2Event.json"), {})
@@ -110,9 +110,9 @@ def test_include_rule_matching():
110110
app = ApiGatewayResolver()
111111

112112
@app.get("/<name>/<my_id>")
113-
def get_lambda(my_id: str, name: str) -> Tuple[int, str, str]:
113+
def get_lambda(my_id: str, name: str) -> Response:
114114
assert name == "my"
115-
return 200, TEXT_HTML, my_id
115+
return Response(200, TEXT_HTML, my_id)
116116

117117
# WHEN calling the event handler
118118
result = app(LOAD_GW_EVENT, {})
@@ -179,8 +179,8 @@ def test_cors():
179179
app = ApiGatewayResolver()
180180

181181
@app.get("/my/path", cors=True)
182-
def with_cors() -> Tuple[int, str, str]:
183-
return 200, TEXT_HTML, "test"
182+
def with_cors() -> Response:
183+
return Response(200, TEXT_HTML, "test")
184184

185185
def handler(event, context):
186186
return app.resolve(event, context)
@@ -205,8 +205,8 @@ def test_compress():
205205
expected_value = '{"test": "value"}'
206206

207207
@app.get("/my/request", compress=True)
208-
def with_compression() -> Tuple[int, str, str]:
209-
return 200, APPLICATION_JSON, expected_value
208+
def with_compression() -> Response:
209+
return Response(200, APPLICATION_JSON, expected_value)
210210

211211
def handler(event, context):
212212
return app.resolve(event, context)
@@ -230,8 +230,8 @@ def test_base64_encode():
230230
mock_event = {"path": "/my/path", "httpMethod": "GET", "headers": {"Accept-Encoding": "deflate, gzip"}}
231231

232232
@app.get("/my/path", compress=True)
233-
def read_image() -> Tuple[int, str, bytes]:
234-
return 200, "image/png", read_media("idempotent_sequence_exception.png")
233+
def read_image() -> Response:
234+
return Response(200, "image/png", read_media("idempotent_sequence_exception.png"))
235235

236236
# WHEN calling the event handler
237237
result = app(mock_event, None)
@@ -251,8 +251,8 @@ def test_compress_no_accept_encoding():
251251
expected_value = "Foo"
252252

253253
@app.get("/my/path", compress=True)
254-
def return_text() -> Tuple[int, str, str]:
255-
return 200, "text/plain", expected_value
254+
def return_text() -> Response:
255+
return Response(200, "text/plain", expected_value)
256256

257257
# WHEN calling the event handler
258258
result = app({"path": "/my/path", "httpMethod": "GET", "headers": {}}, None)
@@ -267,8 +267,8 @@ def test_cache_control_200():
267267
app = ApiGatewayResolver()
268268

269269
@app.get("/success", cache_control="max-age=600")
270-
def with_cache_control() -> Tuple[int, str, str]:
271-
return 200, TEXT_HTML, "has 200 response"
270+
def with_cache_control() -> Response:
271+
return Response(200, TEXT_HTML, "has 200 response")
272272

273273
def handler(event, context):
274274
return app.resolve(event, context)
@@ -288,8 +288,8 @@ def test_cache_control_non_200():
288288
app = ApiGatewayResolver()
289289

290290
@app.delete("/fails", cache_control="max-age=600")
291-
def with_cache_control_has_500() -> Tuple[int, str, str]:
292-
return 503, TEXT_HTML, "has 503 response"
291+
def with_cache_control_has_500() -> Response:
292+
return Response(503, TEXT_HTML, "has 503 response")
293293

294294
def handler(event, context):
295295
return app.resolve(event, context)
@@ -306,7 +306,7 @@ def handler(event, context):
306306

307307
def test_rest_api():
308308
# GIVEN a function that returns a Dict
309-
app = ApiGatewayResolver(proxy_type=ProxyEventType.http_api_v1)
309+
app = ApiGatewayResolver(proxy_type=ProxyEventType.APIGatewayProxyEvent)
310310
expected_dict = {"foo": "value", "second": Decimal("100.01")}
311311

312312
@app.get("/my/path")
@@ -325,7 +325,7 @@ def rest_func() -> Dict:
325325

326326
def test_handling_response_type():
327327
# GIVEN a function that returns Response
328-
app = ApiGatewayResolver(proxy_type=ProxyEventType.http_api_v1)
328+
app = ApiGatewayResolver(proxy_type=ProxyEventType.APIGatewayProxyEvent)
329329

330330
@app.get("/my/path")
331331
def rest_func() -> Response:

0 commit comments

Comments
 (0)