Changes
Event source data classes
This release adds support for Cognito Authentication Challenge Lambda Triggers for create, define and verify trigger sources. It also enhances get_header_value
method by optionally supporting case insensitive headers.
Credits: Thanks to @michaelbrewer from Gyft for both enhancements.
Parser utility
Parser is a new utility that provides parsing and deep data validation using Pydantic Models - It requires an optional dependency (Pydantic) to work.
It uses Pydantic Model classes, similar to dataclasses, to model the shape of your data, enforce type hints at runtime, serialize your models to JSON, JSON Schema, and with third party tools you can also auto-generate Model classes from JSON, YAML, OpenAPI, etc.
from aws_lambda_powertools.utilities.parser import event_parser, BaseModel, ValidationError
from aws_lambda_powertools.utilities.typing import LambdaContext
import json
class OrderItem(BaseModel):
id: int
quantity: int
description: str
class Order(BaseModel):
id: int
description: str
items: List[OrderItem] # nesting models are supported
optional_field: Optional[str] # this field may or may not be available when parsing
@event_parser(model=Order)
def handler(event: Order, context: LambdaContext):
assert event.id == 10876546789
assert event.description == "My order"
assert len(event.items) == 1
order_items = [items for item in event.items]
...
payload = {
"id": 10876546789,
"description": "My order",
"items": [
{
"id": 1015938732,
"quantity": 1,
"description": "item xpto"
}
]
}
handler(event=payload, context=LambdaContext())
# also works if event is a JSON string
handler(event=json.dumps(payload), context=LambdaContext())
With this release, we provide a few built-in models to start with such as Amazon EventBridge, Amazon DynamoDB, and Amazon SQS - You can extend them by inheriting and overriding their properties to plug-in your models.
from aws_lambda_powertools.utilities.parser import parse, BaseModel
from aws_lambda_powertools.utilities.parser.models import EventBridgeModel
from typing import List, Optional
class OrderItem(BaseModel):
id: int
quantity: int
description: str
class Order(BaseModel):
id: int
description: str
items: List[OrderItem]
# Override `detail` key of a custom event in EventBridge from str to Order
class OrderEventModel(EventBridgeModel):
detail: Order
payload = {...} # EventBridge event dict with Order inside detail as JSON
order = parse(model=OrderEventModel, event=payload) # parse input event into OrderEventModel
assert order.source == "OrderService"
assert order.detail.description == "My order"
assert order.detail_type == "OrderPurchased" # we rename it to snake_case since detail-type is an invalid name
# We can access our Order just as fine now
for order_item in order.detail.items:
...
# We can also serialize any property of our parsed model into JSON, JSON Schema, or as a Dict
order_dict = order.dict()
order_json = order.json()
order_json_schema_as_dict = order.schema()
order_json_schema_as_json = order.schema_json(indent=2)
Similar to Validator utility, it provides an envelope
feature to parse known structures that wrap your event. It's useful when you you want to parse both the structure and your model but only return your actual data from the envelope.
Example using one of the built-in envelopes provided from day one:
from aws_lambda_powertools.utilities.parser import event_parser, parse, BaseModel, envelopes
from aws_lambda_powertools.utilities.typing import LambdaContext
class UserModel(BaseModel):
username: str
password1: str
password2: str
payload = {
"version": "0",
"id": "6a7e8feb-b491-4cf7-a9f1-bf3703467718",
"detail-type": "CustomerSignedUp",
"source": "CustomerService",
"account": "111122223333",
"time": "2020-10-22T18:43:48Z",
"region": "us-west-1",
"resources": ["some_additional_"],
"detail": {
"username": "universe",
"password1": "myp@ssword",
"password2": "repeat password"
}
}
ret = parse(model=UserModel, envelope=envelopes.EventBridgeModel, event=payload)
# Parsed model only contains our actual model, not the entire EventBridge + Payload parsed
assert ret.password1 == ret.password2
# Same behaviour but using our decorator
@event_parser(model=UserModel, envelope=envelopes.EventBridgeModel)
def handler(event: UserModel, context: LambdaContext):
assert event.password1 == event.password2
Credits: Thanks to @risenberg-cyberark from CyberArk for the idea, implementation, and guidance on how to best support Pydantic to provide both parsing and deep data validation. Also, special thanks to @koxudaxi for helping review with his extensive Pydantic experience.
πNew features and non-breaking changes
- feat: Advanced parser utility (pydantic) (#118) by @risenberg-cyberark
π Minor Changes
- feat(data_classes): case insensitive header lookup (#186) by @michaelbrewer
- test(parser): Add missing test coverage and lint changes (#188) by @michaelbrewer
- feat(data_classes): Cognito custom auth triggers (#178) by @michaelbrewer
π Documentation updates
- docs: add more info on conditional keys #195 (#199) by @heitorlessa
- docs: new parser utility (#192) by @heitorlessa
- improv: Update README.md (#190) by @bmicklea
π Bug and hot fixes
- improv: keeps Lambda root logger handler intact, and add log filter instead to prevent child log records duplication (#198) by @heitorlessa
π§ Internal
- fix: move from npm to yarn to fix peer deps vuln with resolutions feature (#197) by @heitorlessa
- build(deps): bump object-path from 0.11.4 to 0.11.5 in /docs (#196) by @dependabot
- chore: docs npm security node-forge (#181) by @heitorlessa
- chore: remove kitchen sink example (#180) by @heitorlessa
- chore: ease maintenance of upcoming parser #118 (#189) by @heitorlessa
- chore: bump to 1.7.0 (#200) by @heitorlessa
This release was made possible by the following contributors:
@bmicklea, @dependabot, @dependabot[bot], @heitorlessa, @michaelbrewer, @risenberg-cyberark, @Nr18, and @koxudaxi