Skip to content

Commit

Permalink
fix(docs): Extract parser code examples
Browse files Browse the repository at this point in the history
Changes:
- Extract code examples
- Run isort and black
- Fix python code examples
- Update line highlights
- Add make task

Related to:
- aws-powertools#1064
  • Loading branch information
michaelbrewer committed Apr 13, 2022
1 parent b577366 commit 7dc97b3
Show file tree
Hide file tree
Showing 13 changed files with 339 additions and 302 deletions.
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
52 changes: 52 additions & 0 deletions docs/examples/utilities/parser/parser_extending_builtin_models.py
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

0 comments on commit 7dc97b3

Please sign in to comment.