Skip to content

Commit 75dc529

Browse files
committed
docs: add envelope section
1 parent 0ed746b commit 75dc529

File tree

6 files changed

+127
-10
lines changed

6 files changed

+127
-10
lines changed

aws_lambda_powertools/utilities/parser/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44
from .envelopes import BaseEnvelope
55
from .exceptions import ModelValidationError
66
from .parser import event_parser, parse
7-
from .pydantic import BaseModel, root_validator, validator
7+
from .pydantic import BaseModel, Field, root_validator, validator
88

99
__all__ = [
1010
"event_parser",
1111
"parse",
1212
"envelopes",
1313
"BaseEnvelope",
1414
"BaseModel",
15+
"Field",
1516
"validator",
1617
"root_validator",
1718
"ModelValidationError",
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from .base import BaseEnvelope
2-
from .dynamodb import DynamoDBEnvelope
2+
from .dynamodb import DynamoDBStreamEnvelope
33
from .event_bridge import EventBridgeEnvelope
44
from .sqs import SqsEnvelope
55

6-
__all__ = ["DynamoDBEnvelope", "EventBridgeEnvelope", "SqsEnvelope", "BaseEnvelope"]
6+
__all__ = ["DynamoDBStreamEnvelope", "EventBridgeEnvelope", "SqsEnvelope", "BaseEnvelope"]

aws_lambda_powertools/utilities/parser/envelopes/dynamodb.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
logger = logging.getLogger(__name__)
1111

1212

13-
class DynamoDBEnvelope(BaseEnvelope):
13+
class DynamoDBStreamEnvelope(BaseEnvelope):
1414
""" DynamoDB Stream Envelope to extract data within NewImage/OldImage
1515
1616
Note: Values are the parsed models. Images' values can also be None, and
@@ -31,6 +31,8 @@ def parse(self, data: Dict[str, Any], model: BaseModel) -> List[Dict[Literal["Ne
3131
-------
3232
List
3333
List of records parsed with model provided
34+
35+
3436
"""
3537
parsed_envelope = DynamoDBStreamModel(**data)
3638
output = []

aws_lambda_powertools/utilities/parser/envelopes/sqs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class SqsEnvelope(BaseEnvelope):
1919
all items in the list will be parsed as str and npt as JSON (and vice versa)
2020
"""
2121

22-
def parse(self, data: Dict[str, Any], model: Union[BaseModel, str]) -> List[Union[BaseModel, str]]:
22+
def parse(self, data: Dict[str, Any], model: Union[BaseModel, str]) -> List[BaseModel]:
2323
"""Parses records found with model provided
2424
2525
Parameters

docs/content/utilities/parser.mdx

Lines changed: 118 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ These are simply Python classes that inherit from BaseModel. **Parser** enforces
5757
Use Koudai Aono's <a href="https://github.com/koxudaxi/datamodel-code-generator">data model code generation tool for Pydantic</a>
5858
</Note><br/>
5959

60+
### Extending built-in models
61+
62+
**TBW**
63+
6064
## Parsing events
6165

6266
You can parse inbound events using **event_parser** decorator, or the standalone `parse` function. Both are also able to parse either dictionary or JSON string as an input.
@@ -226,10 +230,120 @@ parse(model=UserModel, event=payload)
226230
</Note><br/>
227231

228232

229-
## Built-in envelopes
233+
## Envelopes
230234

231-
**TBW**
235+
Envelope parameter is useful when your actual payload is wrapped around a known structure, for example Lambda Event Sources like Amazon EventBridge.
232236

233-
## Extending built-in models
237+
Example of parsing a model found in an event coming from EventBridge, where all you want is what's inside the `detail` key.
234238

235-
**TBW**
239+
```python:title=parse_eventbridge_payload.py
240+
from aws_lambda_powertools.utilities.parser import parse, BaseModel, envelopes
241+
242+
class UserModel(BaseModel):
243+
username: str
244+
password1: str
245+
password2: str
246+
247+
payload = {
248+
"version": "0",
249+
"id": "6a7e8feb-b491-4cf7-a9f1-bf3703467718",
250+
"detail-type": "CustomerSignedUp",
251+
"source": "CustomerService",
252+
"account": "111122223333",
253+
"time": "2020-10-22T18:43:48Z",
254+
"region": "us-west-1",
255+
"resources": ["some_additional_"],
256+
# highlight-start
257+
"detail": {
258+
"username": "universe",
259+
"password1": "myp@ssword",
260+
"password2": "repeat password"
261+
}
262+
# highlight-end
263+
}
264+
265+
ret = parse(model=UserModel, envelope=envelopes.EventBridgeModel, payload=payload) # highlight-line
266+
267+
# Parsed model only contains our actual model, not the entire EventBridge + Payload parsed
268+
assert ret.password1 == ret.password2
269+
```
270+
271+
**What's going on here, you might ask**:
272+
273+
1. We imported built-in `models` from the parser utility
274+
2. Used `envelopes.EventBridgeModel` as the envelope for our `UserModel` model
275+
3. Parser parsed the original event against the EventBridge model
276+
4 Parser then parsed the `detail` key using `UserModel`
277+
278+
### Built-in envelopes
279+
280+
Parser comes with the following built-in envelopes, where `BaseModel` in the return section is your given model.
281+
282+
Envelope name | Behaviour | Return
283+
------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | ------------------------------------
284+
**DynamoDBStreamEnvelope** | 1. Parses data using `DynamoDBStreamModel`. <br/> 2. Parses records in `NewImage` and `OldImage` keys using your model. <br/> 3. Returns a list with a dictionary containing `NewImage` and `OldImage` keys | `List[Dict[Literal["NewImage", "OldImage"], BaseModel]]`
285+
**EventBridgeEnvelope** | 1. Parses data using `EventBridgeModel`. <br/> 2. Parses `detail` key using your model and returns it. | `BaseModel`
286+
**SqsEnvelope** | 1. Parses data using `SqsModel`. <br/> 2. Parses records in `body` key using your model and return them in a list. | `List[BaseModel]`
287+
288+
### Bringing your own envelope model
289+
290+
You can create your own Envelope model and logic by inheriting from `BaseEnvelope`, and implementing the `parse` method.
291+
292+
Here's an snippet of how the EventBridge Envelope we demonstrated previously is implemented.
293+
294+
**EventBridge Model**
295+
296+
```python:title=eventbridge_model.py
297+
from datetime import datetime
298+
from typing import Any, Dict, List
299+
300+
from aws_lambda_powertools.utilities.parser import BaseModel, Field
301+
302+
303+
class EventBridgeModel(BaseModel):
304+
version: str
305+
id: str # noqa: A003,VNE003
306+
source: str
307+
account: str
308+
time: datetime
309+
region: str
310+
resources: List[str]
311+
detail_type: str = Field(None, alias="detail-type")
312+
detail: Dict[str, Any]
313+
```
314+
315+
**EventBridge Envelope**
316+
317+
```python:title=eventbridge_envelope.py
318+
from aws_lambda_powertools.utilities.parser import BaseEnvelope, BaseModel
319+
from typing import Any, Dict
320+
from ..models import EventBridgeModel
321+
322+
class EventBridgeEnvelope(BaseEnvelope): # highlight-line
323+
324+
def parse(self, data: Dict[str, Any], model: BaseModel) -> BaseModel: # highlight-line
325+
"""Parses data found with model provided
326+
327+
Parameters
328+
----------
329+
data : Dict
330+
Lambda event to be parsed
331+
model : BaseModel
332+
Data model provided to parse after extracting data using envelope
333+
334+
Returns
335+
-------
336+
Any
337+
Parsed detail payload with model provided
338+
"""
339+
parsed_envelope = EventBridgeModel(**data) # highlight-line
340+
return self._parse(data=parsed_envelope.detail, model=model) # highlight-line
341+
```
342+
343+
**What's going on here, you might ask**:
344+
345+
1. We defined an envelope named `EventBridgeEnvelope` inheriting from `BaseEnvelope`
346+
2. Implemented the `parse` abstract method taking `data` and `model` as parameters
347+
3. Then, we parsed the incoming data with our envelope model (EventBridgeModel)
348+
3.1. This confirms that data received conforms with how EventBridge wraps events
349+
4. Lastly, we call `_parse` from `BaseEnvelope` to parse the data in our envelope (.detail) using the customer model

tests/functional/parser/test_dynamodb.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from tests.functional.parser.utils import load_event
99

1010

11-
@event_parser(model=MyDynamoBusiness, envelope=envelopes.DynamoDBEnvelope)
11+
@event_parser(model=MyDynamoBusiness, envelope=envelopes.DynamoDBStreamEnvelope)
1212
def handle_dynamodb(event: List[Dict[str, MyDynamoBusiness]], _: LambdaContext):
1313
assert len(event) == 2
1414
assert event[0]["OldImage"] is None

0 commit comments

Comments
 (0)