github aws-powertools/powertools-lambda-python v1.7.0
v.1.7.0

latest releases: v2.37.0, v2.36.0, v2.35.1...
3 years ago

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

image

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

πŸ“œ Documentation updates

πŸ› 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

This release was made possible by the following contributors:

@bmicklea, @dependabot, @dependabot[bot], @heitorlessa, @michaelbrewer, @risenberg-cyberark, @Nr18, and @koxudaxi

Don't miss a new powertools-lambda-python release

NewReleases is sending notifications on new releases.