Skip to content

feat(event_sources): add support for VPC Lattice events #2358

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 17 commits into from
Jun 8, 2023
Merged
Show file tree
Hide file tree
Changes from 9 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
2 changes: 2 additions & 0 deletions aws_lambda_powertools/utilities/data_classes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from .ses_event import SESEvent
from .sns_event import SNSEvent
from .sqs_event import SQSEvent
from .vpc_lattice import VPCLatticeEvent

__all__ = [
"APIGatewayProxyEvent",
Expand All @@ -42,4 +43,5 @@
"SNSEvent",
"SQSEvent",
"event_source",
"VPCLatticeEvent",
]
36 changes: 36 additions & 0 deletions aws_lambda_powertools/utilities/data_classes/vpc_lattice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from typing import Dict

from aws_lambda_powertools.utilities.data_classes.common import (
DictWrapper,
)

class VPCLatticeEvent(DictWrapper):
@property
def body(self) -> str:
"""The VPC Lattice body."""
return self["body"]

@property
def headers(self) -> Dict[str, str]:
"""The VPC Lattice event headers."""
return self["headers"]

@property
def is_base64_encoded(self) -> bool:
"""A boolean flag to indicate if the applicable request payload is Base64-encode"""
return self["is_base64_encoded"]

@property
def method(self) -> str:
"""The VPC Lattice method used. Valid values include: DELETE, GET, HEAD, OPTIONS, PATCH, POST, and PUT."""
return self["method"]

@property
def query_string_parameters(self) -> Dict[str, str]:
"""The request query string parameters."""
return self["query_string_parameters"]

@property
def raw_path(self) -> str:
"""The raw VPC Lattice request path."""
return self["raw_path"]
66 changes: 66 additions & 0 deletions docs/utilities/data_classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ Log Data Event for Troubleshooting
| [SES](#ses) | `SESEvent` |
| [SNS](#sns) | `SNSEvent` |
| [SQS](#sqs) | `SQSEvent` |
| [VPC Lattice](#vpc-lattice) | `VPCLatticeEvent` |

???+ info
The examples provided below are far from exhaustive - the data classes themselves are designed to provide a form of
Expand Down Expand Up @@ -1121,6 +1122,71 @@ This example is based on the AWS Blog post [Introducing Amazon S3 Object Lambda
do_something_with(record.body)
```

### VPC Lattice

The VPC Lattice service supports Lambda invocation for requests over both HTTP and HTTPS. The service sends an event in JSON format, and adds the `X-Forwarded-For` header to every request. The service also adds the X-Forwarded-Proto header to requests over HTTPS.

The service Base64 encodes the body and sets isBase64Encoded to true, if the content-encoding header is present, and the content type is not one of the following:

* `text/*`
* `application/json`
* `application/xml`
* `application/javascript`

If the content-encoding header is not present, Base64 encoding depends on the content type. For the content types, `text/*`, `application/json`, `application/xml`, and `application/javascript`, the service sends the body as is and sets `is_base64_encoded` to `false`.

=== "app.py"

```python
from aws_lambda_powertools.utilities.data_classes import event_source, VPCLatticeEvent

@event_source(data_class=VPCLatticeEvent)
def lambda_handler(event: VPCLatticeEvent, context):
do_something_with(event.body)

```

=== "Lattice Example Event"

```json
{
"raw_path": "/testpath",
"method": "GET",
"headers": {
"user_agent": "curl/7.64.1",
"x-forwarded-for": "10.213.229.10",
"host": "test-lambda-service-3908sdf9u3u.dkfjd93.vpc-lattice-svcs.us-east-2.on.aws",
"accept": "*/*"
},
"query_string_parameters": {
"key": "value"
},
"body": "...",
"is_base64_encoded": false
}
```

=== "Lattice Return Object"

```python
response = {
"isBase64Encoded": False,
"statusCode": 200,
headers: {
"Content-Type": "application/text"
},
body: "Event Response to VPC Lattice 🔥🚀🔥"
}

return response
```

The response from your Lambda function must include the Base64 encoding status, status code, and headers. You can omit the body.

To include a binary content in the body of the response, you must Base64 encode the content and set `isBase64Encoded` to `True`. The service decodes the content to retrieve the binary content and sends it to the client in the body of the HTTP response.

The VPC Lattice service does not honor hop-by-hop headers, such as `Connection` or `Transfer-Encoding`. You can omit the `Content-Length` header because the service computes it before sending responses to clients.

## Advanced

### Debugging
Expand Down
13 changes: 13 additions & 0 deletions tests/events/vpcLatticeEvent.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"raw_path": "/testpath",
"method": "GET",
"headers": {
"user_agent": "curl/7.64.1",
"x-forwarded-for": "10.213.229.10",
"host": "test-lambda-service-3908sdf9u3u.dkfjd93.vpc-lattice-svcs.us-east-2.on.aws",
"accept": "*/*"
},
"query_string_parameters": {},
"body": "",
"is_base64_encoded": false
}
12 changes: 12 additions & 0 deletions tests/functional/test_data_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
SESEvent,
SNSEvent,
SQSEvent,
VPCLatticeEvent
)
from aws_lambda_powertools.utilities.data_classes.api_gateway_authorizer_event import (
APIGatewayAuthorizerEventV2,
Expand Down Expand Up @@ -2041,3 +2042,14 @@ def test_api_gateway_route_arn_parser():
details = parse_api_gateway_arn(arn)
assert details.resource == ""
assert details.arn == arn + "/"


def test_vpc_lattice_event():
event = VPCLatticeEvent(load_event("vpcLatticeEvent.json"))

assert event.raw_path == event["raw_path"]
assert event.method == event["method"]
assert event.headers == event["headers"]
assert event.query_string_parameters == event["query_string_parameters"]
assert event.body == event["body"]
assert event.is_base64_encoded == event["is_base64_encoded"]