Skip to content

Commit 7dc97b3

Browse files
committed
fix(docs): Extract parser code examples
Changes: - Extract code examples - Run isort and black - Fix python code examples - Update line highlights - Add make task Related to: - aws-powertools#1064
1 parent b577366 commit 7dc97b3

13 files changed

+339
-302
lines changed

Diff for: Makefile

+7
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,10 @@ changelog:
9090

9191
mypy:
9292
poetry run mypy --pretty aws_lambda_powertools
93+
94+
format-examples:
95+
poetry run isort docs/examples
96+
poetry run black docs/examples/*/*/*.py
97+
98+
lint-examples:
99+
poetry run python3 -m py_compile docs/examples/*/*/*.py

Diff for: docs/examples/utilities/parser/parser_envelope.py

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from aws_lambda_powertools.utilities.parser import BaseModel, envelopes, event_parser, parse
2+
from aws_lambda_powertools.utilities.typing import LambdaContext
3+
4+
5+
class UserModel(BaseModel):
6+
username: str
7+
password1: str
8+
password2: str
9+
10+
11+
payload = {
12+
"version": "0",
13+
"id": "6a7e8feb-b491-4cf7-a9f1-bf3703467718",
14+
"detail-type": "CustomerSignedUp",
15+
"source": "CustomerService",
16+
"account": "111122223333",
17+
"time": "2020-10-22T18:43:48Z",
18+
"region": "us-west-1",
19+
"resources": ["some_additional_"],
20+
"detail": {
21+
"username": "universe",
22+
"password1": "myp@ssword",
23+
"password2": "repeat password",
24+
},
25+
}
26+
27+
ret = parse(model=UserModel, envelope=envelopes.EventBridgeEnvelope, event=payload)
28+
29+
# Parsed model only contains our actual model, not the entire EventBridge + Payload parsed
30+
assert ret.password1 == ret.password2
31+
32+
# Same behaviour but using our decorator
33+
@event_parser(model=UserModel, envelope=envelopes.EventBridgeEnvelope)
34+
def handler(event: UserModel, context: LambdaContext):
35+
assert event.password1 == event.password2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from typing import Any, Dict, Optional, TypeVar, Union
2+
3+
from aws_lambda_powertools.utilities.parser import BaseEnvelope, BaseModel
4+
from aws_lambda_powertools.utilities.parser.models import EventBridgeModel
5+
6+
Model = TypeVar("Model", bound=BaseModel)
7+
8+
9+
class EventBridgeEnvelope(BaseEnvelope):
10+
def parse(self, data: Optional[Union[Dict[str, Any], Any]], model: Model) -> Optional[Model]:
11+
"""Parses data found with model provided
12+
13+
Parameters
14+
----------
15+
data : Dict
16+
Lambda event to be parsed
17+
model : Model
18+
Data model provided to parse after extracting data using envelope
19+
20+
Returns
21+
-------
22+
Any
23+
Parsed detail payload with model provided
24+
"""
25+
parsed_envelope = EventBridgeModel.parse_obj(data)
26+
return self._parse(data=parsed_envelope.detail, model=model)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from datetime import datetime
2+
from typing import Any, Dict, List
3+
4+
from aws_lambda_powertools.utilities.parser import BaseModel, Field
5+
6+
7+
class EventBridgeModel(BaseModel):
8+
version: str
9+
id: str # noqa: A003,VNE003
10+
source: str
11+
account: str
12+
time: datetime
13+
region: str
14+
resources: List[str]
15+
detail_type: str = Field(None, alias="detail-type")
16+
detail: Dict[str, Any]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import json
2+
from typing import List, Optional
3+
4+
from aws_lambda_powertools.utilities.parser import BaseModel, event_parser
5+
from aws_lambda_powertools.utilities.typing import LambdaContext
6+
7+
8+
class OrderItem(BaseModel):
9+
id: int
10+
quantity: int
11+
description: str
12+
13+
14+
class Order(BaseModel):
15+
id: int
16+
description: str
17+
items: List[OrderItem] # nesting models are supported
18+
optional_field: Optional[str] # this field may or may not be available when parsing
19+
20+
21+
@event_parser(model=Order)
22+
def handler(event: Order, context: LambdaContext):
23+
print(event.id)
24+
print(event.description)
25+
print(event.items)
26+
27+
order_items = [item for item in event.items]
28+
...
29+
30+
31+
payload = {
32+
"id": 10876546789,
33+
"description": "My order",
34+
"items": [
35+
{
36+
"id": 1015938732,
37+
"quantity": 1,
38+
"description": "item xpto",
39+
},
40+
],
41+
}
42+
43+
handler(event=payload, context=LambdaContext())
44+
handler(event=json.dumps(payload), context=LambdaContext()) # also works if event is a JSON string
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from typing import List, Optional
2+
3+
from aws_lambda_powertools.utilities.parser import BaseModel, parse
4+
from aws_lambda_powertools.utilities.parser.models import EventBridgeModel
5+
6+
7+
class OrderItem(BaseModel):
8+
id: int
9+
quantity: int
10+
description: str
11+
12+
13+
class Order(BaseModel):
14+
id: int
15+
description: str
16+
items: List[OrderItem]
17+
18+
19+
class OrderEventModel(EventBridgeModel):
20+
detail: Order
21+
22+
23+
payload = {
24+
"version": "0",
25+
"id": "6a7e8feb-b491-4cf7-a9f1-bf3703467718",
26+
"detail-type": "OrderPurchased",
27+
"source": "OrderService",
28+
"account": "111122223333",
29+
"time": "2020-10-22T18:43:48Z",
30+
"region": "us-west-1",
31+
"resources": ["some_additional"],
32+
"detail": {
33+
"id": 10876546789,
34+
"description": "My order",
35+
"items": [
36+
{
37+
"id": 1015938732,
38+
"quantity": 1,
39+
"description": "item xpto",
40+
},
41+
],
42+
},
43+
}
44+
45+
ret = parse(model=OrderEventModel, event=payload)
46+
47+
assert ret.source == "OrderService"
48+
assert ret.detail.description == "My order"
49+
assert ret.detail_type == "OrderPurchased" # we rename it to snake_case since detail-type is an invalid name
50+
51+
for order_item in ret.detail.items:
52+
...
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from aws_lambda_powertools.utilities import Logger
2+
from aws_lambda_powertools.utilities.parser import BaseModel, ValidationError, parse, validator
3+
4+
logger = Logger(service="user")
5+
6+
7+
class UserModel(BaseModel):
8+
username: str
9+
password1: str
10+
password2: str
11+
12+
13+
payload = {
14+
"username": "universe",
15+
"password1": "myp@ssword",
16+
"password2": "repeat password",
17+
}
18+
19+
20+
def my_function():
21+
try:
22+
return parse(model=UserModel, event=payload)
23+
except ValidationError as e:
24+
logger.exception(e.json())
25+
return {"status_code": 400, "message": "Invalid username"}
26+
27+
28+
User: UserModel = my_function()
29+
user_dict = User.dict()
30+
user_json = User.json()
31+
user_json_schema_as_dict = User.schema()
32+
user_json_schema_as_json = User.schema_json(indent=2)

Diff for: docs/examples/utilities/parser/parser_models.py

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from typing import List, Optional
2+
3+
from aws_lambda_powertools.utilities.parser import BaseModel
4+
5+
6+
class OrderItem(BaseModel):
7+
id: int
8+
quantity: int
9+
description: str
10+
11+
12+
class Order(BaseModel):
13+
id: int
14+
description: str
15+
items: List[OrderItem] # nesting models are supported
16+
optional_field: Optional[str] # this field may or may not be available when parsing
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from typing import List, Optional
2+
3+
from aws_lambda_powertools.utilities.parser import BaseModel, ValidationError, parse
4+
5+
6+
class OrderItem(BaseModel):
7+
id: int
8+
quantity: int
9+
description: str
10+
11+
12+
class Order(BaseModel):
13+
id: int
14+
description: str
15+
items: List[OrderItem] # nesting models are supported
16+
optional_field: Optional[str] # this field may or may not be available when parsing
17+
18+
19+
payload = {
20+
"id": 10876546789,
21+
"description": "My order",
22+
"items": [
23+
{
24+
# this will cause a validation error
25+
"id": [1015938732],
26+
"quantity": 1,
27+
"description": "item xpto",
28+
}
29+
],
30+
}
31+
32+
33+
def my_function():
34+
try:
35+
parsed_payload: Order = parse(event=payload, model=Order)
36+
# payload dict is now parsed into our model
37+
return parsed_payload.items
38+
except ValidationError:
39+
return {"status_code": 400, "message": "Invalid order"}

Diff for: docs/examples/utilities/parser/parser_validator.py

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from aws_lambda_powertools.utilities.parser import BaseModel, parse, validator
2+
3+
4+
class HelloWorldModel(BaseModel):
5+
message: str
6+
7+
@validator("message")
8+
def is_hello_world(cls, v):
9+
if v != "hello world":
10+
raise ValueError("Message must be hello world!")
11+
return v
12+
13+
14+
parse(model=HelloWorldModel, event={"message": "hello universe"})
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from aws_lambda_powertools.utilities.parser import BaseModel, parse, validator
2+
3+
4+
class HelloWorldModel(BaseModel):
5+
message: str
6+
sender: str
7+
8+
@validator("*")
9+
def has_whitespace(cls, v):
10+
if " " not in v:
11+
raise ValueError("Must have whitespace...")
12+
13+
return v
14+
15+
16+
parse(model=HelloWorldModel, event={"message": "hello universe", "sender": "universe"})
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from aws_lambda_powertools.utilities.parser import BaseModel, parse, root_validator
2+
3+
4+
class UserModel(BaseModel):
5+
username: str
6+
password1: str
7+
password2: str
8+
9+
@root_validator
10+
def check_passwords_match(cls, values):
11+
pw1, pw2 = values.get("password1"), values.get("password2")
12+
if pw1 is not None and pw2 is not None and pw1 != pw2:
13+
raise ValueError("passwords do not match")
14+
return values
15+
16+
17+
payload = {
18+
"username": "universe",
19+
"password1": "myp@ssword",
20+
"password2": "repeat password",
21+
}
22+
23+
parse(model=UserModel, event=payload)

0 commit comments

Comments
 (0)