Skip to content

docs(parser): extract and fix code examples #1125

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,10 @@ changelog:

mypy:
poetry run mypy --pretty aws_lambda_powertools

format-examples:
poetry run isort docs/examples
poetry run black docs/examples/*/*/*.py

lint-examples:
poetry run python3 -m py_compile docs/examples/*/*/*.py
35 changes: 35 additions & 0 deletions docs/examples/utilities/parser/parser_envelope.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from aws_lambda_powertools.utilities.parser import BaseModel, envelopes, event_parser, parse
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.EventBridgeEnvelope, 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.EventBridgeEnvelope)
def handler(event: UserModel, context: LambdaContext):
assert event.password1 == event.password2
26 changes: 26 additions & 0 deletions docs/examples/utilities/parser/parser_event_bridge_envelope.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from typing import Any, Dict, Optional, TypeVar, Union

from aws_lambda_powertools.utilities.parser import BaseEnvelope, BaseModel
from aws_lambda_powertools.utilities.parser.models import EventBridgeModel

Model = TypeVar("Model", bound=BaseModel)


class EventBridgeEnvelope(BaseEnvelope):
def parse(self, data: Optional[Union[Dict[str, Any], Any]], model: Model) -> Optional[Model]:
"""Parses data found with model provided

Parameters
----------
data : Dict
Lambda event to be parsed
model : Model
Data model provided to parse after extracting data using envelope

Returns
-------
Any
Parsed detail payload with model provided
"""
parsed_envelope = EventBridgeModel.parse_obj(data)
return self._parse(data=parsed_envelope.detail, model=model)
16 changes: 16 additions & 0 deletions docs/examples/utilities/parser/parser_event_bridge_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from datetime import datetime
from typing import Any, Dict, List

from aws_lambda_powertools.utilities.parser import BaseModel, Field


class EventBridgeModel(BaseModel):
version: str
id: str # noqa: A003,VNE003
source: str
account: str
time: datetime
region: str
resources: List[str]
detail_type: str = Field(None, alias="detail-type")
detail: Dict[str, Any]
44 changes: 44 additions & 0 deletions docs/examples/utilities/parser/parser_event_parser_decorator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import json
from typing import List, Optional

from aws_lambda_powertools.utilities.parser import BaseModel, event_parser
from aws_lambda_powertools.utilities.typing import LambdaContext


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):
print(event.id)
print(event.description)
print(event.items)

order_items = [item for item in event.items]
...


payload = {
"id": 10876546789,
"description": "My order",
"items": [
{
"id": 1015938732,
"quantity": 1,
"description": "item xpto",
},
],
}

handler(event=payload, context=LambdaContext())
handler(event=json.dumps(payload), context=LambdaContext()) # also works if event is a JSON string
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from typing import List, Optional

from aws_lambda_powertools.utilities.parser import BaseModel, parse
from aws_lambda_powertools.utilities.parser.models import EventBridgeModel


class OrderItem(BaseModel):
id: int
quantity: int
description: str


class Order(BaseModel):
id: int
description: str
items: List[OrderItem]


class OrderEventModel(EventBridgeModel):
detail: Order


payload = {
"version": "0",
"id": "6a7e8feb-b491-4cf7-a9f1-bf3703467718",
"detail-type": "OrderPurchased",
"source": "OrderService",
"account": "111122223333",
"time": "2020-10-22T18:43:48Z",
"region": "us-west-1",
"resources": ["some_additional"],
"detail": {
"id": 10876546789,
"description": "My order",
"items": [
{
"id": 1015938732,
"quantity": 1,
"description": "item xpto",
},
],
},
}

ret = parse(model=OrderEventModel, event=payload)

assert ret.source == "OrderService"
assert ret.detail.description == "My order"
assert ret.detail_type == "OrderPurchased" # we rename it to snake_case since detail-type is an invalid name

for order_item in ret.detail.items:
...
32 changes: 32 additions & 0 deletions docs/examples/utilities/parser/parser_model_export.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from aws_lambda_powertools.utilities import Logger
from aws_lambda_powertools.utilities.parser import BaseModel, ValidationError, parse, validator

logger = Logger(service="user")


class UserModel(BaseModel):
username: str
password1: str
password2: str


payload = {
"username": "universe",
"password1": "myp@ssword",
"password2": "repeat password",
}


def my_function():
try:
return parse(model=UserModel, event=payload)
except ValidationError as e:
logger.exception(e.json())
return {"status_code": 400, "message": "Invalid username"}


User: UserModel = my_function()
user_dict = User.dict()
user_json = User.json()
user_json_schema_as_dict = User.schema()
user_json_schema_as_json = User.schema_json(indent=2)
16 changes: 16 additions & 0 deletions docs/examples/utilities/parser/parser_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from typing import List, Optional

from aws_lambda_powertools.utilities.parser import BaseModel


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
39 changes: 39 additions & 0 deletions docs/examples/utilities/parser/parser_parse_function.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from typing import List, Optional

from aws_lambda_powertools.utilities.parser import BaseModel, ValidationError, parse


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


payload = {
"id": 10876546789,
"description": "My order",
"items": [
{
# this will cause a validation error
"id": [1015938732],
"quantity": 1,
"description": "item xpto",
}
],
}


def my_function():
try:
parsed_payload: Order = parse(event=payload, model=Order)
# payload dict is now parsed into our model
return parsed_payload.items
except ValidationError:
return {"status_code": 400, "message": "Invalid order"}
14 changes: 14 additions & 0 deletions docs/examples/utilities/parser/parser_validator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from aws_lambda_powertools.utilities.parser import BaseModel, parse, validator


class HelloWorldModel(BaseModel):
message: str

@validator("message")
def is_hello_world(cls, v):
if v != "hello world":
raise ValueError("Message must be hello world!")
return v


parse(model=HelloWorldModel, event={"message": "hello universe"})
16 changes: 16 additions & 0 deletions docs/examples/utilities/parser/parser_validator_all.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from aws_lambda_powertools.utilities.parser import BaseModel, parse, validator


class HelloWorldModel(BaseModel):
message: str
sender: str

@validator("*")
def has_whitespace(cls, v):
if " " not in v:
raise ValueError("Must have whitespace...")

return v


parse(model=HelloWorldModel, event={"message": "hello universe", "sender": "universe"})
23 changes: 23 additions & 0 deletions docs/examples/utilities/parser/parser_validator_root.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from aws_lambda_powertools.utilities.parser import BaseModel, parse, root_validator


class UserModel(BaseModel):
username: str
password1: str
password2: str

@root_validator
def check_passwords_match(cls, values):
pw1, pw2 = values.get("password1"), values.get("password2")
if pw1 is not None and pw2 is not None and pw1 != pw2:
raise ValueError("passwords do not match")
return values


payload = {
"username": "universe",
"password1": "myp@ssword",
"password2": "repeat password",
}

parse(model=UserModel, event=payload)
Loading