Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
5 changes: 4 additions & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
[run]
omit = __init__.py, setup.py, test_*.py
source = flask_inputfilter

[report]
omit = __init__.py, setup.py, */test/*
1 change: 0 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
**.md
.gitignore
.git
.github
.idea
.vscode
Expand Down
9 changes: 7 additions & 2 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Run Tests

on: [ push ]
on: [push]

permissions:
actions: read
Expand All @@ -23,7 +23,12 @@ jobs:
set -e # Exit immediately if a command exits with a non-zero status.
set -u # Exit immediately if a variable is not defined.

docker run flask-inputfilter pytest
docker run flask-inputfilter coverage run --source=flask_inputfilter -m pytest test/

- name: Upload coverage to Coveralls
env:
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
run: docker run -e COVERALLS_REPO_TOKEN=${{ secrets.COVERALLS_REPO_TOKEN }} flask-inputfilter coveralls

- name: Run code style checks
run: |
Expand Down
4 changes: 2 additions & 2 deletions CHAGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ All notable changes to this project will be documented in this file.

### Added

- New condition functionality between fields. [Check it out](src/flask_inputfilter/Condition/README.md)
- New condition functionality between fields. [Check it out](flask_inputfilter/Condition/README.md)

### Changed

- Switched external_api config from dict to class. [Check it out](src/flask_inputfilter/Model/ExternalApiConfig.py)
- Switched external_api config from dict to class. [Check it out](flask_inputfilter/Model/ExternalApiConfig.py)


## [0.0.4] - 2025-01-09
Expand Down
1 change: 1 addition & 0 deletions CREATE_OWN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Create own
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ FROM python:3.7-slim

WORKDIR /app

RUN apt-get update && apt-get install -y gcc python3-dev
RUN apt-get update && apt-get install -y gcc python3-dev git

RUN pip install --upgrade pip

Expand Down
32 changes: 26 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,30 @@
from src.flask_inputfilter.Validator import IsStringValidator

# flask-inputfilter

The `InputFilter` class is used to validate and filter input data in Flask applications.
It provides a modular way to clean and ensure that incoming data meets expected format and type requirements before being processed.

:Test Status:

.. image:: https://img.shields.io/github/actions/workflow/status/LeanderCS/flask-inputfilter/test.yaml?branch=main&style=flat-square&label=Github%20Actions
:target: https://github.com/LeanderCS/flask-inputfilter/actions
.. image:: https://img.shields.io/coveralls/LeanderCS/flask-inputfilter/main.svg?style=flat-square&label=Coverage
:target: https://coveralls.io/r/LeanderCS/flask-inputfilter

:Version Info:

.. image:: https://img.shields.io/pypi/v/flask-inputfilter?style=flat-square&label=PyPI
:target: https://pypi.org/project/flask-inputfilter/

:Compatibility:

.. image:: https://img.shields.io/pypi/pyversions/flask-inputfilter?style=flat-square&label=PyPI
:target: https://pypi.org/project/flask-inputfilter/

:Downloads:

.. image:: https://img.shields.io/pypi/dm/flask-inputfilter?style=flat-square&label=PyPI
:target: https://pypi.org/project/flask-inputfilter/

---

## Installation
Expand All @@ -19,7 +39,7 @@ pip install flask-inputfilter

To use the `InputFilter` class, you need to create a new class that inherits from it and define the fields you want to validate and filter.

There are lots of different filters and validators available to use, but it is also possible to create your own.
There are lots of different filters and validators available to use, but it is also possible to create your [own](CREATE_OWN.md).

### Definition

Expand All @@ -40,7 +60,7 @@ class UpdateZipcodeInputFilter(InputFilter):
self.add(
'id',
required=True,
filters=[ToNullFilter()],
filters=[ToIntegerFilter(), ToNullFilter()],
validators=[
IsIntegerValidator()
]
Expand Down Expand Up @@ -102,8 +122,8 @@ def updateZipcode():
The `add` method takes the following options:

- [`Required`](#required)
- [`Filter`](src/flask_inputfilter/Filter/README.md)
- [`Validator`](src/flask_inputfilter/Validator/README.md)
- [`Filter`](flask_inputfilter/Filter/README.md)
- [`Validator`](flask_inputfilter/Validator/README.md)
- [`Default`](#default)
- [`Fallback`](#fallback)
- [`ExternalApi`](EXTERNAL_API.md)
Expand Down
20 changes: 20 additions & 0 deletions flask_inputfilter/Condition/ArrayLengthEqualCondition.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from typing import Any, Dict

from .BaseCondition import BaseCondition


class ArrayLengthEqualCondition(BaseCondition):
"""
Condition that checks if the array is of the specified length.
"""

def __init__(
self, first_array_field: str, second_array_field: str
) -> None:
self.first_array_field = first_array_field
self.second_array_field = second_array_field

def check(self, data: Dict[str, Any]) -> bool:
return len(data.get(self.first_array_field) or []) == len(
data.get(self.second_array_field) or []
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,33 @@ The `Condition` module contains the conditions that can be used to validate the
The `addCondition` method is used to add a condition between fields.

```python

from flask_inputfilter import InputFilter
from flask_inputfilter.Condition import ExactlyOneOfCondition
from flask_inputfilter.Filter import ToIntegerFilter
from flask_inputfilter.Validator import IsIntegerValidator
from flask_inputfilter.Condition import OneOfCondition
from flask_inputfilter.Filter import StringTrimFilter
from flask_inputfilter.Validator import IsStringValidator


class TestInputFilter(InputFilter):
def __init__(self):
super().__init__()

self.add(
'id',
required=True,
filters=[ToIntegerFilter()],
validators=[IsIntegerValidator()]
'username',
filters=[StringTrimFilter()],
validators=[IsStringValidator()]
)

self.add(
'name',
required=True
filters=[StringTrimFilter()],
validators=[IsStringValidator()]
)

self.addCondition(
ExactlyOneOfCondition('id', 'name')
OneOfCondition(['id', 'name'])
)

```

## Available conditions
Expand All @@ -51,4 +54,4 @@ The following conditions are available in the `Condition` module:
12. [`OneOfMatchesCondition`](OneOfMatchesCondition.py) - Validates that at least one of the given matches is true.
13. [`RequiredIfCondition`](RequiredIfCondition.py) - Validates that the input is required if the given condition is true.
14. [`StringLongerThanCondition`](StringLongerThanCondition.py) - Validates that the string is longer than the given value.
15. [`TemporalOrderCondition`](TemporalOrderCondition.py) - Validates that the input is a temporal value.
15. [`TemporalOrderCondition`](TemporalOrderCondition.py) - Validates that the input is in correct temporal order.
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,20 @@ class TemporalOrderCondition(BaseCondition):
Supports datetime, date, and ISO 8601 formatted strings.
"""

def __init__(self, first_field: str, second_field: str) -> None:
self.first_field = first_field
self.second_field = second_field
def __init__(
self, smaller_date_field: str, larger_date_field: str
) -> None:
self.smaller_date_field = smaller_date_field
self.larger_date_field = larger_date_field

def check(self, data: Dict[str, Any]) -> bool:
first_date = self._parse_date(data.get(self.first_field))
second_date = self._parse_date(data.get(self.second_field))
smaller_date = self._parse_date(data.get(self.smaller_date_field))
larger_date = self._parse_date(data.get(self.larger_date_field))

return first_date < second_date
return smaller_date < larger_date

@staticmethod
def _parse_date(value: Any) -> datetime:
"""
Converts a value to a datetime object if possible.
Supports datetime, date, and ISO 8601 strings.
"""

if isinstance(value, datetime):
return value

Expand Down
28 changes: 28 additions & 0 deletions flask_inputfilter/Filter/BlacklistFilter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from typing import Any, List

from .BaseFilter import BaseFilter


class BlacklistFilter(BaseFilter):
"""Filter that filters out values that are in the blacklist."""

def __init__(self, blacklist: List[str]) -> None:
self.blacklist = blacklist

def apply(self, value: Any) -> Any:
if isinstance(value, str):
for item in self.blacklist:
value = value.replace(item, "")
return value.strip()

if isinstance(value, list):
return [item for item in value if item not in self.blacklist]

if isinstance(value, dict):
return {
key: value
for key, value in value.items()
if key not in self.blacklist
}

return value
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ class RemoveEmojisFilter(BaseFilter):
"""

def apply(self, value: Any) -> Union[Optional[str], Any]:

if not isinstance(value, str):
return value

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,4 @@ class ToBooleanFilter(BaseFilter):
"""

def apply(self, value: Any) -> Union[Optional[bool], Any]:
try:
return bool(value)

except (ValueError, TypeError):
return value
return bool(value)
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ class ToDateFilter(BaseFilter):
"""

def apply(self, value: Any) -> Union[date, Any]:
if isinstance(value, date):
return value

elif isinstance(value, datetime):
if isinstance(value, datetime):
return value.date()

elif isinstance(value, date):
return value

elif isinstance(value, str):
try:
return datetime.fromisoformat(value).date()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from datetime import datetime
from datetime import date, datetime
from typing import Any, Union

from .BaseFilter import BaseFilter
Expand All @@ -14,6 +14,9 @@ def apply(self, value: Any) -> Union[datetime, Any]:
if isinstance(value, datetime):
return value

elif isinstance(value, date):
return datetime.combine(value, datetime.min.time())

elif isinstance(value, str):
try:
return datetime.fromisoformat(value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ def apply(self, value: Any) -> Union[Enum, Any]:
if not isinstance(value, (str, int)):
return value

if isinstance(value, Enum):
return value

try:
return self.enum_class(value)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,4 @@ class ToStringFilter(BaseFilter):
"""

def apply(self, value: Any) -> Union[str, Any]:
try:
return str(value)

except (ValueError, TypeError):
return value
return str(value)
28 changes: 28 additions & 0 deletions flask_inputfilter/Filter/WhitelistFilter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from typing import Any, List

from .BaseFilter import BaseFilter


class WhitelistFilter(BaseFilter):
"""Filter that filters out values that are not in the whitelist."""

def __init__(self, whitelist: List[str] = None) -> None:
self.whitelist = whitelist

def apply(self, value: Any) -> Any:
if isinstance(value, str):
return " ".join(
[word for word in value.split() if word in self.whitelist]
)

if isinstance(value, list):
return [item for item in value if item in self.whitelist]

if isinstance(value, dict):
return {
key: value
for key, value in value.items()
if key in self.whitelist
}

return value
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from .ArrayExplodeFilter import ArrayExplodeFilter
from .BaseFilter import BaseFilter
from .BlacklistFilter import BlacklistFilter
from .RemoveEmojisFilter import RemoveEmojisFilter
from .SlugifyFilter import SlugifyFilter
from .StringTrimFilter import StringTrimFilter
Expand All @@ -20,4 +21,5 @@
from .ToStringFilter import ToStringFilter
from .ToUpperFilter import ToUpperFilter
from .TruncateFilter import TruncateFilter
from .WhitelistFilter import WhitelistFilter
from .WhitespaceCollapseFilter import WhitespaceCollapseFilter
Loading
Loading