Skip to content

Commit 25b4ee1

Browse files
committed
Merge branch 'develop' into feat/reduce-batch-boilerplate
* develop: fix(batch): handle early validation errors for pydantic models (poison pill) #2091 (#2099) update changelog with latest changes docs(homepage): remove banner for end-of-support v1 (#2098) chore(deps-dev): bump aws-cdk-lib from 2.72.1 to 2.73.0 (#2097) chore(deps-dev): bump filelock from 3.10.7 to 3.11.0 (#2094) chore(deps-dev): bump coverage from 7.2.2 to 7.2.3 (#2092) chore(deps-dev): bump aws-cdk from 2.72.1 to 2.73.0 (#2093) chore(deps-dev): bump mypy-boto3-cloudformation from 1.26.60 to 1.26.108 (#2095) Signed-off-by: heitorlessa <[email protected]>
2 parents 45fa58c + c3e25d6 commit 25b4ee1

File tree

15 files changed

+396
-171
lines changed

15 files changed

+396
-171
lines changed

CHANGELOG.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
## Documentation
88

9+
* **homepage:** remove banner for end-of-support v1 ([#2098](https://github.com/awslabs/aws-lambda-powertools-python/issues/2098))
910
* **idempotency:** fixes to testing your code section ([#2073](https://github.com/awslabs/aws-lambda-powertools-python/issues/2073))
1011
* **idempotency:** new sequence diagrams, fix idempotency record vs DynamoDB TTL confusion ([#2074](https://github.com/awslabs/aws-lambda-powertools-python/issues/2074))
1112
* **parser:** fix highlighted line ([#2064](https://github.com/awslabs/aws-lambda-powertools-python/issues/2064))
@@ -18,13 +19,18 @@
1819

1920
* **deps:** bump aws-xray-sdk from 2.11.0 to 2.12.0 ([#2080](https://github.com/awslabs/aws-lambda-powertools-python/issues/2080))
2021
* **deps:** bump peaceiris/actions-gh-pages from 3.9.2 to 3.9.3 ([#2069](https://github.com/awslabs/aws-lambda-powertools-python/issues/2069))
22+
* **deps-dev:** bump aws-cdk-lib from 2.72.1 to 2.73.0 ([#2097](https://github.com/awslabs/aws-lambda-powertools-python/issues/2097))
23+
* **deps-dev:** bump aws-cdk from 2.72.1 to 2.73.0 ([#2093](https://github.com/awslabs/aws-lambda-powertools-python/issues/2093))
24+
* **deps-dev:** bump mypy-boto3-cloudformation from 1.26.60 to 1.26.108 ([#2095](https://github.com/awslabs/aws-lambda-powertools-python/issues/2095))
2125
* **deps-dev:** bump types-python-dateutil from 2.8.19.11 to 2.8.19.12 ([#2085](https://github.com/awslabs/aws-lambda-powertools-python/issues/2085))
26+
* **deps-dev:** bump cfn-lint from 0.76.1 to 0.76.2 ([#2084](https://github.com/awslabs/aws-lambda-powertools-python/issues/2084))
2227
* **deps-dev:** bump aws-cdk from 2.72.0 to 2.72.1 ([#2081](https://github.com/awslabs/aws-lambda-powertools-python/issues/2081))
28+
* **deps-dev:** bump coverage from 7.2.2 to 7.2.3 ([#2092](https://github.com/awslabs/aws-lambda-powertools-python/issues/2092))
2329
* **deps-dev:** bump mkdocs-material from 9.1.4 to 9.1.5 ([#2077](https://github.com/awslabs/aws-lambda-powertools-python/issues/2077))
2430
* **deps-dev:** bump aws-cdk-lib from 2.72.0 to 2.72.1 ([#2076](https://github.com/awslabs/aws-lambda-powertools-python/issues/2076))
2531
* **deps-dev:** bump mypy-boto3-s3 from 1.26.99 to 1.26.104 ([#2075](https://github.com/awslabs/aws-lambda-powertools-python/issues/2075))
2632
* **deps-dev:** bump aws-cdk from 2.71.0 to 2.72.0 ([#2071](https://github.com/awslabs/aws-lambda-powertools-python/issues/2071))
27-
* **deps-dev:** bump cfn-lint from 0.76.1 to 0.76.2 ([#2084](https://github.com/awslabs/aws-lambda-powertools-python/issues/2084))
33+
* **deps-dev:** bump filelock from 3.10.7 to 3.11.0 ([#2094](https://github.com/awslabs/aws-lambda-powertools-python/issues/2094))
2834
* **deps-dev:** bump aws-cdk-lib from 2.71.0 to 2.72.0 ([#2070](https://github.com/awslabs/aws-lambda-powertools-python/issues/2070))
2935
* **deps-dev:** bump black from 23.1.0 to 23.3.0 ([#2066](https://github.com/awslabs/aws-lambda-powertools-python/issues/2066))
3036
* **deps-dev:** bump aws-cdk from 2.70.0 to 2.71.0 ([#2067](https://github.com/awslabs/aws-lambda-powertools-python/issues/2067))

aws_lambda_powertools/utilities/batch/base.py

+38-5
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
KinesisStreamRecord,
2727
)
2828
from aws_lambda_powertools.utilities.data_classes.sqs_event import SQSRecord
29+
from aws_lambda_powertools.utilities.parser import ValidationError
2930
from aws_lambda_powertools.utilities.typing import LambdaContext
3031

3132
logger = logging.getLogger(__name__)
@@ -305,21 +306,36 @@ def _get_messages_to_report(self) -> List[Dict[str, str]]:
305306
def _collect_sqs_failures(self):
306307
failures = []
307308
for msg in self.fail_messages:
308-
msg_id = msg.messageId if self.model else msg.message_id
309+
# If a message failed due to model validation (e.g., poison pill)
310+
# we convert to an event source data class...but self.model is still true
311+
# therefore, we do an additional check on whether the failed message is still a model
312+
# see https://github.com/awslabs/aws-lambda-powertools-python/issues/2091
313+
if self.model and getattr(msg, "parse_obj", None):
314+
msg_id = msg.messageId
315+
else:
316+
msg_id = msg.message_id
309317
failures.append({"itemIdentifier": msg_id})
310318
return failures
311319

312320
def _collect_kinesis_failures(self):
313321
failures = []
314322
for msg in self.fail_messages:
315-
msg_id = msg.kinesis.sequenceNumber if self.model else msg.kinesis.sequence_number
323+
# # see https://github.com/awslabs/aws-lambda-powertools-python/issues/2091
324+
if self.model and getattr(msg, "parse_obj", None):
325+
msg_id = msg.kinesis.sequenceNumber
326+
else:
327+
msg_id = msg.kinesis.sequence_number
316328
failures.append({"itemIdentifier": msg_id})
317329
return failures
318330

319331
def _collect_dynamodb_failures(self):
320332
failures = []
321333
for msg in self.fail_messages:
322-
msg_id = msg.dynamodb.SequenceNumber if self.model else msg.dynamodb.sequence_number
334+
# see https://github.com/awslabs/aws-lambda-powertools-python/issues/2091
335+
if self.model and getattr(msg, "parse_obj", None):
336+
msg_id = msg.dynamodb.SequenceNumber
337+
else:
338+
msg_id = msg.dynamodb.sequence_number
323339
failures.append({"itemIdentifier": msg_id})
324340
return failures
325341

@@ -336,6 +352,17 @@ def _to_batch_type(self, record: dict, event_type: EventType, model: Optional["B
336352
return model.parse_obj(record)
337353
return self._DATA_CLASS_MAPPING[event_type](record)
338354

355+
def _register_model_validation_error_record(self, record: dict):
356+
"""Convert and register failure due to poison pills where model failed validation early"""
357+
# Parser will fail validation if record is a poison pill (malformed input)
358+
# this means we can't collect the message id if we try transforming again
359+
# so we convert into to the equivalent batch type model (e.g., SQS, Kinesis, DynamoDB Stream)
360+
# and downstream we can correctly collect the correct message id identifier and make the failed record available
361+
# see https://github.com/awslabs/aws-lambda-powertools-python/issues/2091
362+
logger.debug("Record cannot be converted to customer's model; converting without model")
363+
failed_record: "EventSourceDataClassTypes" = self._to_batch_type(record=record, event_type=self.event_type)
364+
return self.failure_handler(record=failed_record, exception=sys.exc_info())
365+
339366

340367
class BatchProcessor(BasePartialBatchProcessor): # Keep old name for compatibility
341368
"""Process native partial responses from SQS, Kinesis Data Streams, and DynamoDB.
@@ -460,14 +487,17 @@ def _process_record(self, record: dict) -> Union[SuccessResponse, FailureRespons
460487
record: dict
461488
A batch record to be processed.
462489
"""
463-
data = self._to_batch_type(record=record, event_type=self.event_type, model=self.model)
490+
data: Optional["BatchTypeModels"] = None
464491
try:
492+
data = self._to_batch_type(record=record, event_type=self.event_type, model=self.model)
465493
if self._handler_accepts_lambda_context:
466494
result = self.handler(record=data, lambda_context=self.lambda_context)
467495
else:
468496
result = self.handler(record=data)
469497

470498
return self.success_handler(record=record, result=result)
499+
except ValidationError:
500+
return self._register_model_validation_error_record(record)
471501
except Exception:
472502
return self.failure_handler(record=data, exception=sys.exc_info())
473503

@@ -595,13 +625,16 @@ async def _async_process_record(self, record: dict) -> Union[SuccessResponse, Fa
595625
record: dict
596626
A batch record to be processed.
597627
"""
598-
data = self._to_batch_type(record=record, event_type=self.event_type, model=self.model)
628+
data: Optional["BatchTypeModels"] = None
599629
try:
630+
data = self._to_batch_type(record=record, event_type=self.event_type, model=self.model)
600631
if self._handler_accepts_lambda_context:
601632
result = await self.handler(record=data, lambda_context=self.lambda_context)
602633
else:
603634
result = await self.handler(record=data)
604635

605636
return self.success_handler(record=record, result=result)
637+
except ValidationError:
638+
return self._register_model_validation_error_record(record)
606639
except Exception:
607640
return self.failure_handler(record=data, exception=sys.exc_info())

aws_lambda_powertools/utilities/parser/models/dynamodb.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
class DynamoDBStreamChangedRecordModel(BaseModel):
1010
ApproximateCreationDateTime: Optional[date]
1111
Keys: Dict[str, Dict[str, Any]]
12-
NewImage: Optional[Union[Dict[str, Any], Type[BaseModel]]]
13-
OldImage: Optional[Union[Dict[str, Any], Type[BaseModel]]]
12+
NewImage: Optional[Union[Dict[str, Any], Type[BaseModel], BaseModel]]
13+
OldImage: Optional[Union[Dict[str, Any], Type[BaseModel], BaseModel]]
1414
SequenceNumber: str
1515
SizeBytes: int
1616
StreamViewType: Literal["NEW_AND_OLD_IMAGES", "KEYS_ONLY", "NEW_IMAGE", "OLD_IMAGE"]

aws_lambda_powertools/utilities/parser/models/kinesis.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class KinesisDataStreamRecordPayload(BaseModel):
1515
kinesisSchemaVersion: str
1616
partitionKey: str
1717
sequenceNumber: str
18-
data: Union[bytes, Type[BaseModel]] # base64 encoded str is parsed into bytes
18+
data: Union[bytes, Type[BaseModel], BaseModel] # base64 encoded str is parsed into bytes
1919
approximateArrivalTimestamp: float
2020

2121
@validator("data", pre=True, allow_reuse=True)

aws_lambda_powertools/utilities/parser/models/sqs.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ class SqsMsgAttributeModel(BaseModel):
5252
class SqsRecordModel(BaseModel):
5353
messageId: str
5454
receiptHandle: str
55-
body: Union[str, Type[BaseModel]]
55+
body: Union[str, Type[BaseModel], BaseModel]
5656
attributes: SqsAttributesModel
5757
messageAttributes: Dict[str, SqsMsgAttributeModel]
5858
md5OfBody: str

aws_lambda_powertools/utilities/parser/types.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@
33
import sys
44
from typing import Any, Dict, Type, TypeVar, Union
55

6-
from pydantic import BaseModel
6+
from pydantic import BaseModel, Json
77

88
# We only need typing_extensions for python versions <3.8
99
if sys.version_info >= (3, 8):
10-
from typing import Literal # noqa: F401
10+
from typing import Literal
1111
else:
12-
from typing_extensions import Literal # noqa: F401
12+
from typing_extensions import Literal
1313

1414
Model = TypeVar("Model", bound=BaseModel)
1515
EnvelopeModel = TypeVar("EnvelopeModel")
1616
EventParserReturnType = TypeVar("EventParserReturnType")
1717
AnyInheritedModel = Union[Type[BaseModel], BaseModel]
1818
RawDictOrModel = Union[Dict[str, Any], AnyInheritedModel]
19+
20+
__all__ = ["Json", "Literal"]

docs/overrides/main.html

-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
{% extends "base.html" %}
22

3-
{% block announce %}
4-
👋 Powertools for Python v1 will no longer receive updates or releases after 31/03/2023!
5-
We encourage you to read our <a href="{{ base_url }}/upgrade/#end-of-support-v1">upgrade guide on how to migrate to v2</a>.
6-
{% endblock %}
7-
83
{% block outdated %}
94
You're not viewing the latest version.
105
<a href="{{ '../' ~ base_url }}">

docs/upgrade.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ description: Guide to update between major Powertools versions
77

88
## End of support v1
99

10-
On March 31st, AWS Lambda Powertools for Python v1 will reach end of support. After that, Powertools v1 will no longer receive updates or releases. If you are still using v1, we encourage you to read our upgrade guide and update to the latest version.
10+
!!! warning "On March 31st, 2023, AWS Lambda Powertools for Python v1 reached end of support and will no longer receive updates or releases. If you are still using v1, we strongly recommend you to read our upgrade guide and update to the latest version."
1111

1212
Given our commitment to all of our customers using AWS Lambda Powertools for Python, we will keep [Pypi](https://pypi.org/project/aws-lambda-powertools/) v1 releases and documentation 1.x versions to prevent any disruption.
1313

package-lock.json

+7-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
"name": "aws-lambda-powertools-python-e2e",
33
"version": "1.0.0",
44
"devDependencies": {
5-
"aws-cdk": "^2.72.1"
5+
"aws-cdk": "^2.73.0"
66
}
77
}

0 commit comments

Comments
 (0)