Skip to content
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

feat(event_handler): add support to VPC Lattice payload v2 #3153

Merged
merged 32 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
1cf5073
updated vpc lattice v2 event
stephenbawks Oct 1, 2023
7530760
updating base
stephenbawks Oct 1, 2023
93cd96e
remove line break
stephenbawks Oct 1, 2023
b1f9e96
more details return type for request context
stephenbawks Oct 1, 2023
c1dbf1b
couple doc string updates
stephenbawks Oct 1, 2023
680c9be
copy pasta
stephenbawks Oct 1, 2023
7f25990
Merge branch 'develop' into vpcLatticeV2
leandrodamascena Oct 2, 2023
3d996ac
ruff work
stephenbawks Oct 2, 2023
d278441
Merge branch 'develop' into vpcLatticeV2
leandrodamascena Oct 2, 2023
0c48df9
small tweak
stephenbawks Oct 2, 2023
27e3761
adding model 2 tests
stephenbawks Oct 3, 2023
86a7ff5
starting docs
stephenbawks Oct 3, 2023
687f45e
couple more tests
stephenbawks Oct 3, 2023
5e47060
words matter
stephenbawks Oct 3, 2023
32463a1
missing line break
stephenbawks Oct 3, 2023
77ee153
ruff do work
stephenbawks Oct 3, 2023
2dc9b0d
Adding more fields in parser
leandrodamascena Oct 3, 2023
fb07a96
pull and add examples
stephenbawks Oct 3, 2023
47c12d4
where did you go
stephenbawks Oct 3, 2023
54f9363
Adding more fields in parser and event source
leandrodamascena Oct 3, 2023
d9c702b
Convert microsecond to milisecond - need to review
leandrodamascena Oct 3, 2023
54d6a2f
additional tests
stephenbawks Oct 4, 2023
0c5cf99
fix path
stephenbawks Oct 4, 2023
2b50d13
Wording
leandrodamascena Oct 4, 2023
46c0009
Adding VPC Lattice v2 resolver
leandrodamascena Oct 4, 2023
157556f
Fixing resolver
leandrodamascena Oct 4, 2023
5c56a43
Fixing mypy
leandrodamascena Oct 4, 2023
072987e
Merge branch 'develop' into vpcLatticeV2
leandrodamascena Oct 4, 2023
144df4b
Event Handler documentation
leandrodamascena Oct 4, 2023
ce1f307
Preserving timeEpoch field + adding a new one
leandrodamascena Oct 4, 2023
8bd48c1
Merge branch 'develop' into vpcLatticeV2
leandrodamascena Oct 5, 2023
f9a3ed0
Merge branch 'develop' into vpcLatticeV2
leandrodamascena Oct 5, 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
3 changes: 2 additions & 1 deletion aws_lambda_powertools/event_handler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from aws_lambda_powertools.event_handler.lambda_function_url import (
LambdaFunctionUrlResolver,
)
from aws_lambda_powertools.event_handler.vpc_lattice import VPCLatticeResolver
from aws_lambda_powertools.event_handler.vpc_lattice import VPCLatticeResolver, VPCLatticeV2Resolver

__all__ = [
"AppSyncResolver",
Expand All @@ -26,4 +26,5 @@
"LambdaFunctionUrlResolver",
"Response",
"VPCLatticeResolver",
"VPCLatticeV2Resolver",
]
5 changes: 5 additions & 0 deletions aws_lambda_powertools/event_handler/api_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
APIGatewayProxyEventV2,
LambdaFunctionUrlEvent,
VPCLatticeEvent,
VPCLatticeEventV2,
)
from aws_lambda_powertools.utilities.data_classes.common import BaseProxyEvent
from aws_lambda_powertools.utilities.typing import LambdaContext
Expand All @@ -43,6 +44,7 @@ class ProxyEventType(Enum):
APIGatewayProxyEventV2 = "APIGatewayProxyEventV2"
ALBEvent = "ALBEvent"
VPCLatticeEvent = "VPCLatticeEvent"
VPCLatticeEventV2 = "VPCLatticeEventV2"
LambdaFunctionUrlEvent = "LambdaFunctionUrlEvent"


Expand Down Expand Up @@ -999,6 +1001,9 @@ def _to_proxy_event(self, event: Dict) -> BaseProxyEvent:
if self._proxy_type == ProxyEventType.VPCLatticeEvent:
logger.debug("Converting event to VPC Lattice contract")
return VPCLatticeEvent(event)
if self._proxy_type == ProxyEventType.VPCLatticeEventV2:
logger.debug("Converting event to VPC LatticeV2 contract")
return VPCLatticeEventV2(event)
logger.debug("Converting event to ALB contract")
return ALBEvent(event)

Expand Down
47 changes: 46 additions & 1 deletion aws_lambda_powertools/event_handler/vpc_lattice.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
ApiGatewayResolver,
ProxyEventType,
)
from aws_lambda_powertools.utilities.data_classes import VPCLatticeEvent
from aws_lambda_powertools.utilities.data_classes import VPCLatticeEvent, VPCLatticeEventV2


class VPCLatticeResolver(ApiGatewayResolver):
Expand Down Expand Up @@ -51,3 +51,48 @@ def __init__(
):
"""Amazon VPC Lattice resolver"""
super().__init__(ProxyEventType.VPCLatticeEvent, cors, debug, serializer, strip_prefixes)


class VPCLatticeV2Resolver(ApiGatewayResolver):
"""VPC Lattice resolver

Documentation:
- https://docs.aws.amazon.com/lambda/latest/dg/services-vpc-lattice.html
- https://docs.aws.amazon.com/lambda/latest/dg/services-vpc-lattice.html#vpc-lattice-receiving-events

Examples
--------
Simple example integrating with Tracer

```python
from aws_lambda_powertools import Tracer
from aws_lambda_powertools.event_handler import VPCLatticeV2Resolver

tracer = Tracer()
app = VPCLatticeV2Resolver()

@app.get("/get-call")
def simple_get():
return {"message": "Foo"}

@app.post("/post-call")
def simple_post():
post_data: dict = app.current_event.json_body
return {"message": post_data}

@tracer.capture_lambda_handler
def lambda_handler(event, context):
return app.resolve(event, context)
"""

current_event: VPCLatticeEventV2

def __init__(
self,
cors: Optional[CORSConfig] = None,
debug: Optional[bool] = None,
serializer: Optional[Callable[[Dict], str]] = None,
strip_prefixes: Optional[List[Union[str, Pattern]]] = None,
):
"""Amazon VPC Lattice resolver"""
super().__init__(ProxyEventType.VPCLatticeEventV2, cors, debug, serializer, strip_prefixes)
3 changes: 2 additions & 1 deletion aws_lambda_powertools/utilities/data_classes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from .ses_event import SESEvent
from .sns_event import SNSEvent
from .sqs_event import SQSEvent
from .vpc_lattice import VPCLatticeEvent
from .vpc_lattice import VPCLatticeEvent, VPCLatticeEventV2

__all__ = [
"APIGatewayProxyEvent",
Expand Down Expand Up @@ -56,4 +56,5 @@
"event_source",
"AWSConfigRuleEvent",
"VPCLatticeEvent",
"VPCLatticeEventV2",
]
161 changes: 136 additions & 25 deletions aws_lambda_powertools/utilities/data_classes/vpc_lattice.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
BaseHeadersSerializer,
HttpApiHeadersSerializer,
)
from aws_lambda_powertools.utilities.data_classes.common import BaseProxyEvent
from aws_lambda_powertools.utilities.data_classes.common import BaseProxyEvent, DictWrapper
from aws_lambda_powertools.utilities.data_classes.shared_functions import (
base64_decode,
get_header_value,
get_query_string_value,
)


class VPCLatticeEvent(BaseProxyEvent):
class VPCLatticeEventBase(BaseProxyEvent):
stephenbawks marked this conversation as resolved.
Show resolved Hide resolved
leandrodamascena marked this conversation as resolved.
Show resolved Hide resolved
@property
def body(self) -> str:
"""The VPC Lattice body."""
Expand All @@ -30,11 +30,6 @@ 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 decoded_body(self) -> str:
"""Dynamically base64 decode body as a str"""
Expand All @@ -48,24 +43,6 @@ 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"]

# VPCLattice event has no path field
# Added here for consistency with the BaseProxyEvent class
@property
def path(self) -> str:
return self["raw_path"]

# VPCLattice event has no http_method field
# Added here for consistency with the BaseProxyEvent class
@property
def http_method(self) -> str:
"""The HTTP method used. Valid values include: DELETE, GET, HEAD, OPTIONS, PATCH, POST, and PUT."""
Expand Down Expand Up @@ -140,3 +117,137 @@ def get_header_value(
def header_serializer(self) -> BaseHeadersSerializer:
# When using the VPC Lattice integration, we have multiple HTTP Headers.
return HttpApiHeadersSerializer()


class VPCLatticeEvent(VPCLatticeEventBase):
@property
def raw_path(self) -> str:
"""The raw VPC Lattice request path."""
return self["raw_path"]

@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"]

# VPCLattice event has no path field
# Added here for consistency with the BaseProxyEvent class
@property
def path(self) -> str:
return self["raw_path"]

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


class vpcLatticeEventV2Identity(DictWrapper):
stephenbawks marked this conversation as resolved.
Show resolved Hide resolved
@property
def source_vpc_arn(self) -> Optional[str]:
"""The VPC Lattice v2 Event requestContext Identity sourceVpcArn"""
return self.get("sourceVpcArn")

@property
def get_type(self) -> Optional[str]:
"""The VPC Lattice v2 Event requestContext Identity type"""
return self.get("type")

@property
def principal(self) -> Optional[str]:
"""The VPC Lattice v2 Event requestContext principal"""
return self.get("principal")

@property
def principal_org_id(self) -> Optional[str]:
"""The VPC Lattice v2 Event requestContext principalOrgID"""
return self.get("principalOrgID")

@property
def session_name(self) -> Optional[str]:
"""The VPC Lattice v2 Event requestContext sessionName"""
return self.get("sessionName")

@property
def x509_subject_cn(self) -> Optional[str]:
"""The VPC Lattice v2 Event requestContext X509SubjectCn"""
return self.get("X509SubjectCn")

@property
def x509_issuer_ou(self) -> Optional[str]:
"""The VPC Lattice v2 Event requestContext X509IssuerOu"""
return self.get("X509IssuerOu")

@property
def x509_san_dns(self) -> Optional[str]:
"""The VPC Lattice v2 Event requestContext X509SanDns"""
return self.get("x509SanDns")

@property
def x509_san_uri(self) -> Optional[str]:
"""The VPC Lattice v2 Event requestContext X509SanUri"""
return self.get("X509SanUri")

@property
def x509_san_name_cn(self) -> Optional[str]:
"""The VPC Lattice v2 Event requestContext X509SanNameCn"""
return self.get("X509SanNameCn")


class vpcLatticeEventV2RequestContext(DictWrapper):
@property
def service_network_arn(self) -> str:
"""The VPC Lattice v2 Event requestContext serviceNetworkArn"""
return self["serviceNetworkArn"]

@property
def service_arn(self) -> str:
"""The VPC Lattice v2 Event requestContext serviceArn"""
return self["serviceArn"]

@property
def target_group_arn(self) -> str:
"""The VPC Lattice v2 Event requestContext targetGroupArn"""
return self["targetGroupArn"]

@property
def identity(self) -> vpcLatticeEventV2Identity:
"""The VPC Lattice v2 Event requestContext identity"""
return vpcLatticeEventV2Identity(self["identity"])

@property
def region(self) -> str:
"""The VPC Lattice v2 Event requestContext serviceNetworkArn"""
return self["region"]

@property
def time_epoch(self) -> float:
"""The VPC Lattice v2 Event requestContext timeEpoch"""
return self["timeEpoch"]
heitorlessa marked this conversation as resolved.
Show resolved Hide resolved


class VPCLatticeEventV2(VPCLatticeEventBase):
@property
def version(self) -> str:
"""The VPC Lattice v2 Event version"""
return self["version"]

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

@property
def path(self) -> str:
"""The VPC Lattice v2 Event path"""
return self["path"]

@property
def request_context(self) -> vpcLatticeEventV2RequestContext:
"""he VPC Lattice v2 Event request context."""
return vpcLatticeEventV2RequestContext(self["requestContext"])
stephenbawks marked this conversation as resolved.
Show resolved Hide resolved

@property
def query_string_parameters(self) -> Optional[Dict[str, str]]:
"""The request query string parameters."""
return self.get("queryStringParameters")
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from .sns import SnsEnvelope, SnsSqsEnvelope
from .sqs import SqsEnvelope
from .vpc_lattice import VpcLatticeEnvelope
from .vpc_latticev2 import VpcLatticeV2Envelope

__all__ = [
"ApiGatewayEnvelope",
Expand All @@ -27,4 +28,5 @@
"KafkaEnvelope",
"BaseEnvelope",
"VpcLatticeEnvelope",
"VpcLatticeV2Envelope",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import logging
from typing import Any, Dict, Optional, Type, Union

from ..models import VpcLatticeV2Model
from ..types import Model
from .base import BaseEnvelope

logger = logging.getLogger(__name__)


class VpcLatticeV2Envelope(BaseEnvelope):
"""Amazon VPC Lattice envelope to extract data within body key"""

def parse(self, data: Optional[Union[Dict[str, Any], Any]], model: Type[Model]) -> Optional[Model]:
"""Parses data found with model provided

Parameters
----------
data : Dict
Lambda event to be parsed
model : Type[Model]
Data model provided to parse after extracting data using envelope

Returns
-------
Optional[Model]
Parsed detail payload with model provided
"""
logger.debug(f"Parsing incoming data with VPC Lattice V2 model {VpcLatticeV2Model}")
parsed_envelope: VpcLatticeV2Model = VpcLatticeV2Model.parse_obj(data)
logger.debug(f"Parsing event payload in `detail` with {model}")
return self._parse(data=parsed_envelope.body, model=model)
2 changes: 2 additions & 0 deletions aws_lambda_powertools/utilities/parser/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
from .sns import SnsModel, SnsNotificationModel, SnsRecordModel
from .sqs import SqsAttributesModel, SqsModel, SqsMsgAttributeModel, SqsRecordModel
from .vpc_lattice import VpcLatticeModel
from .vpc_latticev2 import VpcLatticeV2Model

__all__ = [
"APIGatewayProxyEventV2Model",
Expand Down Expand Up @@ -163,4 +164,5 @@
"CloudFormationCustomResourceCreateModel",
"CloudFormationCustomResourceBaseModel",
"VpcLatticeModel",
"VpcLatticeV2Model",
]
Loading