From 8b59bdb8bab197b2989f84e35ed5e935f0b2d6cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAben=20Fonseca?= Date: Tue, 20 Sep 2022 15:57:10 +0200 Subject: [PATCH 1/5] docs(parser): add JSON string field extension example --- docs/utilities/parser.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/utilities/parser.md b/docs/utilities/parser.md index e97395ae56c..d4e292f14a8 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,19 @@ 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 + from pydantic import Json + + ... + + class OrderEventModel(APIGatewayProxyEventV2Model): + body: Json[Order] + ``` + ## Envelopes When trying to parse your payloads wrapped in a known structure, you might encounter the following situations: From e6fbae80e42b52f7e621f19b9ba45ca609f71892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BAben=20Fonseca?= Date: Thu, 22 Sep 2022 11:39:25 +0200 Subject: [PATCH 2/5] fix(docs): extracted examples --- docs/utilities/parser.md | 11 ++++---- ...xtending_built_in_models_with_json_mypy.py | 21 +++++++++++++++ ...ing_built_in_models_with_json_validator.py | 27 +++++++++++++++++++ 3 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 examples/parser/src/extending_built_in_models_with_json_mypy.py create mode 100644 examples/parser/src/extending_built_in_models_with_json_validator.py diff --git a/docs/utilities/parser.md b/docs/utilities/parser.md index d4e292f14a8..cdcb949d28a 100644 --- a/docs/utilities/parser.md +++ b/docs/utilities/parser.md @@ -240,13 +240,14 @@ for order_item in ret.detail.items: 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 - from pydantic import Json + ```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: - class OrderEventModel(APIGatewayProxyEventV2Model): - body: Json[Order] + ```python hl_lines="18-20 24-25" + --8<-- "examples/parser/src/extending_built_in_models_with_json_validator.py" ``` ## Envelopes 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..e325ac0b91e --- /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 + + +@event_parser(model=CancelOrderModel) +def handler(event: CancelOrderModel, context: LambdaContext): + cancel_order: CancelOrder = event.body # type: ignore + + 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..cde6c450b47 --- /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 + + @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 From ec6a6f84403617488a0be003068e2f2ae09f6c28 Mon Sep 17 00:00:00 2001 From: Heitor Lessa Date: Fri, 23 Sep 2022 12:05:02 +0200 Subject: [PATCH 3/5] chore: ignore assignment error only in mypy --- examples/parser/src/extending_built_in_models_with_json_mypy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index e325ac0b91e..0268e0d4477 100644 --- a/examples/parser/src/extending_built_in_models_with_json_mypy.py +++ b/examples/parser/src/extending_built_in_models_with_json_mypy.py @@ -11,7 +11,7 @@ class CancelOrder(BaseModel): class CancelOrderModel(APIGatewayProxyEventV2Model): - body: Json[CancelOrder] # type: ignore + body: Json[CancelOrder] # type: ignore[assignment] @event_parser(model=CancelOrderModel) From 8906200f14540c2b6aa4cb57592e8c45080d398b Mon Sep 17 00:00:00 2001 From: Heitor Lessa Date: Fri, 23 Sep 2022 12:05:11 +0200 Subject: [PATCH 4/5] chore: ignore assignment error only in mypy --- .../parser/src/extending_built_in_models_with_json_validator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index cde6c450b47..acd4f3fc825 100644 --- a/examples/parser/src/extending_built_in_models_with_json_validator.py +++ b/examples/parser/src/extending_built_in_models_with_json_validator.py @@ -13,7 +13,7 @@ class CancelOrder(BaseModel): class CancelOrderModel(APIGatewayProxyEventV2Model): - body: CancelOrder # type: ignore + body: CancelOrder # type: ignore[assignment] @validator("body", pre=True) def transform_body_to_dict(cls, value: str): From 120a33eb32a770babae9c9dd7a64d23aa3ebc218 Mon Sep 17 00:00:00 2001 From: Heitor Lessa Date: Fri, 23 Sep 2022 12:05:16 +0200 Subject: [PATCH 5/5] chore: ignore assignment error only in mypy --- examples/parser/src/extending_built_in_models_with_json_mypy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 0268e0d4477..43e04143347 100644 --- a/examples/parser/src/extending_built_in_models_with_json_mypy.py +++ b/examples/parser/src/extending_built_in_models_with_json_mypy.py @@ -16,6 +16,6 @@ class CancelOrderModel(APIGatewayProxyEventV2Model): @event_parser(model=CancelOrderModel) def handler(event: CancelOrderModel, context: LambdaContext): - cancel_order: CancelOrder = event.body # type: ignore + cancel_order: CancelOrder = event.body # type: ignore[assignment] assert cancel_order.order_id is not None