Skip to content

Commit 6c8e797

Browse files
committed
13 | Refactor
1 parent 2f4b22c commit 6c8e797

File tree

10 files changed

+148
-130
lines changed

10 files changed

+148
-130
lines changed

.DS_Store

-8 KB
Binary file not shown.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,3 +156,4 @@ cython_debug/
156156

157157
.idea/
158158
.vscode/
159+
.DS_Store

flask_inputfilter/InputFilter.py

Lines changed: 81 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ def add(
3131
fallback: Any = None,
3232
filters: Optional[List[BaseFilter]] = None,
3333
validators: Optional[List[BaseValidator]] = None,
34+
steps: Optional[List[Union[BaseFilter, BaseValidator]]] = None,
3435
external_api: Optional[ExternalApiConfig] = None,
3536
) -> None:
3637
"""
@@ -43,6 +44,7 @@ def add(
4344
or field None, although it is required .
4445
:param filters: The filters to apply to the field value.
4546
:param validators: The validators to apply to the field value.
47+
:param steps:
4648
:param external_api: Configuration for an external API call.
4749
"""
4850

@@ -52,6 +54,7 @@ def add(
5254
"fallback": fallback,
5355
"filters": filters or [],
5456
"validators": validators or [],
57+
"steps": steps or [],
5558
"external_api": external_api,
5659
}
5760

@@ -73,11 +76,14 @@ def addGlobalValidator(self, validator: BaseValidator) -> None:
7376
"""
7477
self.global_validators.append(validator)
7578

76-
def _applyFilters(self, field_name: str, value: Any) -> Any:
79+
def __applyFilters(self, field_name: str, value: Any) -> Any:
7780
"""
7881
Apply filters to the field value.
7982
"""
8083

84+
if value is None:
85+
return value
86+
8187
for filter_ in self.global_filters:
8288
value = filter_.apply(value)
8389

@@ -88,27 +94,38 @@ def _applyFilters(self, field_name: str, value: Any) -> Any:
8894

8995
return value
9096

91-
def _validateField(self, field_name: str, value: Any) -> None:
97+
def __validateField(self, field_name: str, field_info, value: Any) -> None:
9298
"""
9399
Validate the field value.
94100
"""
95101

96-
for validator in self.global_validators:
97-
validator.validate(value)
102+
if value is None:
103+
return
98104

99-
field = self.fields.get(field_name)
105+
try:
106+
for validator in self.global_validators:
107+
validator.validate(value)
108+
109+
field = self.fields.get(field_name)
100110

101-
for validator in field["validators"]:
102-
validator.validate(value)
111+
for validator in field["validators"]:
112+
validator.validate(value)
113+
except ValidationError:
114+
if field_info.get("fallback") is None:
115+
raise
103116

104-
def _callExternalApi(
105-
self, config: ExternalApiConfig, validated_data: dict
117+
return field_info.get("fallback")
118+
119+
def __callExternalApi(
120+
self, field_info, validated_data: dict
106121
) -> Optional[Any]:
107122
"""
108123
Führt den API-Aufruf durch und gibt den Wert zurück,
109124
der im Antwortkörper zu finden ist.
110125
"""
111126

127+
config: ExternalApiConfig = field_info.get("external_api")
128+
112129
requestData = {
113130
"headers": {},
114131
"params": {},
@@ -132,21 +149,30 @@ def _callExternalApi(
132149
)
133150
requestData["method"] = config.method
134151

135-
response = requests.request(**requestData)
152+
try:
153+
response = requests.request(**requestData)
136154

137-
if response.status_code != 200:
138-
raise ValidationError(
139-
f"External API call failed with "
140-
f"status code {response.status_code}"
141-
)
155+
if response.status_code != 200:
156+
raise ValidationError(
157+
f"External API call failed with "
158+
f"status code {response.status_code}"
159+
)
160+
161+
result = response.json()
142162

143-
result = response.json()
163+
data_key = config.data_key
164+
if data_key:
165+
return result.get(data_key)
144166

145-
data_key = config.data_key
146-
if data_key:
147-
return result.get(data_key)
167+
return result
168+
except Exception:
169+
if field_info and field_info.get("fallback") is None:
170+
raise ValidationError(
171+
f"External API call failed for field "
172+
f"'{config.data_key}'."
173+
)
148174

149-
return result
175+
return field_info.get("fallback")
150176

151177
@staticmethod
152178
def __replacePlaceholders(value: str, validated_data: dict) -> str:
@@ -175,6 +201,34 @@ def __replacePlaceholdersInParams(
175201
for key, value in params.items()
176202
}
177203

204+
@staticmethod
205+
def __checkForRequired(
206+
field_name: str, field_info: dict, value: Any
207+
) -> Any:
208+
"""
209+
Determine the value of the field, considering the required and fallback attributes.
210+
211+
If the field is not required and no value is provided, the default value is returned.
212+
If the field is required and no value is provided, the fallback value is returned.
213+
If no of the above conditions are met, a ValidationError is raised.
214+
"""
215+
216+
if value is not None:
217+
return value
218+
219+
if not field_info.get("required"):
220+
return field_info.get("default")
221+
222+
if field_info.get("fallback") is not None:
223+
return field_info.get("fallback")
224+
225+
raise ValidationError(f"Field '{field_name}' is required.")
226+
227+
def __checkConditions(self, validated_data: dict) -> None:
228+
for condition in self.conditions:
229+
if not condition.check(validated_data):
230+
raise ValidationError(f"Condition '{condition}' not met.")
231+
178232
def validateData(
179233
self, data: Dict[str, Any], kwargs: Dict[str, Any] = None
180234
) -> Dict[str, Any]:
@@ -192,71 +246,20 @@ def validateData(
192246
for field_name, field_info in self.fields.items():
193247
value = combined_data.get(field_name)
194248

195-
# Apply filters
196-
value = self._applyFilters(field_name, value)
197-
198-
# Check for required field
199-
if value is None:
200-
if (
201-
field_info.get("required")
202-
and field_info.get("external_api") is None
203-
):
204-
if field_info.get("fallback") is None:
205-
raise ValidationError(
206-
f"Field '{field_name}' is required."
207-
)
249+
value = self.__applyFilters(field_name, value)
208250

209-
value = field_info.get("fallback")
210-
211-
if field_info.get("default") is not None:
212-
value = field_info.get("default")
251+
value = (
252+
self.__validateField(field_name, field_info, value) or value
253+
)
213254

214-
# Validate field
215-
if value is not None:
216-
try:
217-
self._validateField(field_name, value)
218-
except ValidationError:
219-
if field_info.get("fallback") is not None:
220-
value = field_info.get("fallback")
221-
else:
222-
raise
223-
224-
# External API call
225255
if field_info.get("external_api"):
226-
external_api_config = field_info.get("external_api")
227-
228-
try:
229-
value = self._callExternalApi(
230-
external_api_config, validated_data
231-
)
232-
233-
except Exception:
234-
if field_info.get("fallback") is None:
235-
raise ValidationError(
236-
f"External API call failed for field "
237-
f"'{field_name}'."
238-
)
239-
240-
value = field_info.get("fallback")
241-
242-
if value is None:
243-
if field_info.get("required"):
244-
if field_info.get("fallback") is None:
245-
raise ValidationError(
246-
f"Field '{field_name}' is required."
247-
)
256+
value = self.__callExternalApi(field_info, validated_data)
248257

249-
value = field_info.get("fallback")
250-
251-
if field_info.get("default") is not None:
252-
value = field_info.get("default")
258+
value = self.__checkForRequired(field_name, field_info, value)
253259

254260
validated_data[field_name] = value
255261

256-
# Check conditions
257-
for condition in self.conditions:
258-
if not condition.check(validated_data):
259-
raise ValidationError(f"Condition '{condition}' not met.")
262+
self.__checkConditions(validated_data)
260263

261264
return validated_data
262265

flask_inputfilter/Validator/DateAfterValidator.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def validate(self, value: Any) -> None:
2323
value_reference_date = self._parse_date(self.reference_date)
2424
value_datetime = self._parse_date(value)
2525

26-
if value_datetime <= value_reference_date:
26+
if value_datetime < value_reference_date:
2727
raise ValidationError(
2828
self.error_message
2929
or f"Date '{value}' is not after '{value_reference_date}'."
@@ -43,7 +43,6 @@ def _parse_date(self, value: Any) -> datetime:
4343
except ValueError:
4444
raise ValidationError(f"Invalid ISO 8601 format '{value}'.")
4545

46-
else:
47-
raise ValidationError(
48-
f"Unsupported type for date comparison '{type(value)}'."
49-
)
46+
raise ValidationError(
47+
f"Unsupported type for date comparison '{type(value)}'."
48+
)

flask_inputfilter/Validator/DateBeforeValidator.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ def _parse_date(self, value: Any) -> datetime:
4343
except ValueError:
4444
raise ValidationError(f"Invalid ISO 8601 format '{value}'.")
4545

46-
else:
47-
raise ValidationError(
48-
f"Unsupported type for date comparison '{type(value)}'."
49-
)
46+
raise ValidationError(
47+
f"Unsupported type for date comparison '{type(value)}'."
48+
)

flask_inputfilter/Validator/IsPastDateValidator.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ def _parse_date(self, value: Any) -> datetime:
4040
elif isinstance(value, date):
4141
return datetime.combine(value, datetime.min.time())
4242

43-
else:
44-
raise ValidationError(
45-
f"Unsupported type for past date validation '{type(value)}'."
46-
)
43+
raise ValidationError(
44+
f"Unsupported type for past date validation '{type(value)}'."
45+
)

flask_inputfilter/Validator/IsWeekdayValidator.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ def _parse_date(self, value: Any) -> datetime:
3636
except ValueError:
3737
raise ValidationError(f"Invalid ISO 8601 format '{value}'.")
3838

39-
else:
40-
raise ValidationError(
41-
f"Unsupported type for weekday validation '{type(value)}'."
42-
)
39+
raise ValidationError(
40+
f"Unsupported type for weekday validation '{type(value)}'."
41+
)

setup.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from setuptools import find_packages, setup
1+
from setuptools import setup
22

33
setup(
44
name="flask_inputfilter",
@@ -10,22 +10,22 @@
1010
long_description=open("README.rst").read(),
1111
long_description_content_type="text/markdown",
1212
url="https://github.com/LeanderCS/flask-inputfilter",
13-
packages=find_packages(),
13+
packages=["flask_inputfilter"],
1414
install_requires=[
1515
"Flask>=2.1",
1616
"pillow>=8.0.0",
1717
"requests>=2.22.0",
1818
],
1919
classifiers=[
20+
"License :: OSI Approved :: MIT License",
21+
"Operating System :: OS Independent",
2022
"Programming Language :: Python :: 3.13",
2123
"Programming Language :: Python :: 3.12",
2224
"Programming Language :: Python :: 3.11",
2325
"Programming Language :: Python :: 3.10",
2426
"Programming Language :: Python :: 3.9",
2527
"Programming Language :: Python :: 3.8",
2628
"Programming Language :: Python :: 3.7",
27-
"License :: OSI Approved :: MIT License",
28-
"Operating System :: OS Independent",
2929
],
3030
python_requires=">=3.7",
3131
)

test/test_input_filter.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,11 @@ def __init__(self):
3737
self.add(
3838
name="username",
3939
required=True,
40-
filters=[],
41-
validators=[],
4240
)
4341
self.add(
4442
name="age",
4543
required=False,
4644
default=18,
47-
filters=[],
4845
validators=[IsIntegerValidator()],
4946
)
5047

@@ -107,7 +104,6 @@ def __init__(self):
107104
name="age",
108105
required=False,
109106
default=18,
110-
filters=[],
111107
validators=[IsIntegerValidator()],
112108
)
113109

@@ -135,14 +131,11 @@ def __init__(self):
135131
self.add(
136132
name="username",
137133
required=True,
138-
filters=[],
139-
validators=[],
140134
)
141135
self.add(
142136
name="age",
143137
required=False,
144138
default=18,
145-
filters=[],
146139
validators=[IsIntegerValidator()],
147140
)
148141

@@ -237,13 +230,18 @@ def test_fallback_with_default(self) -> None:
237230

238231
validated_data = self.inputFilter.validateData({})
239232

240-
self.assertEqual(validated_data["available"], True)
233+
self.assertEqual(validated_data["available"], False)
241234
self.assertEqual(validated_data["color"], "red")
242235

243236
validated_data = self.inputFilter.validateData({"available": False})
244237

245238
self.assertEqual(validated_data["available"], False)
246239

240+
self.inputFilter.add("required_without_fallback", required=True)
241+
242+
with self.assertRaises(ValidationError):
243+
self.inputFilter.validateData({})
244+
247245
@patch("requests.request")
248246
def test_external_api(self, mock_request: Mock) -> None:
249247
"""

0 commit comments

Comments
 (0)