diff --git a/CHAGELOG.md b/CHAGELOG.md index 784491d..c1414a9 100644 --- a/CHAGELOG.md +++ b/CHAGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. +# [0.0.8] - 2025-01-16 + +## Added + +- New functionality to define steps for a field to have more control over the + order of the validation and filtering process. + + # [0.0.7.1] - 2025-01-16 ## Changed diff --git a/flask_inputfilter/InputFilter.py b/flask_inputfilter/InputFilter.py index a854d96..d1fd1cb 100644 --- a/flask_inputfilter/InputFilter.py +++ b/flask_inputfilter/InputFilter.py @@ -44,7 +44,8 @@ def add( or field None, although it is required . :param filters: The filters to apply to the field value. :param validators: The validators to apply to the field value. - :param steps: + :param steps: Allows to apply multiple filters and validators + in a specific order. :param external_api: Configuration for an external API call. """ @@ -89,12 +90,14 @@ def __applyFilters(self, field_name: str, value: Any) -> Any: field = self.fields.get(field_name) - for filter_ in field["filters"]: + for filter_ in field.get("filters"): value = filter_.apply(value) return value - def __validateField(self, field_name: str, field_info, value: Any) -> None: + def __validateField( + self, field_name: str, field_info: Any, value: Any + ) -> None: """ Validate the field value. """ @@ -108,7 +111,7 @@ def __validateField(self, field_name: str, field_info, value: Any) -> None: field = self.fields.get(field_name) - for validator in field["validators"]: + for validator in field.get("validators"): validator.validate(value) except ValidationError: if field_info.get("fallback") is None: @@ -116,8 +119,30 @@ def __validateField(self, field_name: str, field_info, value: Any) -> None: return field_info.get("fallback") + def __applySteps( + self, field_name: str, field_info: Any, value: Any + ) -> Any: + """ + Apply multiple filters and validators in a specific order. + """ + + field = self.fields.get(field_name) + + try: + for step in field.get("steps"): + if isinstance(step, BaseFilter): + value = step.apply(value) + elif isinstance(step, BaseValidator): + step.validate(value) + except ValidationError: + if field_info.get("fallback") is None: + raise + return field_info.get("fallback") + + return value + def __callExternalApi( - self, field_info, validated_data: dict + self, field_info: Any, validated_data: dict ) -> Optional[Any]: """ Führt den API-Aufruf durch und gibt den Wert zurück, @@ -255,6 +280,8 @@ def validateData( self.__validateField(field_name, field_info, value) or value ) + value = self.__applySteps(field_name, field_info, value) + if field_info.get("external_api"): value = self.__callExternalApi(field_info, validated_data) diff --git a/setup.py b/setup.py index ba1354f..d50544f 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name="flask_inputfilter", - version="0.0.7.2", + version="0.0.8", author="Leander Cain Slotosch", author_email="slotosch.leander@outlook.de", description="A library to filter and validate input data in" diff --git a/test/test_input_filter.py b/test/test_input_filter.py index 1c36e58..4fc78d1 100644 --- a/test/test_input_filter.py +++ b/test/test_input_filter.py @@ -6,7 +6,7 @@ from flask_inputfilter import InputFilter from flask_inputfilter.Condition import BaseCondition from flask_inputfilter.Exception import ValidationError -from flask_inputfilter.Filter import ToUpperFilter +from flask_inputfilter.Filter import ToLowerFilter, ToUpperFilter from flask_inputfilter.Model import ExternalApiConfig from flask_inputfilter.Validator import ( InArrayValidator, @@ -242,6 +242,31 @@ def test_fallback_with_default(self) -> None: with self.assertRaises(ValidationError): self.inputFilter.validateData({}) + def test_steps(self) -> None: + """ + Test that custom steps works. + """ + + self.inputFilter.add( + "name_upper", + steps=[ + ToUpperFilter(), + InArrayValidator(["MAURICE"]), + ToLowerFilter(), + ], + ) + + validated_data = self.inputFilter.validateData( + {"name_upper": "Maurice"} + ) + self.assertEqual(validated_data["name_upper"], "maurice") + + with self.assertRaises(ValidationError): + validated_data = self.inputFilter.validateData( + {"name_upper": "Alice"} + ) + self.assertEqual(validated_data["name_upper"], "ALICE") + @patch("requests.request") def test_external_api(self, mock_request: Mock) -> None: """