Description
Use case
I often want to retrieve values from a ProxyEvent using the helper functions prior to the app.resolve()
call; and find myself creating a second instance for it, for example:
@event_source(data_class=APIGatewayProxyEvent)
def lambda_handler(event: APIGatewayProxyEvent, context: LambdaContext):
logger.append_keys(user_agent=event.get_header_value("user-agent", "[No User-Agent]"))
return app.resolve(event.raw_event, context)
Where I feel I should just be able to pass in the instance which was already created:
return app.resolve(event, context)
I've now found myself wanting to use the ApiGatewayResolver class with a Lambda triggered by EventBridge Rules, and found that it can be done trivially with a custom ProxyEvent subclass -- but only if the ApiGatewayResolver accepts the custom ProxyEvent.
The changes required to support a custom ProxyEvent are very minimal and shouldn't be backwards-incompatible.
I see there was an adjustment about 18 months ago in #1152 & #1159 where a warning message and fallback were added; my proposed change involves replacing the warning with fully supporting the existing ProxyEvent class.
Solution/User Experience
@event_source(data_class=APIGatewayProxyEvent)
def lambda_handler(event: APIGatewayProxyEvent, context: LambdaContext):
logger.append_keys(user_agent=event.get_header_value("user-agent", "[No User-Agent]"))
return app.resolve(event.raw_event, context)
becomes
@event_source(data_class=APIGatewayProxyEvent)
def lambda_handler(event: APIGatewayProxyEvent, context: LambdaContext):
logger.append_keys(user_agent=event.get_header_value("user-agent", "[No User-Agent]"))
return app.resolve(event, context)
Also it allows a custom ProxyEvent to be used such as:
class CustomProxyEvent(BaseProxyEvent):
def __init__(self, data: dict[str, Any], json_deserializer: Optional[Callable]=None):
data_wrapper = {"path": "", "httpMethod": data["action"], "body": json.dumps(data)}
super().__init__(data_wrapper, json_deserializer)
self._json_data = data
@property
def action(self) -> str:
return cast(str, cast(dict, self._json_data)["action"])
@property
def content(self) -> str:
return cast(str, cast(dict, self._json_data)["content"])
# Unfortunately this must still be provided unless we mark it as optional.
# I have a separate commit prepared, if you are willing to consider I can include it
def header_serializer(self) -> BaseHeadersSerializer:
return MultiValueHeadersSerializer()
app = ApiGatewayResolver(debug=True)
logger = Logger()
@app.route("", "foo")
def group_stats_get() -> Response:
return Response(200, body=app.current_event.content)
@event_source(data_class=CustomProxyEvent)
def handler(event: CustomProxyEvent, context: LambdaContext):
logger.append_keys(action=event.action)
return app.resolve(event, context)
with the data
{"action": "foo", "content": "bar"}
Alternative solutions
No response
Acknowledgment
- This feature request meets Powertools for AWS Lambda (Python) Tenets
- Should this be considered in other Powertools for AWS Lambda languages? i.e. Java, TypeScript, and .NET
Metadata
Metadata
Assignees
Labels
Type
Projects
Status