Skip to content

Commit 10d0079

Browse files
author
Ran Isenberg
committed
fix: CR fixes
Merge branch 'develop' of https://github.com/awslabs/aws-lambda-powertools-python into pydantic
2 parents a47056f + d08de0b commit 10d0079

File tree

10 files changed

+14586
-7915
lines changed

10 files changed

+14586
-7915
lines changed

Makefile

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ target:
44

55
dev:
66
pip install --upgrade pip poetry pre-commit
7-
poetry install
87
poetry install --extras "pydantic"
98
pre-commit install
109

aws_lambda_powertools/utilities/advanced_parser/envelopes/base.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ def _parse_user_dict_schema(self, user_event: Dict[str, Any], schema: BaseModel)
2121
def _parse_user_json_string_schema(self, user_event: str, schema: BaseModel) -> Any:
2222
if user_event is None:
2323
return None
24-
logger.debug("parsing user dictionary schema")
24+
# this is used in cases where the underlying schema is not a Dict that can be parsed as baseModel
25+
# but a plain string i.e SQS has plain string payload
2526
if schema == str:
2627
logger.debug("input is string, returning")
2728
return user_event

aws_lambda_powertools/utilities/advanced_parser/envelopes/dynamodb.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import logging
2-
from typing import Any, Dict
2+
from typing import Any, Dict, List
3+
from typing_extensions import Literal
34

45
from pydantic import BaseModel, ValidationError
56

@@ -9,8 +10,11 @@
910
logger = logging.getLogger(__name__)
1011

1112

13+
# returns a List of dictionaries which each contains two keys, "NewImage" and "OldImage".
14+
# The values are the parsed schema models. The images' values can also be None.
15+
# Length of the list is the record's amount in the original event.
1216
class DynamoDBEnvelope(BaseEnvelope):
13-
def parse(self, event: Dict[str, Any], schema: BaseModel) -> Any:
17+
def parse(self, event: Dict[str, Any], schema: BaseModel) -> List[Dict[Literal["NewImage", "OldImage"], BaseModel]]:
1418
try:
1519
parsed_envelope = DynamoDBSchema(**event)
1620
except (ValidationError, TypeError):

aws_lambda_powertools/utilities/advanced_parser/envelopes/event_bridge.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
logger = logging.getLogger(__name__)
1010

1111

12+
# returns a parsed BaseModel object according to schema type
1213
class EventBridgeEnvelope(BaseEnvelope):
13-
def parse(self, event: Dict[str, Any], schema: BaseModel) -> Any:
14+
def parse(self, event: Dict[str, Any], schema: BaseModel) -> BaseModel:
1415
try:
1516
parsed_envelope = EventBridgeSchema(**event)
1617
except (ValidationError, TypeError):

aws_lambda_powertools/utilities/advanced_parser/envelopes/sqs.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import logging
2-
from typing import Any, Dict, List
2+
from typing import Any, Dict, List, Union
33

44
from pydantic import BaseModel, ValidationError
55

@@ -9,8 +9,13 @@
99
logger = logging.getLogger(__name__)
1010

1111

12+
# returns a list of parsed schemas of type BaseModel or plain string.
13+
# The record's body parameter is a string. However, it can also be a JSON encoded string which
14+
# can then be parsed into a BaseModel object.
15+
# Note that all records will be parsed the same way so if schema is str,
16+
# all the items in the list will be parsed as str and npt as JSON (and vice versa).
1217
class SqsEnvelope(BaseEnvelope):
13-
def parse(self, event: Dict[str, Any], schema: BaseModel) -> List[BaseModel]:
18+
def parse(self, event: Dict[str, Any], schema: Union[BaseModel, str]) -> List[Union[BaseModel, str]]:
1419
try:
1520
parsed_envelope = SqsSchema(**event)
1621
except (ValidationError, TypeError):

aws_lambda_powertools/utilities/advanced_parser/parser.py

+17-5
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,36 @@ def parser(
2121
2222
As Lambda follows (event, context) signature we can remove some of the boilerplate
2323
and also capture any exception any Lambda function throws as metadata.
24-
Event will be the parsed & validated BaseModel pydantic object of the input type "schema"
24+
event will be the parsed and passed as a BaseModel pydantic class of the input type "schema"
25+
to the lambda handler.
26+
event will be extracted from the envelope in case envelope is not None.
27+
In case envelope is None, the complete event is parsed to match the schema parameter BaseModel definition.
28+
In case envelope is not None, first the event is parsed as the envelope's schema definition, and the user
29+
message is extracted and parsed again as the schema parameter's definition.
2530
2631
Example
2732
-------
2833
**Lambda function using validation decorator**
2934
3035
@parser(schema=MyBusiness, envelope=envelopes.EVENTBRIDGE)
31-
def handler(event: inbound_schema_model , context: LambdaContext):
36+
def handler(event: MyBusiness , context: LambdaContext):
3237
...
3338
3439
Parameters
3540
----------
36-
todo add
41+
handler: input for lambda_handler_decorator, wraps the handler lambda
42+
event: AWS event dictionary
43+
context: AWS lambda context
44+
schema: pydantic BaseModel class. This is the user data schema that will replace the event.
45+
event parameter will be parsed and a new schema object will be created from it.
46+
envelope: what envelope to extract the schema from, can be any AWS service that is currently
47+
supported in the envelopes module. Can be None.
3748
3849
Raises
3950
------
4051
err
41-
TypeError or pydantic.ValidationError or any exception raised by the lambda handler itself
52+
TypeError - in case event is None
53+
pydantic.ValidationError - event fails validation, either of the envelope
4254
"""
4355
lambda_handler_name = handler.__name__
4456
parsed_event = None
@@ -53,4 +65,4 @@ def handler(event: inbound_schema_model , context: LambdaContext):
5365
parsed_event = parse_envelope(event, envelope, schema)
5466

5567
logger.debug(f"Calling handler {lambda_handler_name}")
56-
handler(parsed_event, context)
68+
return handler(parsed_event, context)

aws_lambda_powertools/utilities/advanced_parser/schemas/dynamodb.py

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ class DynamoScheme(BaseModel):
1414
SizeBytes: int
1515
StreamViewType: Literal["NEW_AND_OLD_IMAGES", "KEYS_ONLY", "NEW_IMAGE", "OLD_IMAGE"]
1616

17+
# since both images are optional, they can both be None. However, at least one must
18+
# exist in a legal schema of NEW_AND_OLD_IMAGES type
1719
@root_validator
1820
def check_one_image_exists(cls, values):
1921
newimg, oldimg = values.get("NewImage"), values.get("OldImage")

aws_lambda_powertools/utilities/advanced_parser/schemas/sqs.py

+4
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,17 @@ class SqsMsgAttributeSchema(BaseModel):
2424
binaryListValues: List[str] = []
2525
dataType: str
2626

27+
# Amazon SQS supports the logical data types String, Number, and Binary with optional custom data type
28+
# labels with the format .custom-data-type.
29+
# https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-message-metadata.html#sqs-message-attributes
2730
@validator("dataType")
2831
def valid_type(cls, v): # noqa: VNE001
2932
pattern = re.compile("Number.*|String.*|Binary.*")
3033
if not pattern.match(v):
3134
raise TypeError("data type is invalid")
3235
return v
3336

37+
# validate that dataType and value are not None and match
3438
@root_validator
3539
def check_str_and_binary_values(cls, values):
3640
binary_val, str_val = values.get("binaryValue", ""), values.get("stringValue", "")

0 commit comments

Comments
 (0)