From 4ef42983a742c828693dbbb9c34cec9c9c2ab6ba Mon Sep 17 00:00:00 2001 From: Gabriel Guarisa Date: Mon, 3 Jun 2024 21:20:05 -0300 Subject: [PATCH 1/2] Set empty strings to null in metadata fields --- pyproject.toml | 4 ++-- retrack/nodes/base.py | 11 +++++++++++ retrack/nodes/inputs.py | 3 ++- retrack/nodes/outputs.py | 9 +++++++-- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a02ecbe..1037366 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,8 +1,8 @@ [tool.poetry] name = "retrack" -version = "2.5.0" +version = "2.5.1" description = "A business rules engine" -authors = ["Gabriel Guarisa ", "Nathalia Trotte "] +authors = ["Gabriel Guarisa "] license = "MIT" readme = "README.md" repository = "https://github.com/pier-digital/retrack" diff --git a/retrack/nodes/base.py b/retrack/nodes/base.py index 0ef84d2..215b526 100644 --- a/retrack/nodes/base.py +++ b/retrack/nodes/base.py @@ -44,6 +44,17 @@ def cast_int_to_str(v: typing.Any, info: pydantic.ValidationInfo) -> str: ] +def cast_empty_string_to_none(v: str, info: pydantic.ValidationInfo) -> typing.Any: + if v == "": + return None + return v + + +CastedToNoneStringType = typing.Annotated[ + str, pydantic.BeforeValidator(cast_empty_string_to_none) +] + + class OutputConnectionItemModel(pydantic.BaseModel): node: CastedToStringType input_: str = pydantic.Field(alias="input") diff --git a/retrack/nodes/inputs.py b/retrack/nodes/inputs.py index 72db14f..48dc244 100644 --- a/retrack/nodes/inputs.py +++ b/retrack/nodes/inputs.py @@ -7,6 +7,7 @@ InputConnectionModel, NodeKind, OutputConnectionModel, + CastedToNoneStringType, ) ################################################ @@ -16,7 +17,7 @@ class InputMetadataModel(pydantic.BaseModel): name: str - default: typing.Optional[str] = None + default: typing.Optional[CastedToNoneStringType] = None ################################################ diff --git a/retrack/nodes/outputs.py b/retrack/nodes/outputs.py index 9aee712..792a518 100644 --- a/retrack/nodes/outputs.py +++ b/retrack/nodes/outputs.py @@ -3,7 +3,12 @@ import pandas as pd import pydantic -from retrack.nodes.base import BaseNode, InputConnectionModel, NodeKind +from retrack.nodes.base import ( + BaseNode, + InputConnectionModel, + NodeKind, + CastedToNoneStringType, +) from retrack.utils import constants ################################################ @@ -12,7 +17,7 @@ class OutputMetadataModel(pydantic.BaseModel): - message: typing.Optional[str] = None + message: typing.Optional[CastedToNoneStringType] = None ################################################ From 62bd7ede76e2ea873ac6632fac846ccfea08536a Mon Sep 17 00:00:00 2001 From: Gabriel Guarisa Date: Tue, 4 Jun 2024 09:13:01 -0300 Subject: [PATCH 2/2] Update type annotations for metadata fields to use Optional[str] instead of CastedToNoneStringType --- retrack/nodes/base.py | 4 ++-- retrack/nodes/inputs.py | 4 ++-- retrack/nodes/outputs.py | 4 ++-- tests/conftest.py | 2 +- tests/test_nodes/test_input.py | 7 +++++++ 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/retrack/nodes/base.py b/retrack/nodes/base.py index 215b526..680039f 100644 --- a/retrack/nodes/base.py +++ b/retrack/nodes/base.py @@ -50,8 +50,8 @@ def cast_empty_string_to_none(v: str, info: pydantic.ValidationInfo) -> typing.A return v -CastedToNoneStringType = typing.Annotated[ - str, pydantic.BeforeValidator(cast_empty_string_to_none) +OptionalCastedToNoneStringType = typing.Annotated[ + typing.Optional[str], pydantic.BeforeValidator(cast_empty_string_to_none) ] diff --git a/retrack/nodes/inputs.py b/retrack/nodes/inputs.py index 48dc244..e0db7ff 100644 --- a/retrack/nodes/inputs.py +++ b/retrack/nodes/inputs.py @@ -7,7 +7,7 @@ InputConnectionModel, NodeKind, OutputConnectionModel, - CastedToNoneStringType, + OptionalCastedToNoneStringType, ) ################################################ @@ -17,7 +17,7 @@ class InputMetadataModel(pydantic.BaseModel): name: str - default: typing.Optional[CastedToNoneStringType] = None + default: OptionalCastedToNoneStringType = None ################################################ diff --git a/retrack/nodes/outputs.py b/retrack/nodes/outputs.py index 792a518..5bf75dc 100644 --- a/retrack/nodes/outputs.py +++ b/retrack/nodes/outputs.py @@ -7,7 +7,7 @@ BaseNode, InputConnectionModel, NodeKind, - CastedToNoneStringType, + OptionalCastedToNoneStringType, ) from retrack.utils import constants @@ -17,7 +17,7 @@ class OutputMetadataModel(pydantic.BaseModel): - message: typing.Optional[CastedToNoneStringType] = None + message: OptionalCastedToNoneStringType = None ################################################ diff --git a/tests/conftest.py b/tests/conftest.py index 04827ad..c5e3d8e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,7 @@ import pytest -@pytest.fixture +@pytest.fixture(scope="function") def valid_input_dict_before_validation() -> dict: return { "id": 1, diff --git a/tests/test_nodes/test_input.py b/tests/test_nodes/test_input.py index 7a44c38..d1a770c 100644 --- a/tests/test_nodes/test_input.py +++ b/tests/test_nodes/test_input.py @@ -7,3 +7,10 @@ def test_input_node( input_node = Input(**valid_input_dict_before_validation) assert input_node.model_dump(by_alias=True) == valid_input_dict_after_validation + + +def test_input_with_empty_string_as_default(valid_input_dict_before_validation): + valid_input_dict_before_validation["data"]["default"] = "" + input_node = Input(**valid_input_dict_before_validation) + + assert input_node.data.default is None