Skip to content

feat(event_handler): add Middleware support for REST Event Handler #2917

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

Merged
merged 92 commits into from
Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
fe2341f
feat(rest-middleware): initial work for rest API middleware
walmsles Aug 2, 2023
4cfa87d
feat(rest-middlware): Add in Router based middleware and add Middlewa…
walmsles Aug 3, 2023
4e28b06
feat(api-middleware): remove compression middleware - not implemented…
walmsles Aug 3, 2023
edcc6f7
fix(typing): resolve mypy errors
walmsles Aug 3, 2023
2c67e10
Update aws_lambda_powertools/event_handler/api_gateway.py
walmsles Aug 3, 2023
822f16b
Update aws_lambda_powertools/event_handler/api_gateway.py
walmsles Aug 3, 2023
51c3da5
Update aws_lambda_powertools/event_handler/api_gateway.py
walmsles Aug 3, 2023
98da7dc
Update aws_lambda_powertools/event_handler/api_gateway.py
walmsles Aug 3, 2023
d775bba
Update aws_lambda_powertools/event_handler/api_gateway.py
walmsles Aug 3, 2023
d12fa20
Update aws_lambda_powertools/event_handler/api_gateway.py
walmsles Aug 3, 2023
e765ebc
Apply suggestions from code review
walmsles Aug 3, 2023
6e54256
fix(naming-intent): rename middleware to middlewares universally in a…
walmsles Aug 3, 2023
727effb
fix(args-debt): rename args to route_arguments in all places
walmsles Aug 3, 2023
0afe31c
fix(middleware-clarity): Rename and repackage middleware construction…
walmsles Aug 4, 2023
8501b9a
fix(jsdocs): added docs to MiddlewareStackWrapper and BaseMiddlewareH…
walmsles Aug 4, 2023
0d59e12
fix(types): Mkae MiddlewareStackWrapper __call__ return type more spe…
walmsles Aug 4, 2023
6b9b369
fix(middlewares): internalise registered_api_adapter and remove Cors/…
walmsles Aug 5, 2023
409da32
feat(middlewares): Add chema validation middleware and internalise re…
walmsles Aug 5, 2023
1a90b34
chore(security): relabel http to https for test JSON Schema (because …
walmsles Aug 5, 2023
a9d9cb2
Apply suggestions from code review
walmsles Aug 10, 2023
10080b5
chore(typing): Ficup typing and function/attribute scopes - keep priv…
walmsles Aug 10, 2023
de7247d
feat(middlewares): Add optional outbound schema dn formats to schema …
walmsles Aug 10, 2023
dbae938
fix(errors): add logger for config exceptions and return minimal details
walmsles Aug 10, 2023
13df122
chore(docs): fixup format of docstrings to numpy format
walmsles Aug 10, 2023
8067116
chore(docs): change docstrings to numpy format
walmsles Aug 10, 2023
6ce64f1
fix(tests): fix failing tests due to resposne change:
walmsles Aug 11, 2023
22b83e0
chore(tests): group middleware testing to one module
walmsles Aug 11, 2023
af719f4
chore(docs): api_agteway - improve docs for middleware and examples
walmsles Aug 13, 2023
0adfc70
chore(docs): document why mypy 'type: ignore' is used here
walmsles Aug 13, 2023
63db815
feat(api_middleware): add source diagram for middlewares docs
walmsles Aug 13, 2023
3131f3e
fix(tests): fix unreachable code
walmsles Aug 13, 2023
1a20c9e
fix(debug): fix processed middleware stack frame list, cleanup try/ca…
walmsles Aug 15, 2023
48a9462
fix(debug): revert small change to ensure debug noise reduced via han…
walmsles Aug 15, 2023
587348c
chore(comments): cleanup middleware execution
walmsles Aug 19, 2023
deabb26
feat(docs): Add docs for event_handler middleware
walmsles Aug 20, 2023
fbb36ab
feat(docs): Tidy up middleware docs and add to section to split routes
walmsles Aug 20, 2023
d4fc1ba
feat(route): expose matched route instance to Resolver in additional …
walmsles Aug 22, 2023
837613b
fix(resolver): Add 'powertols_route' to additional context
walmsles Aug 22, 2023
9aec250
Merge branch 'develop' into feat/api-middleware
heitorlessa Aug 28, 2023
49537a9
docs: make intro punchy plus intro diagram
heitorlessa Aug 28, 2023
d0be0e4
docs: use correlation id as first middleware
heitorlessa Aug 28, 2023
2b6ee0b
docs: add output for completeness
heitorlessa Aug 28, 2023
33cf3ad
docs: grammar
heitorlessa Aug 28, 2023
40b68a0
docs: note that middleware works for any resolver
heitorlessa Aug 28, 2023
7bd0dba
docs: add global middlewares section
heitorlessa Aug 28, 2023
947d0ce
docs: additional ctx for global middlewares order
heitorlessa Aug 28, 2023
efee2ae
docs: add early return section
heitorlessa Aug 28, 2023
741f2a5
feat(route): expose route instance in ephemeral context dict
walmsles Aug 30, 2023
388592f
feat(middlewwares): Expand tests to incdlue core gateway types
walmsles Aug 30, 2023
af15705
feat(middlewares): Additional Tests for route middleware, added debug…
walmsles Aug 30, 2023
bf439e4
feat(middlewares): Tidy up config, add additional context for _path f…
walmsles Aug 30, 2023
80aa67e
Merge branch 'develop' into feat/api-middleware
walmsles Sep 1, 2023
9fb4c6f
chore(docs): updated imges to be svg for middlewares from draw.io - l…
walmsles Sep 1, 2023
06b1d90
chore(example): fix custom middleware example to correct raise of cap…
walmsles Sep 2, 2023
61022be
chore: type middleware callback
heitorlessa Sep 2, 2023
21af597
chore: type middleware response
heitorlessa Sep 2, 2023
0ca6683
docs: improve early return; use kwargs over ctx
heitorlessa Sep 2, 2023
9704939
docs: add handling exceptions section
heitorlessa Sep 2, 2023
d166de3
docs: fix media typo
heitorlessa Sep 2, 2023
f476f7d
docs: add being a good citizen section
heitorlessa Sep 3, 2023
e8a071e
docs: add skeleton for class-based middleware
heitorlessa Sep 3, 2023
dd59a7f
Merge branch 'develop' into feat/api-middleware
heitorlessa Sep 4, 2023
1c0977a
chore: test class based middlewares
heitorlessa Sep 4, 2023
7d361b6
docs: rename class based section
heitorlessa Sep 4, 2023
85e65b0
feat(middlewares): remove kwargs from middleware signature for cleane…
walmsles Sep 4, 2023
3a20318
feat(middlewares): align signatures for middlewareprocessing
walmsles Sep 4, 2023
5edde6a
feat(middlewares): cleanup typing for next callbacks
walmsles Sep 4, 2023
a0e784b
feat(middlewares): align typing for next_middlewares for consistency
walmsles Sep 4, 2023
f20b778
Merge branch 'develop' into feat/api-middleware
heitorlessa Sep 4, 2023
d8cf386
chore: use generics to accept any event handler
heitorlessa Sep 4, 2023
f5a613a
refactor: tech debt overload on get_header_value for middleware examp…
heitorlessa Sep 4, 2023
2f2eb91
chore: enforce protocol type checking for app instance
heitorlessa Sep 4, 2023
03708bb
docs: complete extending middlewares section
heitorlessa Sep 4, 2023
5dc5719
fix: remove schema validation export due to optional dependency
heitorlessa Sep 4, 2023
f7800b2
refactor: improve docstrings for schema validation; typing
heitorlessa Sep 4, 2023
6f63b4f
chore: leftover from previous circular dep issue
heitorlessa Sep 4, 2023
03d15fa
docs: add native middleware section
heitorlessa Sep 4, 2023
b7bcb08
docs - refactor references of with kwargs to , remove **lwargs refer…
walmsles Sep 4, 2023
7ca4a3a
chore: add middleware order test
heitorlessa Sep 5, 2023
870545a
refactor: add debug log
heitorlessa Sep 5, 2023
44cb04b
docs: move staging area to router section
heitorlessa Sep 5, 2023
3832817
Merge branch 'develop' into feat/api-middleware
heitorlessa Sep 5, 2023
4e1a654
fix: remove leftover order from middleware order test
heitorlessa Sep 5, 2023
6400b28
Merge branch 'develop' into feat/api-middleware
heitorlessa Sep 5, 2023
19eaa73
middlewares: reverse internal function rename to ensure no breaking c…
walmsles Sep 6, 2023
592d9da
chore(tests): remove typing isues
walmsles Sep 6, 2023
d1508c7
docs: middleware in router
heitorlessa Sep 7, 2023
aa68e40
chore: last cleanups
heitorlessa Sep 7, 2023
98948ec
docs: leftover to highlight works for micro/mono fns
heitorlessa Sep 7, 2023
20c344a
docs: fix highlighting after refactoring
heitorlessa Sep 7, 2023
4ed0076
chore: remove backup drawio file
heitorlessa Sep 7, 2023
7fbb132
Merge branch 'develop' into feat/api-middleware
leandrodamascena Sep 7, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Apply suggestions from code review
Co-authored-by: Heitor Lessa <lessa@amazon.nl>
Signed-off-by: walmsles <2704782+walmsles@users.noreply.github.com>
  • Loading branch information
walmsles and heitorlessa committed Aug 23, 2023
commit a9d9cb2b4c9b76b40a3e303f2bf948dd68214b5f
37 changes: 26 additions & 11 deletions aws_lambda_powertools/event_handler/api_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ def build_middleware_stack(self, router_middlewares: List[Callable]) -> None:
handler wanting to short-circuit the middlware call chain should raise an execution
to force the Python call stack created by the handler call-chain to naturally un-wind.

This becomes a simple concept for Users ot understand and reason with - no additional
This becomes a simple concept for Users to understand and reason with - no additional
gymanstics other than plain old try ... except.

Notes
Expand Down Expand Up @@ -416,10 +416,10 @@ def route(

def use(self, middlewares: List[Callable[..., Any]]):
"""
Add a List of middlewares to the global routetr middleware list
Add a list of middlewares to the global router middleware list

These Middleware handlers will be executed in order of adding to the array and will
be executed before any specific middleware applied at the actual route level.
These middlewares will be called in insertion order and
before any middleware registered at the route level.
"""
self.router_middlewares = self.router_middlewares + middlewares

Expand Down Expand Up @@ -597,7 +597,7 @@ def clear_context(self):
self.context.clear()


class MiddlewareStackWrapper:
class MiddlewareFrame:
"""
creates a Middle Stack Wrapper instance to be used as a "Frame" in the overall stack of
middleware functions. Each instance contains the current middleware to be executed and the next
Expand All @@ -622,11 +622,27 @@ class MiddlewareStackWrapper:

def __init__(
self,
handler: Callable,
current_middleware: Callable,
next_middleware: Callable,
) -> None:
self.handler: Callable = handler
self.next_middleware: Callable = next_middleware
self._next_middleware_name = next_middleware.__name__


@property
def __name__():
"""Current middleware name

It ensures backward compatibility with view functions being callable. This
improves debugging since we need both current and next middlewares/callable names.
"""
return self.handler.__name__

def __str__(self) -> str:
"""Identify current middleware identity and call chain for debugging purposes."""
middleware_name = self.__name__
return f"Middleware '{middleware_name}'. Call chain is: {middleware_name} -> {self._next_middleware_name}"

def __call__(self, app: BaseRouter, **kwargs) -> Union[Dict, Tuple, Response]:
"""
Expand All @@ -642,16 +658,15 @@ def __call__(self, app: BaseRouter, **kwargs) -> Union[Dict, Tuple, Response]:
Returns
-------
Union[Dict, Tuple, Response]
(tech-debt for backward compatability). The response type should be a
Response object in all cases excepting when the oiginal API rout handler
(tech-debt for backward compatibility). The response type should be a
Response object in all cases excepting when the original API rout handler
is executed which will return one of 3 outputs.

"""
return self.handler(app, self.next_middleware, **kwargs)
return self.current_middleware(app, self.next_middleware, **kwargs)


# No type checking possible due to Dependency order of definition ()
def registered_api_adapter(app, get_response: Callable[..., Any], **kwargs) -> Union[Dict, Tuple, Response]:
def registered_api_adapter(app: "ApiGatewayResolver", get_response: Callable[..., Any], **kwargs) -> Union[Dict, Tuple, Response]:
"""
Calls the registered API using ONLY the **kwargs provided to ensure the
call signature of existing defined router of Users does not create a breaking change.
Expand Down
11 changes: 5 additions & 6 deletions aws_lambda_powertools/event_handler/middlewares/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ class BaseMiddlewareHandler(ABC):
```python

# Place code here for actions BEFORE the next middleware handler is called
# or optionally raise an excpetion to short-circuit the middleware execution chain
# or optionally raise an exception to short-circuit the middleware execution chain

# Get the response from the NEXT middleware handler (optionally injecting custom
# arguments into the get_response call)
result: Response = get_response(app, my_custom_arg="handled", **kwargs)

# Place code ehre for actions AFTER the next middleware handler is called
# Place code here for actions AFTER the next middleware handler is called

return result
```
Expand All @@ -32,16 +32,16 @@ class BaseMiddlewareHandler(ABC):
specific types of exceptions.

for example:
============

```python

try:
result: Response = get_response(app, my_custom_arg="handled", **kwargs)
except MyCustomValidationException as e:
# Make sure we send back a 400 resposne for any Custom Validation Exceptions.
# Make sure we send back a 400 response for any Custom Validation Exceptions.
result.status_code = 400
result.body = {"message": str(e)}
result.body = {"message": "Failed validation"}
logger.exception(f"Failed validation when handling route: {app.current_event.path}")

return result
```
Expand All @@ -51,7 +51,6 @@ class BaseMiddlewareHandler(ABC):
handler to get the response from the next middleware handler in the chain.

for example:
============
If you wanted to ensure API callers cannot call a DELETE verb on your API (regardless of defined routes)
you could do so with the following middleware implementation.

Expand Down
2 changes: 1 addition & 1 deletion tests/functional/event_handler/test_api_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -1853,7 +1853,7 @@ def test_middleware_early_return():
# GIVEN
app = ApiGatewayResolver()

def middleware_one(app, get_response, **kwargs):
def middleware_one(app, get_response, **context):
# inject a variable into the kwargs
response = get_response(app, injected="injected_value", **kwargs)

Expand Down