Skip to content

Commit 84a00ef

Browse files
authored
Merge branch 'develop' into feature/file-parameter-clean
2 parents ad56ab1 + bf5a80f commit 84a00ef

File tree

9 files changed

+513
-202
lines changed

9 files changed

+513
-202
lines changed

CHANGELOG.md

Lines changed: 41 additions & 38 deletions
Large diffs are not rendered by default.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Exposes version constant to avoid circular dependencies."""
22

3-
VERSION = "3.19.1a11"
3+
VERSION = "3.19.1a12"

aws_lambda_powertools/utilities/parser/models/kafka.py

Lines changed: 92 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,76 @@
11
from datetime import datetime
22
from typing import Dict, List, Literal, Optional, Type, Union
33

4-
from pydantic import BaseModel, field_validator
4+
from pydantic import BaseModel, Field, field_validator
55

66
from aws_lambda_powertools.shared.functions import base64_decode, bytes_to_string, decode_header_bytes
77

88
SERVERS_DELIMITER = ","
99

1010

1111
class KafkaRecordSchemaMetadata(BaseModel):
12-
dataFormat: str
13-
schemaId: str
12+
dataFormat: str = Field(
13+
description="The data format of the schema (e.g., AVRO, JSON).",
14+
examples=["AVRO", "JSON", "PROTOBUF"],
15+
)
16+
schemaId: str = Field(
17+
description="The unique identifier of the schema.",
18+
examples=["1234", "5678", "schema-abc-123"],
19+
)
1420

1521

1622
class KafkaRecordModel(BaseModel):
17-
topic: str
18-
partition: int
19-
offset: int
20-
timestamp: datetime
21-
timestampType: str
22-
key: Optional[bytes] = None
23-
value: Union[str, Type[BaseModel]]
24-
headers: List[Dict[str, bytes]]
25-
keySchemaMetadata: Optional[KafkaRecordSchemaMetadata] = None
26-
valueSchemaMetadata: Optional[KafkaRecordSchemaMetadata] = None
23+
topic: str = Field(
24+
description="The Kafka topic name from which the record originated.",
25+
examples=["mytopic", "user-events", "order-processing", "mymessage-with-unsigned"],
26+
)
27+
partition: int = Field(
28+
description="The partition number within the topic from which the record was consumed.",
29+
examples=[0, 1, 5, 10],
30+
)
31+
offset: int = Field(
32+
description="The offset of the record within the partition.",
33+
examples=[15, 123, 456789, 1000000],
34+
)
35+
timestamp: datetime = Field(
36+
description="The timestamp of the record.",
37+
examples=[1545084650987, 1640995200000, 1672531200000],
38+
)
39+
timestampType: str = Field(
40+
description="The type of timestamp (CREATE_TIME or LOG_APPEND_TIME).",
41+
examples=["CREATE_TIME", "LOG_APPEND_TIME"],
42+
)
43+
key: Optional[bytes] = Field(
44+
default=None,
45+
description="The message key, base64-encoded. Can be null for messages without keys.",
46+
examples=["cmVjb3JkS2V5", "dXNlci0xMjM=", "b3JkZXItNDU2", None],
47+
)
48+
value: Union[str, Type[BaseModel]] = Field(
49+
description="The message value, base64-encoded.",
50+
examples=[
51+
"eyJrZXkiOiJ2YWx1ZSJ9",
52+
"eyJtZXNzYWdlIjogIkhlbGxvIEthZmthIn0=",
53+
"eyJ1c2VyX2lkIjogMTIzLCAiYWN0aW9uIjogImxvZ2luIn0=",
54+
],
55+
)
56+
headers: List[Dict[str, bytes]] = Field(
57+
description="A list of message headers as key-value pairs with byte array values.",
58+
examples=[
59+
[{"headerKey": [104, 101, 97, 100, 101, 114, 86, 97, 108, 117, 101]}],
60+
[{"contentType": [97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 47, 106, 115, 111, 110]}],
61+
[],
62+
],
63+
)
64+
keySchemaMetadata: Optional[KafkaRecordSchemaMetadata] = Field(
65+
default=None,
66+
description="Schema metadata for the message key when using schema registry.",
67+
examples=[{"dataFormat": "AVRO", "schemaId": "1234"}, None],
68+
)
69+
valueSchemaMetadata: Optional[KafkaRecordSchemaMetadata] = Field(
70+
default=None,
71+
description="Schema metadata for the message value when using schema registry.",
72+
examples=[{"dataFormat": "AVRO", "schemaId": "1234"}, None],
73+
)
2774

2875
# key is optional; only decode if not None
2976
@field_validator("key", mode="before")
@@ -44,8 +91,23 @@ def decode_headers_list(cls, value):
4491

4592

4693
class KafkaBaseEventModel(BaseModel):
47-
bootstrapServers: List[str]
48-
records: Dict[str, List[KafkaRecordModel]]
94+
bootstrapServers: List[str] = Field(
95+
description="A list of Kafka bootstrap servers (broker endpoints).",
96+
examples=[
97+
["b-2.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092"],
98+
[
99+
"b-1.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092",
100+
"b-2.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092",
101+
],
102+
],
103+
)
104+
records: Dict[str, List[KafkaRecordModel]] = Field(
105+
description="A dictionary mapping topic-partition combinations to lists of Kafka records.",
106+
examples=[
107+
{"mytopic-0": [{"topic": "mytopic", "partition": 0, "offset": 15}]},
108+
{"user-events-1": [{"topic": "user-events", "partition": 1, "offset": 123}]},
109+
],
110+
)
49111

50112
@field_validator("bootstrapServers", mode="before")
51113
def split_servers(cls, value):
@@ -59,7 +121,10 @@ class KafkaSelfManagedEventModel(KafkaBaseEventModel):
59121
- https://docs.aws.amazon.com/lambda/latest/dg/with-kafka.html
60122
"""
61123

62-
eventSource: Literal["SelfManagedKafka"]
124+
eventSource: Literal["SelfManagedKafka"] = Field(
125+
description="The event source identifier for self-managed Kafka.",
126+
examples=["SelfManagedKafka"],
127+
)
63128

64129

65130
class KafkaMskEventModel(KafkaBaseEventModel):
@@ -69,5 +134,14 @@ class KafkaMskEventModel(KafkaBaseEventModel):
69134
- https://docs.aws.amazon.com/lambda/latest/dg/with-msk.html
70135
"""
71136

72-
eventSource: Literal["aws:kafka"]
73-
eventSourceArn: str
137+
eventSource: Literal["aws:kafka"] = Field(
138+
description="The AWS service that invoked the function.",
139+
examples=["aws:kafka"],
140+
)
141+
eventSourceArn: str = Field(
142+
description="The Amazon Resource Name (ARN) of the MSK cluster.",
143+
examples=[
144+
"arn:aws:kafka:us-east-1:0123456789019:cluster/SalesCluster/abcd1234-abcd-cafe-abab-9876543210ab-4",
145+
"arn:aws:kafka:eu-central-1:123456789012:cluster/MyCluster/xyz789-1234-5678-90ab-cdef12345678-2",
146+
],
147+
)

aws_lambda_powertools/utilities/parser/models/sns.py

Lines changed: 125 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,116 @@
22
from typing import Dict, List, Literal, Optional, Union
33
from typing import Type as TypingType
44

5-
from pydantic import BaseModel, model_validator
5+
from pydantic import BaseModel, Field, model_validator
66
from pydantic.networks import HttpUrl
77

88

99
class SnsMsgAttributeModel(BaseModel):
10-
Type: str
11-
Value: str
10+
Type: str = Field(
11+
description="The data type of the message attribute (String, Number, Binary, or custom data type).",
12+
examples=["String", "Number", "Binary", "String.Array", "Number.Array"],
13+
)
14+
Value: str = Field(
15+
description="The value of the message attribute. All values are strings, even for Number types.",
16+
examples=["TestString", "123", "TestBinary", '["item1", "item2"]'],
17+
)
1218

1319

1420
class SnsNotificationModel(BaseModel):
15-
Subject: Optional[str] = None
16-
TopicArn: str
17-
UnsubscribeUrl: HttpUrl
18-
Type: Literal["Notification"]
19-
MessageAttributes: Optional[Dict[str, SnsMsgAttributeModel]] = None
20-
Message: Union[str, TypingType[BaseModel]]
21-
MessageId: str
22-
SigningCertUrl: Optional[HttpUrl] = None # NOTE: FIFO opt-in removes attribute
23-
Signature: Optional[str] = None # NOTE: FIFO opt-in removes attribute
24-
Timestamp: datetime
25-
SignatureVersion: Optional[str] = None # NOTE: FIFO opt-in removes attribute
21+
Subject: Optional[str] = Field(
22+
default=None,
23+
description="The subject parameter provided when the notification was published to the topic.",
24+
examples=["TestInvoke", "Alert: System maintenance", "Order Confirmation", None],
25+
)
26+
TopicArn: str = Field(
27+
description="The Amazon Resource Name (ARN) for the topic that this message was published to.",
28+
examples=[
29+
"arn:aws:sns:us-east-2:123456789012:sns-lambda",
30+
"arn:aws:sns:eu-west-1:123456789012:notification-topic",
31+
"arn:aws:sns:us-west-2:123456789012:alerts.fifo",
32+
],
33+
)
34+
UnsubscribeUrl: HttpUrl = Field(
35+
description="A URL that you can use to unsubscribe the endpoint from this topic.",
36+
examples=[
37+
(
38+
"https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn= \
39+
arn:aws:sns:us-east-2:123456789012:test-lambda:21be56ed-a058-49f5-8c98-aedd2564c486"
40+
),
41+
(
42+
"https://sns.eu-west-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn= \
43+
arn:aws:sns:eu-west-1:123456789012:notification-topic:abcd1234-5678-90ef-ghij-klmnopqrstuv"
44+
),
45+
],
46+
)
47+
Type: Literal["Notification"] = Field(
48+
description="The type of message. For Lambda triggers, this is always 'Notification'.",
49+
examples=["Notification"],
50+
)
51+
MessageAttributes: Optional[Dict[str, SnsMsgAttributeModel]] = Field(
52+
default=None,
53+
description="User-defined message attributes as key-value pairs with type information.",
54+
examples=[
55+
{"Test": {"Type": "String", "Value": "TestString"}},
56+
{"priority": {"Type": "Number", "Value": "1"}, "env": {"Type": "String", "Value": "prod"}},
57+
None,
58+
],
59+
)
60+
Message: Union[str, TypingType[BaseModel]] = Field(
61+
description="The message value specified when the notification was published to the topic.",
62+
examples=[
63+
"Hello from SNS!",
64+
'{"alert": "CPU usage above 80%", "instance": "i-1234567890abcdef0"}',
65+
'{"order_id": 12345, "status": "confirmed", "total": 99.99}',
66+
],
67+
)
68+
MessageId: str = Field(
69+
description="A Universally Unique Identifier, unique for each message published.",
70+
examples=[
71+
"95df01b4-ee98-5cb9-9903-4c221d41eb5e",
72+
"da41e39f-ea4d-435a-b922-c6aae3915ebe",
73+
"f3c8d4e2-1a2b-4c5d-9e8f-7g6h5i4j3k2l",
74+
],
75+
)
76+
SigningCertUrl: Optional[HttpUrl] = Field(
77+
default=None,
78+
description=(
79+
"The URL to the certificate that was used to sign the message. "
80+
"Not present for FIFO topics with content-based deduplication."
81+
),
82+
examples=[
83+
"https://sns.us-east-2.amazonaws.com/SimpleNotificationService-1234567890.pem",
84+
"https://sns.eu-west-1.amazonaws.com/SimpleNotificationService-0987654321.pem",
85+
None,
86+
],
87+
) # NOTE: FIFO opt-in removes attribute
88+
Signature: Optional[str] = Field(
89+
default=None,
90+
description=(
91+
"Base64-encoded SHA1withRSA signature of the message. "
92+
"Not present for FIFO topics with content-based deduplication."
93+
),
94+
examples=[
95+
"tcc6faL2yUC6dgZdmrwh1Y4cGa/ebXEkAi6RibDsvpi+tE/1+82j...65r==",
96+
"EXAMPLEw6JRNwm1LFQL4ICB0bnXrdB8ClRMTQFPGBfHs...EXAMPLEw==",
97+
None,
98+
],
99+
) # NOTE: FIFO opt-in removes attribute
100+
Timestamp: datetime = Field(
101+
description="The time (GMT) when the notification was published.",
102+
examples=[
103+
"2019-01-02T12:45:07.000Z",
104+
"2023-06-15T10:30:00.000Z",
105+
"2023-12-25T18:45:30.123Z",
106+
],
107+
)
108+
SignatureVersion: Optional[str] = Field(
109+
default=None,
110+
description=(
111+
"Version of the Amazon SNS signature used. Not present for FIFO topics with content-based deduplication."
112+
),
113+
examples=["1", "2", None],
114+
) # NOTE: FIFO opt-in removes attribute
26115

27116
@model_validator(mode="before")
28117
def check_sqs_protocol(cls, values):
@@ -37,11 +126,28 @@ def check_sqs_protocol(cls, values):
37126

38127

39128
class SnsRecordModel(BaseModel):
40-
EventSource: Literal["aws:sns"]
41-
EventVersion: str
42-
EventSubscriptionArn: str
43-
Sns: SnsNotificationModel
129+
EventSource: Literal["aws:sns"] = Field(
130+
description="The AWS service that invoked the function.",
131+
examples=["aws:sns"],
132+
)
133+
EventVersion: str = Field(
134+
description="The version of the event schema.",
135+
examples=["1.0", "2.0"],
136+
)
137+
EventSubscriptionArn: str = Field(
138+
description="The Amazon Resource Name (ARN) of the subscription.",
139+
examples=[
140+
"arn:aws:sns:us-east-2:123456789012:sns-lambda:21be56ed-a058-49f5-8c98-aedd2564c486",
141+
"arn:aws:sns:eu-west-1:123456789012:notification-topic:abcd1234-5678-90ef-ghij-klmnopqrstuv",
142+
],
143+
)
144+
Sns: SnsNotificationModel = Field(
145+
description="The SNS message that triggered the Lambda function.",
146+
)
44147

45148

46149
class SnsModel(BaseModel):
47-
Records: List[SnsRecordModel]
150+
Records: List[SnsRecordModel] = Field(
151+
description="A list of SNS message records included in the event.",
152+
examples=[[{"EventSource": "aws:sns", "Sns": {"MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e"}}]],
153+
)

0 commit comments

Comments
 (0)