-
Notifications
You must be signed in to change notification settings - Fork 402
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: API Gateway Proxy Event utils #325
Comments
Hey @michaelbrewer - Could you let us know when the RFC body is ready to review? It's missing a few sections like drawbacks, rationale, a more complete proposal besides the link, and any open questions you might have (if any), etc. |
@heitorlessa here is a super lightweight implementation: from typing import Any, Dict, Tuple, Callable, List
from aws_lambda_powertools.utilities.data_classes import APIGatewayProxyEvent
from aws_lambda_powertools.utilities.typing import LambdaContext
class ApiGatewayResolver:
def __init__(self):
self._resolvers: List[Dict] = []
def _register(
self,
func: Callable[[Any, Any], Tuple[int, str, str]],
http_method: str,
uri_starts_with: str,
include_event: bool,
include_context: bool,
kwargs: Dict,
):
kwargs["include_event"] = include_event
kwargs["include_context"] = include_context
self._resolvers.append(
{
"http_method": http_method,
"uri_starts_with": uri_starts_with,
"func": func,
"config": kwargs,
}
)
def get(self, uri: str, include_event: bool = False, include_context: bool = False, **kwargs):
return self.route("GET", uri, include_event, include_context, **kwargs)
def post(self, uri: str, include_event: bool = False, include_context: bool = False, **kwargs):
return self.route("POST", uri, include_event, include_context, **kwargs)
def put(self, uri: str, include_event: bool = False, include_context: bool = False, **kwargs):
return self.route("PUT", uri, include_event, include_context, **kwargs)
def delete(self, uri: str, include_event: bool = False, include_context: bool = False, **kwargs):
return self.route("DELETE", uri, include_event, include_context, **kwargs)
def route(
self,
method: str,
uri: str,
include_event: bool = False,
include_context: bool = False,
**kwargs,
):
def register_resolver(func: Callable[[Any, Any], Tuple[int, str, str]]):
self._register(func, method.upper(), uri, include_event, include_context, kwargs)
return func
return register_resolver
def resolve(self, _event: dict, context: LambdaContext) -> Dict:
event = APIGatewayProxyEvent(_event)
path = _event.get("pathParameters", {}).get("proxy")
resolver: Callable[[Any], Tuple[int, str, str]]
config: Dict
resolver, config = self._find_resolver(event.http_method.upper(), path)
kwargs = self._kwargs(event, context, config)
result = resolver(**kwargs)
return {"statusCode": result[0], "headers": {"Content-Type": result[1]}, "body": result[2]}
def _find_resolver(self, http_method: str, proxy_path: str) -> Tuple[Callable, Dict]:
for resolver in self._resolvers:
expected_method = resolver["http_method"]
if http_method != expected_method:
continue
path_starts_with = resolver["uri_starts_with"]
if proxy_path.startswith(path_starts_with):
return resolver["func"], resolver["config"]
raise ValueError(f"No resolver found for '{http_method}.{proxy_path}'")
@staticmethod
def _kwargs(event: APIGatewayProxyEvent, context: LambdaContext, config: Dict) -> Dict[str, Any]:
kwargs: Dict[str, Any] = {}
if config.get("include_event", False):
kwargs["event"] = event
if config.get("include_context", False):
kwargs["context"] = context
return kwargs
def __call__(self, event, context) -> Any:
return self.resolve(event, context) And its usage: import json
from typing import Tuple
from aws_lambda_powertools.utilities.data_classes import APIGatewayProxyEvent
from aws_lambda_powertools.utilities.event_handler.api_gateway import ApiGatewayResolver
app = ApiGatewayResolver()
@app.get("/foo")
def get_foo() -> Tuple[int, str, str]:
# Matches on http GET and proxy path starting with "/foo"
return 200, "text/html", "Hello"
@app.post("/make_foo", include_event=True)
def make_foo(event: APIGatewayProxyEvent) -> Tuple[int, str, str]:
# Matches on http POST and proxy path starting with "/make_foo"
post_data = json.loads(event.body or "{}")
return 200, "application/json", json.dumps(post_data)
@app.delete("/delete", include_event=True)
def delete_foo(event: APIGatewayProxyEvent) -> Tuple[int, str, str]:
# Matches on http DELETE and proxy path starting with "/delete"
item_to_delete = event.path.removeprefix("/delete/")
return 200, "application/json", json.dumps({"id": item_to_delete}) |
I have updated the RFC PR include a couple more feature ideas: While still keeping the code to a minimum |
This is now OUT, thanks a lot for the gigantic help here @michaelbrewer -- #423 |
Key information
Summary
Add the ability to help map multiple API Gateway Proxy events to a single lambda much like how Chalice or Lambda Proxy does it, but in a very simple and light weight way and still be compatible with Powertools
Motivation
Simplify the work needed to setup API Gateway Proxy Lambdas that support multiple endpoints on a single lambda
Proposal
Build something like: https://github.com/vincentsarago/lambda-proxy
But also allow the develop to keep their existing
handler
with all of the powertools decotators supported kind of like how#324 works.
Drawbacks
Rationale and alternatives
Unresolved questions
The text was updated successfully, but these errors were encountered: