diff --git a/docs/utilities/parser.md b/docs/utilities/parser.md index e97395ae56c..cdcb949d28a 100644 --- a/docs/utilities/parser.md +++ b/docs/utilities/parser.md @@ -171,7 +171,7 @@ Parser comes with the following built-in models: | **KafkaSelfManagedEventModel** | Lambda Event Source payload for self managed Kafka payload | | **KafkaMskEventModel** | Lambda Event Source payload for AWS MSK payload | -### extending built-in models +### Extending built-in models You can extend them to include your own models, and yet have all other known fields parsed along the way. @@ -236,6 +236,20 @@ for order_item in ret.detail.items: 3. Defined how part of our EventBridge event should look like by overriding `detail` key within our `OrderEventModel` 4. Parser parsed the original event against `OrderEventModel` +???+ tip + When extending a `string` field containing JSON, you need to wrap the field + with [Pydantic's Json Type](https://pydantic-docs.helpmanual.io/usage/types/#json-type): + + ```python hl_lines="14 18-19" + --8<-- "examples/parser/src/extending_built_in_models_with_json_mypy.py" + ``` + + Alternatively, you could use a [Pydantic validator](https://pydantic-docs.helpmanual.io/usage/validators/) to transform the JSON string into a dict before the mapping: + + ```python hl_lines="18-20 24-25" + --8<-- "examples/parser/src/extending_built_in_models_with_json_validator.py" + ``` + ## Envelopes When trying to parse your payloads wrapped in a known structure, you might encounter the following situations: diff --git a/examples/parser/src/extending_built_in_models_with_json_mypy.py b/examples/parser/src/extending_built_in_models_with_json_mypy.py new file mode 100644 index 00000000000..43e04143347 --- /dev/null +++ b/examples/parser/src/extending_built_in_models_with_json_mypy.py @@ -0,0 +1,21 @@ +from pydantic import BaseModel, Json + +from aws_lambda_powertools.utilities.parser import event_parser +from aws_lambda_powertools.utilities.parser.models import APIGatewayProxyEventV2Model +from aws_lambda_powertools.utilities.typing import LambdaContext + + +class CancelOrder(BaseModel): + order_id: int + reason: str + + +class CancelOrderModel(APIGatewayProxyEventV2Model): + body: Json[CancelOrder] # type: ignore[assignment] + + +@event_parser(model=CancelOrderModel) +def handler(event: CancelOrderModel, context: LambdaContext): + cancel_order: CancelOrder = event.body # type: ignore[assignment] + + assert cancel_order.order_id is not None diff --git a/examples/parser/src/extending_built_in_models_with_json_validator.py b/examples/parser/src/extending_built_in_models_with_json_validator.py new file mode 100644 index 00000000000..acd4f3fc825 --- /dev/null +++ b/examples/parser/src/extending_built_in_models_with_json_validator.py @@ -0,0 +1,27 @@ +import json + +from pydantic import BaseModel, validator + +from aws_lambda_powertools.utilities.parser import event_parser +from aws_lambda_powertools.utilities.parser.models import APIGatewayProxyEventV2Model +from aws_lambda_powertools.utilities.typing import LambdaContext + + +class CancelOrder(BaseModel): + order_id: int + reason: str + + +class CancelOrderModel(APIGatewayProxyEventV2Model): + body: CancelOrder # type: ignore[assignment] + + @validator("body", pre=True) + def transform_body_to_dict(cls, value: str): + return json.loads(value) + + +@event_parser(model=CancelOrderModel) +def handler(event: CancelOrderModel, context: LambdaContext): + cancel_order: CancelOrder = event.body + + assert cancel_order.order_id is not None