Skip to content

feat(event_handler): add decorator for HTTP HEAD verb #4275

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 3 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
57 changes: 57 additions & 0 deletions aws_lambda_powertools/event_handler/api_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -1222,6 +1222,63 @@ def lambda_handler(event, context):
middlewares,
)

def head(
self,
rule: str,
cors: Optional[bool] = None,
compress: bool = False,
cache_control: Optional[str] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
responses: Optional[Dict[int, OpenAPIResponse]] = None,
response_description: str = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
tags: Optional[List[str]] = None,
operation_id: Optional[str] = None,
include_in_schema: bool = True,
security: Optional[List[Dict[str, List[str]]]] = None,
middlewares: Optional[List[Callable]] = None,
):
"""Head route decorator with HEAD `method`

Examples
--------
Simple example with a custom lambda handler using the Tracer capture_lambda_handler decorator

```python
from aws_lambda_powertools import Tracer
from aws_lambda_powertools.event_handler import APIGatewayRestResolver, Response, content_types

tracer = Tracer()
app = APIGatewayRestResolver()

@app.head("/head-call")
def simple_head():
return Response(status_code=200,
content_type=content_types.APPLICATION_JSON,
headers={"Content-Length": "123"})

@tracer.capture_lambda_handler
def lambda_handler(event, context):
return app.resolve(event, context)
```
"""
return self.route(
rule,
"HEAD",
cors,
compress,
cache_control,
summary,
description,
responses,
response_description,
tags,
operation_id,
include_in_schema,
security,
middlewares,
)

def _push_processed_stack_frame(self, frame: str):
"""
Add Current Middleware to the Middleware Stack Frames
Expand Down
20 changes: 10 additions & 10 deletions docs/core/event_handler/api_gateway.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ You can use named decorators to specify the HTTP method that should be handled i
--8<-- "examples/event_handler_rest/src/http_methods.json"
```

If you need to accept multiple HTTP methods in a single function, you can use the `route` method and pass a list of HTTP methods.
If you need to accept multiple HTTP methods in a single function, or support a HTTP method for which no decorator exists (e.g. [TRACE](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/TRACE)), you can use the `route` method and pass a list of HTTP methods.

```python hl_lines="15" title="Handling multiple HTTP Methods"
--8<-- "examples/event_handler_rest/src/http_methods_multiple.py"
Expand Down Expand Up @@ -524,12 +524,12 @@ Behind the scenes, the [data validation](#data-validation) feature auto-generate

There are some important **caveats** that you should know before enabling it:

| Caveat | Description |
| ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Swagger UI is **publicly accessible by default** | When using `enable_swagger` method, you can [protect sensitive API endpoints by implementing a custom middleware](#customizing-swagger-ui) using your preferred authorization mechanism. |
| **No micro-functions support** yet | Swagger UI is enabled on a per resolver instance which will limit its accuracy here. |
| You need to expose a **new route** | You'll need to expose the following path to Lambda: `/swagger`; ignore if you're routing this path already. |
| JS and CSS files are **embedded within Swagger HTML** | If you are not using an external CDN to serve Swagger UI assets, we embed JS and CSS directly into the HTML. To enhance performance, please consider enabling the `compress` option to minimize the size of HTTP requests. |
| Caveat | Description |
| ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Swagger UI is **publicly accessible by default** | When using `enable_swagger` method, you can [protect sensitive API endpoints by implementing a custom middleware](#customizing-swagger-ui) using your preferred authorization mechanism. |
| **No micro-functions support** yet | Swagger UI is enabled on a per resolver instance which will limit its accuracy here. |
| You need to expose a **new route** | You'll need to expose the following path to Lambda: `/swagger`; ignore if you're routing this path already. |
| JS and CSS files are **embedded within Swagger HTML** | If you are not using an external CDN to serve Swagger UI assets, we embed JS and CSS directly into the HTML. To enhance performance, please consider enabling the `compress` option to minimize the size of HTTP requests. |

```python hl_lines="12-13" title="enabling_swagger.py"
--8<-- "examples/event_handler_rest/src/enabling_swagger.py"
Expand Down Expand Up @@ -835,8 +835,8 @@ As a practical example, let's refactor our correlation ID middleware so it accep

These are native middlewares that may become native features depending on customer demand.

| Middleware | Purpose |
| ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| Middleware | Purpose |
| ------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| [SchemaValidationMiddleware](/lambda/python/latest/api/event_handler/middlewares/schema_validation.html){target="_blank"} | Validates API request body and response against JSON Schema, using [Validation utility](../../utilities/validation.md){target="_blank"} |

#### Being a good citizen
Expand Down Expand Up @@ -1053,7 +1053,7 @@ When you're describing your API, declare security schemes at the top level, and
OpenAPI 3 lets you describe APIs protected using the following security schemes:

| Security Scheme | Type | Description |
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [HTTP auth](https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml){target="_blank"} | `HTTPBase` | HTTP authentication schemes using the Authorization header (e.g: [Basic auth](https://swagger.io/docs/specification/authentication/basic-authentication/){target="_blank"}, [Bearer](https://swagger.io/docs/specification/authentication/bearer-authentication/){target="_blank"}) |
| [API keys](https://swagger.io/docs/specification/authentication/api-keys/https://swagger.io/docs/specification/authentication/api-keys/){target="_blank"} (e.g: query strings, cookies) | `APIKey` | API keys in headers, query strings or [cookies](https://swagger.io/docs/specification/authentication/cookie-authentication/){target="_blank"}. |
| [OAuth 2](https://swagger.io/docs/specification/authentication/oauth2/){target="_blank"} | `OAuth2` | Authorization protocol that gives an API client limited access to user data on a web server. |
Expand Down
8 changes: 7 additions & 1 deletion tests/functional/event_handler/test_api_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,12 +291,16 @@ def delete_func():
def patch_func():
raise RuntimeError()

@app.head("/no_matching_head")
def head_func():
raise RuntimeError()

def handler(event, context):
return app.resolve(event, context)

# Also check the route configurations
routes = app._static_routes
assert len(routes) == 5
assert len(routes) == 6
for route in routes:
if route.func == get_func:
assert route.method == "GET"
Expand All @@ -308,6 +312,8 @@ def handler(event, context):
assert route.method == "DELETE"
elif route.func == patch_func:
assert route.method == "PATCH"
elif route.func == head_func:
assert route.method == "HEAD"

# WHEN calling the handler
# THEN return a 404
Expand Down