Skip to content

Commit a2bfbe2

Browse files
Merge branch 'develop' into dependabot/pip/develop/ruff-0.8.0
2 parents 8a7e892 + 20c0b74 commit a2bfbe2

File tree

12 files changed

+385
-31
lines changed

12 files changed

+385
-31
lines changed

CHANGELOG.md

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,35 +24,42 @@
2424

2525
## Maintenance
2626

27-
* **ci:** new pre-release 3.3.1a3 ([#5598](https://github.com/aws-powertools/powertools-lambda-python/issues/5598))
27+
* **ci:** new pre-release 3.3.1a4 ([#5612](https://github.com/aws-powertools/powertools-lambda-python/issues/5612))
2828
* **ci:** new pre-release 3.3.1a0 ([#5565](https://github.com/aws-powertools/powertools-lambda-python/issues/5565))
29-
* **ci:** new pre-release 3.3.1a1 ([#5577](https://github.com/aws-powertools/powertools-lambda-python/issues/5577))
3029
* **ci:** new pre-release 3.3.1a2 ([#5585](https://github.com/aws-powertools/powertools-lambda-python/issues/5585))
31-
* **ci:** new pre-release 3.3.1a4 ([#5612](https://github.com/aws-powertools/powertools-lambda-python/issues/5612))
32-
* **deps:** bump squidfunk/mkdocs-material from `ce587cb` to `ef0b45e` in /docs ([#5603](https://github.com/aws-powertools/powertools-lambda-python/issues/5603))
30+
* **ci:** remove poetry cache in quality check pipeline ([#5626](https://github.com/aws-powertools/powertools-lambda-python/issues/5626))
31+
* **ci:** new pre-release 3.3.1a1 ([#5577](https://github.com/aws-powertools/powertools-lambda-python/issues/5577))
32+
* **ci:** new pre-release 3.3.1a3 ([#5598](https://github.com/aws-powertools/powertools-lambda-python/issues/5598))
3333
* **deps:** bump codecov/codecov-action from 5.0.3 to 5.0.7 ([#5617](https://github.com/aws-powertools/powertools-lambda-python/issues/5617))
34+
* **deps:** bump datadog-lambda from 6.101.0 to 6.102.0 ([#5570](https://github.com/aws-powertools/powertools-lambda-python/issues/5570))
35+
* **deps:** bump actions/dependency-review-action from 4.4.0 to 4.5.0 ([#5616](https://github.com/aws-powertools/powertools-lambda-python/issues/5616))
36+
* **deps:** bump aws-encryption-sdk from 3.3.0 to 4.0.0 ([#5564](https://github.com/aws-powertools/powertools-lambda-python/issues/5564))
37+
* **deps:** bump squidfunk/mkdocs-material from `ce587cb` to `ef0b45e` in /docs ([#5603](https://github.com/aws-powertools/powertools-lambda-python/issues/5603))
38+
* **deps:** bump aws-actions/closed-issue-message from 80edfc24bdf1283400eb04d20a8a605ae8bf7d48 to 37548691e7cc75ba58f85c9f873f9eee43590449 ([#5606](https://github.com/aws-powertools/powertools-lambda-python/issues/5606))
39+
* **deps:** bump pydantic from 2.10.0 to 2.10.1 ([#5632](https://github.com/aws-powertools/powertools-lambda-python/issues/5632))
3440
* **deps:** bump pydantic from 2.9.2 to 2.10.0 ([#5611](https://github.com/aws-powertools/powertools-lambda-python/issues/5611))
41+
* **deps:** bump datadog-lambda from 6.102.0 to 6.104.0 ([#5631](https://github.com/aws-powertools/powertools-lambda-python/issues/5631))
3542
* **deps:** bump codecov/codecov-action from 4.6.0 to 5.0.2 ([#5567](https://github.com/aws-powertools/powertools-lambda-python/issues/5567))
36-
* **deps:** bump aws-encryption-sdk from 3.3.0 to 4.0.0 ([#5564](https://github.com/aws-powertools/powertools-lambda-python/issues/5564))
3743
* **deps:** bump codecov/codecov-action from 5.0.2 to 5.0.3 ([#5592](https://github.com/aws-powertools/powertools-lambda-python/issues/5592))
38-
* **deps:** bump actions/dependency-review-action from 4.4.0 to 4.5.0 ([#5616](https://github.com/aws-powertools/powertools-lambda-python/issues/5616))
39-
* **deps:** bump datadog-lambda from 6.101.0 to 6.102.0 ([#5570](https://github.com/aws-powertools/powertools-lambda-python/issues/5570))
40-
* **deps:** bump aws-actions/closed-issue-message from 80edfc24bdf1283400eb04d20a8a605ae8bf7d48 to 37548691e7cc75ba58f85c9f873f9eee43590449 ([#5606](https://github.com/aws-powertools/powertools-lambda-python/issues/5606))
41-
* **deps-dev:** bump aws-cdk from 2.167.2 to 2.169.0 ([#5618](https://github.com/aws-powertools/powertools-lambda-python/issues/5618))
42-
* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.167.0a0 to 2.167.1a0 ([#5583](https://github.com/aws-powertools/powertools-lambda-python/issues/5583))
43-
* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.282 to 0.1.284 ([#5607](https://github.com/aws-powertools/powertools-lambda-python/issues/5607))
44-
* **deps-dev:** bump aws-cdk from 2.167.1 to 2.167.2 ([#5593](https://github.com/aws-powertools/powertools-lambda-python/issues/5593))
4544
* **deps-dev:** bump cfn-lint from 1.19.0 to 1.20.0 ([#5595](https://github.com/aws-powertools/powertools-lambda-python/issues/5595))
46-
* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.281 to 0.1.282 ([#5594](https://github.com/aws-powertools/powertools-lambda-python/issues/5594))
47-
* **deps-dev:** bump boto3-stubs from 1.35.60 to 1.35.63 ([#5581](https://github.com/aws-powertools/powertools-lambda-python/issues/5581))
4845
* **deps-dev:** bump aws-cdk-lib from 2.167.1 to 2.167.2 ([#5596](https://github.com/aws-powertools/powertools-lambda-python/issues/5596))
49-
* **deps-dev:** bump aws-cdk from 2.167.0 to 2.167.1 ([#5572](https://github.com/aws-powertools/powertools-lambda-python/issues/5572))
46+
* **deps-dev:** bump aws-cdk from 2.167.1 to 2.167.2 ([#5593](https://github.com/aws-powertools/powertools-lambda-python/issues/5593))
47+
* **deps-dev:** bump boto3-stubs from 1.35.63 to 1.35.64 ([#5582](https://github.com/aws-powertools/powertools-lambda-python/issues/5582))
48+
* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.281 to 0.1.282 ([#5594](https://github.com/aws-powertools/powertools-lambda-python/issues/5594))
49+
* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.167.0a0 to 2.167.1a0 ([#5583](https://github.com/aws-powertools/powertools-lambda-python/issues/5583))
5050
* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.167.1a0 to 2.167.2a0 ([#5619](https://github.com/aws-powertools/powertools-lambda-python/issues/5619))
51+
* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.282 to 0.1.284 ([#5607](https://github.com/aws-powertools/powertools-lambda-python/issues/5607))
52+
* **deps-dev:** bump aws-cdk from 2.167.2 to 2.169.0 ([#5618](https://github.com/aws-powertools/powertools-lambda-python/issues/5618))
5153
* **deps-dev:** bump ruff from 0.7.3 to 0.7.4 ([#5569](https://github.com/aws-powertools/powertools-lambda-python/issues/5569))
52-
* **deps-dev:** bump aws-cdk-lib from 2.167.0 to 2.167.1 ([#5568](https://github.com/aws-powertools/powertools-lambda-python/issues/5568))
53-
* **deps-dev:** bump boto3-stubs from 1.35.64 to 1.35.67 ([#5621](https://github.com/aws-powertools/powertools-lambda-python/issues/5621))
54+
* **deps-dev:** bump boto3-stubs from 1.35.60 to 1.35.63 ([#5581](https://github.com/aws-powertools/powertools-lambda-python/issues/5581))
5455
* **deps-dev:** bump aws-cdk-lib from 2.167.2 to 2.170.0 ([#5622](https://github.com/aws-powertools/powertools-lambda-python/issues/5622))
55-
* **deps-dev:** bump boto3-stubs from 1.35.63 to 1.35.64 ([#5582](https://github.com/aws-powertools/powertools-lambda-python/issues/5582))
56+
* **deps-dev:** bump aws-cdk from 2.167.0 to 2.167.1 ([#5572](https://github.com/aws-powertools/powertools-lambda-python/issues/5572))
57+
* **deps-dev:** bump boto3-stubs from 1.35.64 to 1.35.67 ([#5621](https://github.com/aws-powertools/powertools-lambda-python/issues/5621))
58+
* **deps-dev:** bump mkdocs-material from 9.5.44 to 9.5.45 ([#5610](https://github.com/aws-powertools/powertools-lambda-python/issues/5610))
59+
* **deps-dev:** bump aws-cdk-lib from 2.167.0 to 2.167.1 ([#5568](https://github.com/aws-powertools/powertools-lambda-python/issues/5568))
60+
* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.167.2a0 to 2.170.0a0 ([#5629](https://github.com/aws-powertools/powertools-lambda-python/issues/5629))
61+
* **deps-dev:** bump aws-cdk from 2.169.0 to 2.170.0 ([#5628](https://github.com/aws-powertools/powertools-lambda-python/issues/5628))
62+
* **deps-dev:** bump sentry-sdk from 2.18.0 to 2.19.0 ([#5633](https://github.com/aws-powertools/powertools-lambda-python/issues/5633))
5663
* **layers:** balance Python 3.13 layers in GovCloud partition ([#5579](https://github.com/aws-powertools/powertools-lambda-python/issues/5579))
5764

5865

aws_lambda_powertools/event_handler/api_gateway.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
TypeModelOrEnum,
8484
)
8585
from aws_lambda_powertools.shared.cookies import Cookie
86+
from aws_lambda_powertools.shared.types import AnyCallableT
8687
from aws_lambda_powertools.utilities.typing import LambdaContext
8788

8889

@@ -924,7 +925,7 @@ def route(
924925
security: list[dict[str, list[str]]] | None = None,
925926
openapi_extensions: dict[str, Any] | None = None,
926927
middlewares: list[Callable[..., Any]] | None = None,
927-
):
928+
) -> Callable[[AnyCallableT], AnyCallableT]:
928929
raise NotImplementedError()
929930

930931
def use(self, middlewares: list[Callable[..., Response]]) -> None:
@@ -984,7 +985,7 @@ def get(
984985
security: list[dict[str, list[str]]] | None = None,
985986
openapi_extensions: dict[str, Any] | None = None,
986987
middlewares: list[Callable[..., Any]] | None = None,
987-
):
988+
) -> Callable[[AnyCallableT], AnyCallableT]:
988989
"""Get route decorator with GET `method`
989990
990991
Examples
@@ -1041,7 +1042,7 @@ def post(
10411042
security: list[dict[str, list[str]]] | None = None,
10421043
openapi_extensions: dict[str, Any] | None = None,
10431044
middlewares: list[Callable[..., Any]] | None = None,
1044-
):
1045+
) -> Callable[[AnyCallableT], AnyCallableT]:
10451046
"""Post route decorator with POST `method`
10461047
10471048
Examples
@@ -1099,7 +1100,7 @@ def put(
10991100
security: list[dict[str, list[str]]] | None = None,
11001101
openapi_extensions: dict[str, Any] | None = None,
11011102
middlewares: list[Callable[..., Any]] | None = None,
1102-
):
1103+
) -> Callable[[AnyCallableT], AnyCallableT]:
11031104
"""Put route decorator with PUT `method`
11041105
11051106
Examples
@@ -1157,7 +1158,7 @@ def delete(
11571158
security: list[dict[str, list[str]]] | None = None,
11581159
openapi_extensions: dict[str, Any] | None = None,
11591160
middlewares: list[Callable[..., Any]] | None = None,
1160-
):
1161+
) -> Callable[[AnyCallableT], AnyCallableT]:
11611162
"""Delete route decorator with DELETE `method`
11621163
11631164
Examples
@@ -1214,7 +1215,7 @@ def patch(
12141215
security: list[dict[str, list[str]]] | None = None,
12151216
openapi_extensions: dict[str, Any] | None = None,
12161217
middlewares: list[Callable] | None = None,
1217-
):
1218+
) -> Callable[[AnyCallableT], AnyCallableT]:
12181219
"""Patch route decorator with PATCH `method`
12191220
12201221
Examples
@@ -1274,7 +1275,7 @@ def head(
12741275
security: list[dict[str, list[str]]] | None = None,
12751276
openapi_extensions: dict[str, Any] | None = None,
12761277
middlewares: list[Callable] | None = None,
1277-
):
1278+
) -> Callable[[AnyCallableT], AnyCallableT]:
12781279
"""Head route decorator with HEAD `method`
12791280
12801281
Examples
@@ -1950,10 +1951,10 @@ def route(
19501951
security: list[dict[str, list[str]]] | None = None,
19511952
openapi_extensions: dict[str, Any] | None = None,
19521953
middlewares: list[Callable[..., Any]] | None = None,
1953-
):
1954+
) -> Callable[[AnyCallableT], AnyCallableT]:
19541955
"""Route decorator includes parameter `method`"""
19551956

1956-
def register_resolver(func: Callable):
1957+
def register_resolver(func: AnyCallableT) -> AnyCallableT:
19571958
methods = (method,) if isinstance(method, str) else method
19581959
logger.debug(f"Adding route using rule {rule} and methods: {','.join(m.upper() for m in methods)}")
19591960

@@ -2492,8 +2493,8 @@ def route(
24922493
security: list[dict[str, list[str]]] | None = None,
24932494
openapi_extensions: dict[str, Any] | None = None,
24942495
middlewares: list[Callable[..., Any]] | None = None,
2495-
):
2496-
def register_route(func: Callable):
2496+
) -> Callable[[AnyCallableT], AnyCallableT]:
2497+
def register_route(func: AnyCallableT) -> AnyCallableT:
24972498
# All dict keys needs to be hashable. So we'll need to do some conversions:
24982499
methods = (method,) if isinstance(method, str) else tuple(method)
24992500
frozen_responses = _FrozenDict(responses) if responses else None
@@ -2598,7 +2599,7 @@ def route(
25982599
security: list[dict[str, list[str]]] | None = None,
25992600
openapi_extensions: dict[str, Any] | None = None,
26002601
middlewares: list[Callable[..., Any]] | None = None,
2601-
):
2602+
) -> Callable[[AnyCallableT], AnyCallableT]:
26022603
# NOTE: see #1552 for more context.
26032604
return super().route(
26042605
rule.rstrip("/"),

aws_lambda_powertools/utilities/parser/envelopes/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from .apigw import ApiGatewayEnvelope
2+
from .apigw_websocket import ApiGatewayWebSocketEnvelope
23
from .apigwv2 import ApiGatewayV2Envelope
34
from .base import BaseEnvelope
45
from .bedrock_agent import BedrockAgentEnvelope
@@ -17,6 +18,7 @@
1718
__all__ = [
1819
"ApiGatewayEnvelope",
1920
"ApiGatewayV2Envelope",
21+
"ApiGatewayWebSocketEnvelope",
2022
"BedrockAgentEnvelope",
2123
"CloudWatchLogsEnvelope",
2224
"DynamoDBStreamEnvelope",
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from __future__ import annotations
2+
3+
import logging
4+
from typing import TYPE_CHECKING, Any
5+
6+
from aws_lambda_powertools.utilities.parser.envelopes.base import BaseEnvelope
7+
from aws_lambda_powertools.utilities.parser.models import APIGatewayWebSocketMessageEventModel
8+
9+
if TYPE_CHECKING:
10+
from aws_lambda_powertools.utilities.parser.types import Model
11+
12+
logger = logging.getLogger(__name__)
13+
14+
15+
class ApiGatewayWebSocketEnvelope(BaseEnvelope):
16+
"""API Gateway WebSockets envelope to extract data within body key of messages routes
17+
(not disconnect or connect)"""
18+
19+
def parse(self, data: dict[str, Any] | Any | None, model: type[Model]) -> Model | None:
20+
"""Parses data found with model provided
21+
22+
Parameters
23+
----------
24+
data : dict
25+
Lambda event to be parsed
26+
model : type[Model]
27+
Data model provided to parse after extracting data using envelope
28+
29+
Returns
30+
-------
31+
Any
32+
Parsed detail payload with model provided
33+
"""
34+
logger.debug(
35+
f"Parsing incoming data with Api Gateway WebSockets model {APIGatewayWebSocketMessageEventModel}",
36+
)
37+
parsed_envelope: APIGatewayWebSocketMessageEventModel = APIGatewayWebSocketMessageEventModel.model_validate(
38+
data,
39+
)
40+
logger.debug(f"Parsing event payload in `detail` with {model}")
41+
return self._parse(data=parsed_envelope.body, model=model)

aws_lambda_powertools/utilities/parser/models/__init__.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@
77
APIGatewayEventRequestContext,
88
APIGatewayProxyEventModel,
99
)
10+
from .apigw_websocket import (
11+
APIGatewayWebSocketConnectEventModel,
12+
APIGatewayWebSocketConnectEventRequestContext,
13+
APIGatewayWebSocketDisconnectEventModel,
14+
APIGatewayWebSocketDisconnectEventRequestContext,
15+
APIGatewayWebSocketEventIdentity,
16+
APIGatewayWebSocketEventRequestContextBase,
17+
APIGatewayWebSocketMessageEventModel,
18+
APIGatewayWebSocketMessageEventRequestContext,
19+
)
1020
from .apigwv2 import (
1121
ApiGatewayAuthorizerRequestV2,
1222
APIGatewayProxyEventV2Model,
@@ -105,6 +115,14 @@
105115
__all__ = [
106116
"APIGatewayProxyEventV2Model",
107117
"ApiGatewayAuthorizerRequestV2",
118+
"APIGatewayWebSocketEventIdentity",
119+
"APIGatewayWebSocketMessageEventModel",
120+
"APIGatewayWebSocketMessageEventRequestContext",
121+
"APIGatewayWebSocketConnectEventModel",
122+
"APIGatewayWebSocketConnectEventRequestContext",
123+
"APIGatewayWebSocketDisconnectEventRequestContext",
124+
"APIGatewayWebSocketDisconnectEventModel",
125+
"APIGatewayWebSocketEventRequestContextBase",
108126
"RequestContextV2",
109127
"RequestContextV2Http",
110128
"RequestContextV2Authorizer",
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from datetime import datetime
2+
from typing import Dict, List, Literal, Optional, Type, Union
3+
4+
from pydantic import BaseModel, Field
5+
from pydantic.networks import IPvAnyNetwork
6+
7+
8+
class APIGatewayWebSocketEventIdentity(BaseModel):
9+
source_ip: IPvAnyNetwork = Field(alias="sourceIp")
10+
user_agent: Optional[str] = Field(None, alias="userAgent")
11+
12+
class APIGatewayWebSocketEventRequestContextBase(BaseModel):
13+
extended_request_id: str = Field(alias="extendedRequestId")
14+
request_time: str = Field(alias="requestTime")
15+
stage: str = Field(alias="stage")
16+
connected_at: datetime = Field(alias="connectedAt")
17+
request_time_epoch: datetime = Field(alias="requestTimeEpoch")
18+
identity: APIGatewayWebSocketEventIdentity = Field(alias="identity")
19+
request_id: str = Field(alias="requestId")
20+
domain_name: str = Field(alias="domainName")
21+
connection_id: str = Field(alias="connectionId")
22+
api_id: str = Field(alias="apiId")
23+
24+
25+
class APIGatewayWebSocketMessageEventRequestContext(APIGatewayWebSocketEventRequestContextBase):
26+
route_key: str = Field(alias="routeKey")
27+
message_id: str = Field(alias="messageId")
28+
event_type: Literal["MESSAGE"] = Field(alias="eventType")
29+
message_direction: Literal["IN", "OUT"] = Field(alias="messageDirection")
30+
31+
32+
class APIGatewayWebSocketConnectEventRequestContext(APIGatewayWebSocketEventRequestContextBase):
33+
route_key: Literal["$connect"] = Field(alias="routeKey")
34+
event_type: Literal["CONNECT"] = Field(alias="eventType")
35+
message_direction: Literal["IN"] = Field(alias="messageDirection")
36+
37+
38+
class APIGatewayWebSocketDisconnectEventRequestContext(APIGatewayWebSocketEventRequestContextBase):
39+
route_key: Literal["$disconnect"] = Field(alias="routeKey")
40+
disconnect_status_code: int = Field(alias="disconnectStatusCode")
41+
event_type: Literal["DISCONNECT"] = Field(alias="eventType")
42+
message_direction: Literal["IN"] = Field(alias="messageDirection")
43+
disconnect_reason: str = Field(alias="disconnectReason")
44+
45+
46+
class APIGatewayWebSocketConnectEventModel(BaseModel):
47+
headers: Dict[str, str] = Field(alias="headers")
48+
multi_value_headers: Dict[str, List[str]] = Field(alias="multiValueHeaders")
49+
request_context: APIGatewayWebSocketConnectEventRequestContext = Field(alias="requestContext")
50+
is_base64_encoded: bool = Field(alias="isBase64Encoded")
51+
52+
53+
class APIGatewayWebSocketDisconnectEventModel(BaseModel):
54+
headers: Dict[str, str] = Field(alias="headers")
55+
multi_value_headers: Dict[str, List[str]] = Field(alias="multiValueHeaders")
56+
request_context: APIGatewayWebSocketDisconnectEventRequestContext = Field(alias="requestContext")
57+
is_base64_encoded: bool = Field(alias="isBase64Encoded")
58+
59+
60+
class APIGatewayWebSocketMessageEventModel(BaseModel):
61+
request_context: APIGatewayWebSocketMessageEventRequestContext = Field(alias="requestContext")
62+
is_base64_encoded: bool = Field(alias="isBase64Encoded")
63+
body: Optional[Union[str, Type[BaseModel]]] = Field(None, alias="body")

0 commit comments

Comments
 (0)