diff --git a/.github/workflows/deploy-docs.yaml b/.github/workflows/deploy-docs.yaml new file mode 100644 index 0000000..eecf6ae --- /dev/null +++ b/.github/workflows/deploy-docs.yaml @@ -0,0 +1,33 @@ +name: Deploy Sphinx Documentation + +on: + push: + branches: + - main + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v3 + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + + - name: Install Dependencies + run: | + pip install sphinx sphinx_rtd_theme + + - name: Build Documentation + run: | + sphinx-build -b html docs/ docs/_build/html + + - name: Deploy to GitHub Pages + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: docs/_build/html diff --git a/CHAGELOG.md b/CHAGELOG.md deleted file mode 100644 index b63110a..0000000 --- a/CHAGELOG.md +++ /dev/null @@ -1,78 +0,0 @@ -# Changelog - -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. - -### Filter - -- New [`Base64ImageDownscaleFilter`](flask_inputfilter/Filter/Base64ImageDownscaleFilter.py) to reduce the size of an image. -- New [`Base64ImageResizeFilter`](flask_inputfilter/Filter/Base64ImageResizeFilter.py) to reduce the file size of an image. - -### Validator - -- New [`IsHorizontalImageValidator`](flask_inputfilter/Validator/IsHorizontalImageValidator.py) to check if an image is horizontical. -- New [`IsVerticalImageValidator`](flask_inputfilter/Validator/IsVerticalImageValidator.py) to check if an image is vertical. - -## Changed - -- Added UnicodeFormEnum to show possible config values for ToNormalizedUnicodeFilter. - Old config is still supportet, but will be removed at a later version. - - -# [0.0.7.1] - 2025-01-16 - -## Changed - -- Updated setup.py to fix the issue with the missing subfolders. - - -# [0.0.7] - 2025-01-14 - -## Added - -- Workflow to run tests on all supported python versions. [Check it out](.github/workflows/test_env.yaml) -- Added more test coverage for validators and filters. -- Added tracking of coverage in tests. [Check it out](https://coveralls.io/github/LeanderCS/flask-inputfilter) -- New functionality for global filters and validators in InputFilters. -- New functionality to define custom supported methods. - -### Validator - -- New `NotInArrayValidator` to check if a value is not in a list. [Check it out](flask_inputfilter/Validator/NotInArrayValidator.py) -- New `NotValidator` to invert the result of another validator. [Check it out](flask_inputfilter/Validator/NotValidator.py) - - -# [0.0.6] - 2025-01-12 - -## Added - -- New date validators and filters. - -## Removed - -- Dropped support for Python 3.6. - - -# [0.0.5] - 2025-01-12 - -## Added - -- 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](flask_inputfilter/Model/ExternalApiConfig.py) - - -# [0.0.4] - 2025-01-09 - -## Added - -- New external api functionality. [Check it out](EXTERNAL_API.md) diff --git a/CREATE_OWN.md b/CREATE_OWN.md deleted file mode 100644 index 467646a..0000000 --- a/CREATE_OWN.md +++ /dev/null @@ -1 +0,0 @@ -# Create own \ No newline at end of file diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md deleted file mode 100644 index 89a6b02..0000000 --- a/DEVELOPMENT.md +++ /dev/null @@ -1,27 +0,0 @@ -# Development - -### Build docker image -```bash -docker build -t flask-inputfilter . -``` - -### Run docker container in interactive mode -```bash -docker compose up -d -``` - -```bash -docker exec -it flask-inputfilter /bin/bash -``` - -### Run tests -```bash -docker exec -it flask-inputfilter pytest -``` - -### Run linting -```bash -docker exec -it flask-inputfilter sh -c "isort ." -docker exec -it flask-inputfilter sh -c "autoflake --in-place --remove-all-unused-imports --ignore-init-module-imports --recursive ." -docker exec -it flask-inputfilter black . -``` diff --git a/EXTERNAL_API.md b/EXTERNAL_API.md deleted file mode 100644 index 8e670c6..0000000 --- a/EXTERNAL_API.md +++ /dev/null @@ -1,118 +0,0 @@ -# External API Functionality in `InputFilter` - -This documentation provides a comprehensive overview of the external API functionality available in the `InputFilter` class. It covers the configuration, core methods, and examples of usage for interacting with external APIs. - ---- - -## 1. Overview - -The `InputFilter` class includes a mechanism for fetching data from external APIs during the input validation process. -This feature allows dynamic data retrieval based on user inputs, such as validating fields or fetching related data from an external service. - -Important to know, the external api functionality runs after all other filters and validators have been executed. -This means that the data fetched from the external API will not be validated or filtered. - ---- - -## 2. Configuration - -The external API functionality is configured via the `external_api` parameter in the `add` method. This parameter accepts a dictionary with the following structure: - -### `ExternalApiConfig` Fields - -| Field | Type | Description | -|------------|--------------------------|-----------------------------------------------------------------------------| -| `url` | `str` | The URL of the external API, with optional placeholders in `{{}}` format. | -| `method` | `str` | The HTTP method to use (e.g., `GET`, `POST`). | -| `params` | `Optional[Dict[str, str]]` | Query parameters for the API, with placeholders allowed. | -| `data_key` | `Optional[str]` | Key in the JSON response to extract the required data. | -| `api_key` | `Optional[str]` | API key for authorization, sent in the `Authorization` header. | - ---- - -## 3. Examples - -### 3.1 Basic External API Integration - -```python -from flask_inputfilter.InputFilter import InputFilter - -class MyInputFilter(InputFilter): - def __init__(self): - super().__init__() - - self.add( - "user_id", required=True - ) - self.add( - "is_active", - required=True, - external_api={ - "url": "https://api.example.com/users/{{user_id}}/status", - "method": "GET", - "data_key": "is_active", - }, - ) - -# Example usage -filter_instance = MyInputFilter() -validated_data = filter_instance.validateData({"user_id": 123}) -print(validated_data["is_active"]) # True or False based on API response -``` - -### 3.2 Using Query Parameters - -```python -self.add( - "is_valid", - required=True, - external_api={ - "url": "https://api.example.com/validate", - "method": "GET", - "params": {"user": "{{user_id}}", "hash": "{{hash}}"}, - "data_key": "is_valid", - }, -) -``` - -This configuration sends the `user_id` and `hash` as query parameters, replacing the placeholders with validated data. - ---- - -### 3.3 Handling Fallback Values - -If the external API call fails, a fallback value can be specified: - -```python -self.add( - "user_info", - required=True, - fallback={"name": "unknown", "age": 0}, - external_api={ - "url": "https://api.example.com/user/{{user_id}}", - "method": "GET", - "data_key": "user", - }, -) -``` - ---- - -## 4. Error Handling - -- `ValidationError` is raised when: - - The API call returns a non-200 status code. - - A required field is missing and no fallback/default is provided. - - Validation of the field value fails. - ---- - -## 7. Best Practices - -- **Required Fields:** Clearly define required fields and provide fallback values where necessary. -- **Placeholders:** Ensure placeholders in URLs and parameters match the keys in `validated_data`. -- **Fallbacks:** Always provide fallback values for critical fields to avoid disruptions in case of API failure. -- **Security:** Use HTTPS for API calls and secure sensitive data like API keys. -- **Testing:** Mock external API calls during unit testing to avoid dependencies on external systems. - ---- diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d0c3cbf --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/README.rst b/README.rst index 90cabd6..eac77db 100644 --- a/README.rst +++ b/README.rst @@ -1,10 +1,37 @@ flask-inputfilter ================================== -The `InputFilter` class is used to validate and filter input data in Flask applications. +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. +.. raw:: html + +
+

+ ');"> + Tip +

+

+ Thank you for using flask-inputfilter!
+ If you have any questions or suggestions, please feel free to open an issue on GitHub here.
+ If you don't want to miss any updates, please star the repository.
+ This will help me to understand how many people are interested in this project.
+

+
+ +.. raw:: html + +
+

+ ');"> + Hint +

+

+ For information about the usage you can view the documentation +

+
+ :Test Status: .. image:: https://img.shields.io/github/actions/workflow/status/LeanderCS/flask-inputfilter/test.yaml?branch=main&style=flat-square&label=Github%20Actions @@ -37,10 +64,10 @@ Installation Quickstart ========== -To use the `InputFilter` class, create a new class that inherits from it and define the +To use the ``InputFilter`` class, create a new class that inherits from it and define the fields you want to validate and filter. -There are numerous filters and validators available, but you can also create your `own `_. +There are numerous filters and validators available, but you can also create your `own `_. Definition ---------- @@ -92,8 +119,8 @@ Definition Usage ----- -To use the `InputFilter` class, call the `validate` method on the class instance. -After calling `validate`, the validated data will be available in `g.validated_data`. +To use the ``InputFilter`` class, call the ``validate`` method on the class instance. +After calling ``validate``, the validated data will be available in ``g.validated_data``. If the data is invalid, a 400 response with an error message will be returned. .. code-block:: python @@ -112,33 +139,17 @@ If the data is invalid, a 400 response with an error message will be returned. id = data.get('id') zipcode = data.get('zipcode') -Options -======= - -The `add` method supports several options: - -- `Required`_ -- `Filter `_ -- `Validator `_ -- `Default`_ -- `Fallback`_ -- `ExternalApi `_ - -Required --------- - -The `required` option specifies whether the field must be included in the input data. -If the field is missing, a `ValidationError` will be raised with an appropriate error message. - -Default -------- -The `default` option allows you to specify a default value to use if the field is not -present in the input data. +.. raw:: html -Fallback --------- +
+

+ + Tip +

+

+ For further instructions please view the documentary `Here -The `fallback` option specifies a value to use if validation fails or required data -is missing. Note that if the field is optional and absent, `fallback` will not apply; -use `default` in such cases. + For ideas, suggestions or questions, please open an issue on GitHub here. +

+
diff --git a/flask_inputfilter/Condition/ArrayLengthEqualCondition.py b/flask_inputfilter/Condition/ArrayLengthEqualCondition.py index 90d88cc..40ce815 100644 --- a/flask_inputfilter/Condition/ArrayLengthEqualCondition.py +++ b/flask_inputfilter/Condition/ArrayLengthEqualCondition.py @@ -1,6 +1,6 @@ from typing import Any, Dict -from .BaseCondition import BaseCondition +from flask_inputfilter.Condition.BaseCondition import BaseCondition class ArrayLengthEqualCondition(BaseCondition): diff --git a/flask_inputfilter/Condition/ArrayLongerThanCondition.py b/flask_inputfilter/Condition/ArrayLongerThanCondition.py index de6b3e1..e4f7c2c 100644 --- a/flask_inputfilter/Condition/ArrayLongerThanCondition.py +++ b/flask_inputfilter/Condition/ArrayLongerThanCondition.py @@ -1,6 +1,6 @@ from typing import Any, Dict -from .BaseCondition import BaseCondition +from flask_inputfilter.Condition.BaseCondition import BaseCondition class ArrayLongerThanCondition(BaseCondition): diff --git a/flask_inputfilter/Condition/CustomCondition.py b/flask_inputfilter/Condition/CustomCondition.py index 41d6267..fa87ccc 100644 --- a/flask_inputfilter/Condition/CustomCondition.py +++ b/flask_inputfilter/Condition/CustomCondition.py @@ -1,6 +1,6 @@ from typing import Any, Callable, Dict -from .BaseCondition import BaseCondition +from flask_inputfilter.Condition import BaseCondition class CustomCondition(BaseCondition): diff --git a/flask_inputfilter/Condition/EqualCondition.py b/flask_inputfilter/Condition/EqualCondition.py index 82c3805..75a6944 100644 --- a/flask_inputfilter/Condition/EqualCondition.py +++ b/flask_inputfilter/Condition/EqualCondition.py @@ -1,6 +1,6 @@ from typing import Any, Dict -from .BaseCondition import BaseCondition +from flask_inputfilter.Condition import BaseCondition class EqualCondition(BaseCondition): diff --git a/flask_inputfilter/Condition/ExactlyNOfCondition.py b/flask_inputfilter/Condition/ExactlyNOfCondition.py index c081e3e..59969e7 100644 --- a/flask_inputfilter/Condition/ExactlyNOfCondition.py +++ b/flask_inputfilter/Condition/ExactlyNOfCondition.py @@ -1,6 +1,6 @@ from typing import Any, Dict, List -from .BaseCondition import BaseCondition +from flask_inputfilter.Condition import BaseCondition class ExactlyNOfCondition(BaseCondition): diff --git a/flask_inputfilter/Condition/ExactlyNOfMatchesCondition.py b/flask_inputfilter/Condition/ExactlyNOfMatchesCondition.py index b474b82..07843de 100644 --- a/flask_inputfilter/Condition/ExactlyNOfMatchesCondition.py +++ b/flask_inputfilter/Condition/ExactlyNOfMatchesCondition.py @@ -1,6 +1,6 @@ from typing import Any, Dict, List -from .BaseCondition import BaseCondition +from flask_inputfilter.Condition import BaseCondition class ExactlyNOfMatchesCondition(BaseCondition): diff --git a/flask_inputfilter/Condition/ExactlyOneOfCondition.py b/flask_inputfilter/Condition/ExactlyOneOfCondition.py index bd10b55..de69d09 100644 --- a/flask_inputfilter/Condition/ExactlyOneOfCondition.py +++ b/flask_inputfilter/Condition/ExactlyOneOfCondition.py @@ -1,6 +1,6 @@ from typing import Any, Dict, List -from .BaseCondition import BaseCondition +from flask_inputfilter.Condition import BaseCondition class ExactlyOneOfCondition(BaseCondition): diff --git a/flask_inputfilter/Condition/ExactlyOneOfMatchesCondition.py b/flask_inputfilter/Condition/ExactlyOneOfMatchesCondition.py index 6751602..728e63f 100644 --- a/flask_inputfilter/Condition/ExactlyOneOfMatchesCondition.py +++ b/flask_inputfilter/Condition/ExactlyOneOfMatchesCondition.py @@ -1,6 +1,6 @@ from typing import Any, Dict, List -from .BaseCondition import BaseCondition +from flask_inputfilter.Condition import BaseCondition class ExactlyOneOfMatchesCondition(BaseCondition): diff --git a/flask_inputfilter/Condition/IntegerBiggerThanCondition.py b/flask_inputfilter/Condition/IntegerBiggerThanCondition.py index cc7200c..106c8a7 100644 --- a/flask_inputfilter/Condition/IntegerBiggerThanCondition.py +++ b/flask_inputfilter/Condition/IntegerBiggerThanCondition.py @@ -1,6 +1,6 @@ from typing import Dict -from .BaseCondition import BaseCondition +from flask_inputfilter.Condition import BaseCondition class IntegerBiggerThanCondition(BaseCondition): diff --git a/flask_inputfilter/Condition/NOfCondition.py b/flask_inputfilter/Condition/NOfCondition.py index 15d15da..e7570fb 100644 --- a/flask_inputfilter/Condition/NOfCondition.py +++ b/flask_inputfilter/Condition/NOfCondition.py @@ -1,6 +1,6 @@ from typing import Any, List -from .BaseCondition import BaseCondition +from flask_inputfilter.Condition import BaseCondition class NOfCondition(BaseCondition): diff --git a/flask_inputfilter/Condition/NOfMatchesCondition.py b/flask_inputfilter/Condition/NOfMatchesCondition.py index 5378e65..3e10bc4 100644 --- a/flask_inputfilter/Condition/NOfMatchesCondition.py +++ b/flask_inputfilter/Condition/NOfMatchesCondition.py @@ -1,6 +1,6 @@ from typing import Any, Dict, List -from .BaseCondition import BaseCondition +from flask_inputfilter.Condition import BaseCondition class NOfMatchesCondition(BaseCondition): diff --git a/flask_inputfilter/Condition/NotEqualCondition.py b/flask_inputfilter/Condition/NotEqualCondition.py index 565f077..dab9b1c 100644 --- a/flask_inputfilter/Condition/NotEqualCondition.py +++ b/flask_inputfilter/Condition/NotEqualCondition.py @@ -1,6 +1,6 @@ from typing import Any, Dict -from .BaseCondition import BaseCondition +from flask_inputfilter.Condition import BaseCondition class NotEqualCondition(BaseCondition): diff --git a/flask_inputfilter/Condition/OneOfCondition.py b/flask_inputfilter/Condition/OneOfCondition.py index 367109c..b6e03eb 100644 --- a/flask_inputfilter/Condition/OneOfCondition.py +++ b/flask_inputfilter/Condition/OneOfCondition.py @@ -1,6 +1,6 @@ from typing import Any, Dict, List -from .BaseCondition import BaseCondition +from flask_inputfilter.Condition import BaseCondition class OneOfCondition(BaseCondition): diff --git a/flask_inputfilter/Condition/OneOfMatchesCondition.py b/flask_inputfilter/Condition/OneOfMatchesCondition.py index 45b5afb..3de5702 100644 --- a/flask_inputfilter/Condition/OneOfMatchesCondition.py +++ b/flask_inputfilter/Condition/OneOfMatchesCondition.py @@ -1,6 +1,6 @@ from typing import Any, Dict, List -from .BaseCondition import BaseCondition +from flask_inputfilter.Condition import BaseCondition class OneOfMatchesCondition(BaseCondition): diff --git a/flask_inputfilter/Condition/README.md b/flask_inputfilter/Condition/README.md deleted file mode 100644 index 86911c5..0000000 --- a/flask_inputfilter/Condition/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# Condition - -The `Condition` module contains the conditions that can be used to validate the input data. - -## Conditions - -The `addCondition` method is used to add a condition between fields. - -```python - -from flask_inputfilter import InputFilter -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( - 'username', - filters=[StringTrimFilter()], - validators=[IsStringValidator()] - ) - - self.add( - 'name', - filters=[StringTrimFilter()], - validators=[IsStringValidator()] - ) - - self.addCondition( - OneOfCondition(['id', 'name']) - ) - -``` - -## Available conditions - -The following conditions are available in the `Condition` module: - -1. [`ArrayLengthEqualCondition`](ArrayLengthEqualCondition.py) - Validates that the length of the array is equal to the given value. -2. [`ArrayLongerThanCondition`](ArrayLongerThanCondition.py) - Validates that the length of the array is longer than the given value. -3. [`CustomCondition`](CustomCondition.py) - A custom condition that can be used to validate the input data. -4. [`EqualCondition`](EqualCondition.py) - Validates that the input is equal to the given value. -5. [`ExactlyNOfCondition`](ExactlyNOfCondition.py) - Validates that exactly `n` of the given conditions are true. -6. [`ExactlyNOfMatchesCondition`](ExactlyNOfMatchesCondition.py) - Validates that exactly `n` of the given matches are true. -7. [`ExactlyOneOfCondition`](ExactlyOneOfCondition.py) - Validates that exactly one of the given conditions is true. -8. [`ExactlyOneOfMatchesCondition`](ExactlyOneOfMatchesCondition.py) - Validates that exactly one of the given matches is true. -9. [`IntegerBiggerThanCondition`](IntegerBiggerThanCondition.py) - Validates that the integer is bigger than the given value. -10. [`NOfCondition`](NOfCondition.py) - Validates that at least `n` of the given conditions are true. -11. [`NOfMatchesCondition`](NOfMatchesCondition.py) - Validates that at least `n` of the given matches are true. -12. [`NotEqualCondition`](NotEqualCondition.py) - Validates that the input is not equal to the given value. -13. [`OneOfCondition`](OneOfCondition.py) - Validates that at least one of the given conditions is true. -14. [`OneOfMatchesCondition`](OneOfMatchesCondition.py) - Validates that at least one of the given matches is true. -15. [`RequiredIfCondition`](RequiredIfCondition.py) - Validates that the input is required if the given field has a specific value. -16. [`StringLongerThanCondition`](StringLongerThanCondition.py) - Validates that the string is longer than the given value. -17. [`TemporalOrderCondition`](TemporalOrderCondition.py) - Validates that the input is in correct temporal order. diff --git a/flask_inputfilter/Condition/RequiredIfCondition.py b/flask_inputfilter/Condition/RequiredIfCondition.py index c04a299..6485433 100644 --- a/flask_inputfilter/Condition/RequiredIfCondition.py +++ b/flask_inputfilter/Condition/RequiredIfCondition.py @@ -1,6 +1,6 @@ from typing import Any, Dict, List, Optional, Union -from .BaseCondition import BaseCondition +from flask_inputfilter.Condition import BaseCondition class RequiredIfCondition(BaseCondition): diff --git a/flask_inputfilter/Condition/StringLongerThanCondition.py b/flask_inputfilter/Condition/StringLongerThanCondition.py index 026551e..321d070 100644 --- a/flask_inputfilter/Condition/StringLongerThanCondition.py +++ b/flask_inputfilter/Condition/StringLongerThanCondition.py @@ -1,6 +1,6 @@ from typing import Dict -from .BaseCondition import BaseCondition +from flask_inputfilter.Condition import BaseCondition class StringLongerThanCondition(BaseCondition): diff --git a/flask_inputfilter/Condition/TemporalOrderCondition.py b/flask_inputfilter/Condition/TemporalOrderCondition.py index 807ac4b..9eecfa0 100644 --- a/flask_inputfilter/Condition/TemporalOrderCondition.py +++ b/flask_inputfilter/Condition/TemporalOrderCondition.py @@ -1,8 +1,8 @@ from datetime import date, datetime from typing import Any, Dict -from ..Exception import ValidationError -from .BaseCondition import BaseCondition +from flask_inputfilter.Condition import BaseCondition +from flask_inputfilter.Exception import ValidationError class TemporalOrderCondition(BaseCondition): diff --git a/flask_inputfilter/Filter/ArrayExplodeFilter.py b/flask_inputfilter/Filter/ArrayExplodeFilter.py index ad960ba..32e472f 100644 --- a/flask_inputfilter/Filter/ArrayExplodeFilter.py +++ b/flask_inputfilter/Filter/ArrayExplodeFilter.py @@ -1,6 +1,6 @@ from typing import Any, List, Union -from .BaseFilter import BaseFilter +from flask_inputfilter.Filter.BaseFilter import BaseFilter class ArrayExplodeFilter(BaseFilter): diff --git a/flask_inputfilter/Filter/Base64ImageDownscaleFilter.py b/flask_inputfilter/Filter/Base64ImageDownscaleFilter.py index 08e56e9..93eb534 100644 --- a/flask_inputfilter/Filter/Base64ImageDownscaleFilter.py +++ b/flask_inputfilter/Filter/Base64ImageDownscaleFilter.py @@ -4,7 +4,7 @@ from PIL import Image -from .BaseFilter import BaseFilter +from flask_inputfilter.Filter.BaseFilter import BaseFilter class Base64ImageDownscaleFilter(BaseFilter): diff --git a/flask_inputfilter/Filter/Base64ImageResizeFilter.py b/flask_inputfilter/Filter/Base64ImageResizeFilter.py index c9efb2e..2969d88 100644 --- a/flask_inputfilter/Filter/Base64ImageResizeFilter.py +++ b/flask_inputfilter/Filter/Base64ImageResizeFilter.py @@ -4,8 +4,8 @@ from PIL import Image -from ..Enum import ImageFormatEnum -from .BaseFilter import BaseFilter +from flask_inputfilter.Enum import ImageFormatEnum +from flask_inputfilter.Filter.BaseFilter import BaseFilter class Base64ImageResizeFilter(BaseFilter): diff --git a/flask_inputfilter/Filter/BlacklistFilter.py b/flask_inputfilter/Filter/BlacklistFilter.py index cfca125..49abd20 100644 --- a/flask_inputfilter/Filter/BlacklistFilter.py +++ b/flask_inputfilter/Filter/BlacklistFilter.py @@ -1,6 +1,6 @@ from typing import Any, List -from .BaseFilter import BaseFilter +from flask_inputfilter.Filter import BaseFilter class BlacklistFilter(BaseFilter): diff --git a/flask_inputfilter/Filter/README.md b/flask_inputfilter/Filter/README.md deleted file mode 100644 index df02528..0000000 --- a/flask_inputfilter/Filter/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# Filter - -The `Filter` module contains the filters that can be used to filter the input data. - -## Available filters - -The following filters are available in the `Filter` module: - -1. [`ArrayExplodeFilter`](ArrayExplodeFilter.py) - Explodes the input string into an array. -2. [`Base64ImageDownscaleFilter`](Base64ImageDownscaleFilter.py) - Downscale the base64 image. -3. [`Base64ImageResizeFilter`](Base64ImageResizeFilter.py) - Resize the base64 image. -2. [`BlacklistFilter`](BlacklistFilter.py) - Filters the string based on the blacklist. -3. [`RemoveEmojisFilter`](RemoveEmojisFilter.py) - Removes the emojis from the string. -4. [`SlugifyFilter`](SlugifyFilter.py) - Converts the string to a slug. -5. [`StringTrimFilter`](StringTrimFilter.py) - Trims the whitespace from the beginning and end of the string. -6. [`ToAlphaNumericFilter`](ToAlphaNumericFilter.py) - Converts the string to an alphanumeric string. -7. [`ToBooleanFilter`](ToBooleanFilter.py) - Converts the string to a boolean value. -8. [`ToCamelCaseFilter`](ToCamelCaseFilter.py) - Converts the string to camel case. -9. [`ToDateFilter`](ToDateFilter.py) - Converts a string to a date value. -10. [`ToDateTimeFilter`](ToDateTimeFilter.py) - Converts a string to a datetime value. -11. [`ToEnumFilter`](ToEnumFilter.py) - Converts a string or integer to an enum value. -12. [`ToFloatFilter`](ToFloatFilter.py) - Converts a string to a float value. -13. [`ToIntegerFilter`](ToIntegerFilter.py) - Converts a string to an integer value. -14. [`ToIsoFilter`](ToIsoFilter.py) - Converts a string to an ISO8601 date time value. -15. [`ToLowerFilter`](ToLowerFilter.py) - Converts a string to lowercase. -16. [`ToNormalizedUnicodeFilter`](ToNormalizedUnicodeFilter.py) - Normalizes a unicode string. -17. [`ToNullFilter`](ToNullFilter.py) - Converts the string to `None` if it is already `None` or `''` (empty string). -18. [`ToPascaleCaseFilter`](ToPascaleCaseFilter.py) - Converts the string to pascal case. -19. [`ToSnakeCaseFilter`](ToSnakeCaseFilter.py) - Converts the string to snake case. -20. [`ToStringFilter`](ToStringFilter.py) - Converts the input to a string value. -21. [`ToUpperFilter`](ToUpperFilter.py) - Converts the string to uppercase. -22. [`TruncateFilter`](TruncateFilter.py) - Truncates the string to the specified length. -23. [`WhitelistFilter`](WhitelistFilter.py) - Filters the string based on the whitelist. -24. [`WhitespaceCollapseFilter`](WhitespaceCollapseFilter.py) - Collapses the whitespace in the string. diff --git a/flask_inputfilter/Filter/RemoveEmojisFilter.py b/flask_inputfilter/Filter/RemoveEmojisFilter.py index 6773405..1af6050 100644 --- a/flask_inputfilter/Filter/RemoveEmojisFilter.py +++ b/flask_inputfilter/Filter/RemoveEmojisFilter.py @@ -1,7 +1,7 @@ import re from typing import Any, Optional, Union -from .BaseFilter import BaseFilter +from flask_inputfilter.Filter import BaseFilter emoji_pattern = ( r"[" diff --git a/flask_inputfilter/Filter/SlugifyFilter.py b/flask_inputfilter/Filter/SlugifyFilter.py index 1eb7ffc..a219ebe 100644 --- a/flask_inputfilter/Filter/SlugifyFilter.py +++ b/flask_inputfilter/Filter/SlugifyFilter.py @@ -1,7 +1,7 @@ import re from typing import Any, Optional, Union -from .BaseFilter import BaseFilter +from flask_inputfilter.Filter import BaseFilter class SlugifyFilter(BaseFilter): diff --git a/flask_inputfilter/Filter/StringTrimFilter.py b/flask_inputfilter/Filter/StringTrimFilter.py index 0839664..28ce1e3 100644 --- a/flask_inputfilter/Filter/StringTrimFilter.py +++ b/flask_inputfilter/Filter/StringTrimFilter.py @@ -1,6 +1,6 @@ from typing import Any, Union -from .BaseFilter import BaseFilter +from flask_inputfilter.Filter import BaseFilter class StringTrimFilter(BaseFilter): diff --git a/flask_inputfilter/Filter/ToAlphaNumericFilter.py b/flask_inputfilter/Filter/ToAlphaNumericFilter.py index 6641c99..1de669e 100644 --- a/flask_inputfilter/Filter/ToAlphaNumericFilter.py +++ b/flask_inputfilter/Filter/ToAlphaNumericFilter.py @@ -1,7 +1,7 @@ import re from typing import Any, Optional, Union -from .BaseFilter import BaseFilter +from flask_inputfilter.Filter import BaseFilter class ToAlphaNumericFilter(BaseFilter): diff --git a/flask_inputfilter/Filter/ToBooleanFilter.py b/flask_inputfilter/Filter/ToBooleanFilter.py index 8ed49e1..91a151d 100644 --- a/flask_inputfilter/Filter/ToBooleanFilter.py +++ b/flask_inputfilter/Filter/ToBooleanFilter.py @@ -1,6 +1,6 @@ from typing import Any, Optional, Union -from .BaseFilter import BaseFilter +from flask_inputfilter.Filter import BaseFilter class ToBooleanFilter(BaseFilter): diff --git a/flask_inputfilter/Filter/ToCamelCaseFilter.py b/flask_inputfilter/Filter/ToCamelCaseFilter.py index 1e70093..a305de7 100644 --- a/flask_inputfilter/Filter/ToCamelCaseFilter.py +++ b/flask_inputfilter/Filter/ToCamelCaseFilter.py @@ -1,7 +1,7 @@ import re from typing import Any, Optional, Union -from .BaseFilter import BaseFilter +from flask_inputfilter.Filter import BaseFilter class ToCamelCaseFilter(BaseFilter): diff --git a/flask_inputfilter/Filter/ToDateFilter.py b/flask_inputfilter/Filter/ToDateFilter.py index db5f35e..65b7ac3 100644 --- a/flask_inputfilter/Filter/ToDateFilter.py +++ b/flask_inputfilter/Filter/ToDateFilter.py @@ -1,7 +1,7 @@ from datetime import date, datetime from typing import Any, Union -from .BaseFilter import BaseFilter +from flask_inputfilter.Filter import BaseFilter class ToDateFilter(BaseFilter): diff --git a/flask_inputfilter/Filter/ToDateTimeFilter.py b/flask_inputfilter/Filter/ToDateTimeFilter.py index d646793..383df32 100644 --- a/flask_inputfilter/Filter/ToDateTimeFilter.py +++ b/flask_inputfilter/Filter/ToDateTimeFilter.py @@ -1,7 +1,7 @@ from datetime import date, datetime from typing import Any, Union -from .BaseFilter import BaseFilter +from flask_inputfilter.Filter import BaseFilter class ToDateTimeFilter(BaseFilter): diff --git a/flask_inputfilter/Filter/ToEnumFilter.py b/flask_inputfilter/Filter/ToEnumFilter.py index f4fc5af..a032595 100644 --- a/flask_inputfilter/Filter/ToEnumFilter.py +++ b/flask_inputfilter/Filter/ToEnumFilter.py @@ -1,7 +1,7 @@ from enum import Enum from typing import Any, Type, Union -from .BaseFilter import BaseFilter +from flask_inputfilter.Filter import BaseFilter class ToEnumFilter(BaseFilter): diff --git a/flask_inputfilter/Filter/ToFloatFilter.py b/flask_inputfilter/Filter/ToFloatFilter.py index b18c221..ec6b105 100644 --- a/flask_inputfilter/Filter/ToFloatFilter.py +++ b/flask_inputfilter/Filter/ToFloatFilter.py @@ -1,6 +1,6 @@ from typing import Any, Union -from .BaseFilter import BaseFilter +from flask_inputfilter.Filter import BaseFilter class ToFloatFilter(BaseFilter): diff --git a/flask_inputfilter/Filter/ToIntegerFilter.py b/flask_inputfilter/Filter/ToIntegerFilter.py index 341dca1..aff33ed 100644 --- a/flask_inputfilter/Filter/ToIntegerFilter.py +++ b/flask_inputfilter/Filter/ToIntegerFilter.py @@ -1,6 +1,6 @@ from typing import Any, Union -from .BaseFilter import BaseFilter +from flask_inputfilter.Filter import BaseFilter class ToIntegerFilter(BaseFilter): diff --git a/flask_inputfilter/Filter/ToIsoFilter.py b/flask_inputfilter/Filter/ToIsoFilter.py index 5c17dba..2c0f138 100644 --- a/flask_inputfilter/Filter/ToIsoFilter.py +++ b/flask_inputfilter/Filter/ToIsoFilter.py @@ -1,7 +1,7 @@ from datetime import date, datetime from typing import Any, Union -from .BaseFilter import BaseFilter +from flask_inputfilter.Filter import BaseFilter class ToIsoFilter(BaseFilter): diff --git a/flask_inputfilter/Filter/ToLowerFilter.py b/flask_inputfilter/Filter/ToLowerFilter.py index 333771d..f025c37 100644 --- a/flask_inputfilter/Filter/ToLowerFilter.py +++ b/flask_inputfilter/Filter/ToLowerFilter.py @@ -1,6 +1,6 @@ from typing import Any, Union -from .BaseFilter import BaseFilter +from flask_inputfilter.Filter import BaseFilter class ToLowerFilter(BaseFilter): diff --git a/flask_inputfilter/Filter/ToNormalizedUnicodeFilter.py b/flask_inputfilter/Filter/ToNormalizedUnicodeFilter.py index e817c13..8870a16 100644 --- a/flask_inputfilter/Filter/ToNormalizedUnicodeFilter.py +++ b/flask_inputfilter/Filter/ToNormalizedUnicodeFilter.py @@ -3,8 +3,8 @@ from typing_extensions import Literal -from ..Enum import UnicodeFormEnum -from .BaseFilter import BaseFilter +from flask_inputfilter.Enum import UnicodeFormEnum +from flask_inputfilter.Filter import BaseFilter class ToNormalizedUnicodeFilter(BaseFilter): diff --git a/flask_inputfilter/Filter/ToNullFilter.py b/flask_inputfilter/Filter/ToNullFilter.py index f0e2345..d434a7f 100644 --- a/flask_inputfilter/Filter/ToNullFilter.py +++ b/flask_inputfilter/Filter/ToNullFilter.py @@ -1,6 +1,6 @@ from typing import Any, Optional -from .BaseFilter import BaseFilter +from flask_inputfilter.Filter import BaseFilter class ToNullFilter(BaseFilter): diff --git a/flask_inputfilter/Filter/ToPascaleCaseFilter.py b/flask_inputfilter/Filter/ToPascaleCaseFilter.py index 6504134..cfa71d2 100644 --- a/flask_inputfilter/Filter/ToPascaleCaseFilter.py +++ b/flask_inputfilter/Filter/ToPascaleCaseFilter.py @@ -1,7 +1,7 @@ import re from typing import Any, Optional, Union -from .BaseFilter import BaseFilter +from flask_inputfilter.Filter import BaseFilter class ToPascaleCaseFilter(BaseFilter): diff --git a/flask_inputfilter/Filter/ToSnakeCaseFilter.py b/flask_inputfilter/Filter/ToSnakeCaseFilter.py index 4434df4..5fa7955 100644 --- a/flask_inputfilter/Filter/ToSnakeCaseFilter.py +++ b/flask_inputfilter/Filter/ToSnakeCaseFilter.py @@ -1,7 +1,7 @@ import re from typing import Any, Union -from .BaseFilter import BaseFilter +from flask_inputfilter.Filter import BaseFilter class ToSnakeCaseFilter(BaseFilter): diff --git a/flask_inputfilter/Filter/ToStringFilter.py b/flask_inputfilter/Filter/ToStringFilter.py index 3249cf4..e4091ca 100644 --- a/flask_inputfilter/Filter/ToStringFilter.py +++ b/flask_inputfilter/Filter/ToStringFilter.py @@ -1,6 +1,6 @@ from typing import Any, Union -from .BaseFilter import BaseFilter +from flask_inputfilter.Filter import BaseFilter class ToStringFilter(BaseFilter): diff --git a/flask_inputfilter/Filter/ToUpperFilter.py b/flask_inputfilter/Filter/ToUpperFilter.py index 5c5cefb..7e4c9ae 100644 --- a/flask_inputfilter/Filter/ToUpperFilter.py +++ b/flask_inputfilter/Filter/ToUpperFilter.py @@ -1,6 +1,6 @@ from typing import Any, Union -from .BaseFilter import BaseFilter +from flask_inputfilter.Filter import BaseFilter class ToUpperFilter(BaseFilter): diff --git a/flask_inputfilter/Filter/TruncateFilter.py b/flask_inputfilter/Filter/TruncateFilter.py index 731b6de..658a21e 100644 --- a/flask_inputfilter/Filter/TruncateFilter.py +++ b/flask_inputfilter/Filter/TruncateFilter.py @@ -1,6 +1,6 @@ from typing import Any, Union -from .BaseFilter import BaseFilter +from flask_inputfilter.Filter import BaseFilter class TruncateFilter(BaseFilter): diff --git a/flask_inputfilter/Filter/WhitelistFilter.py b/flask_inputfilter/Filter/WhitelistFilter.py index 490e73d..a77f315 100644 --- a/flask_inputfilter/Filter/WhitelistFilter.py +++ b/flask_inputfilter/Filter/WhitelistFilter.py @@ -1,6 +1,6 @@ from typing import Any, List -from .BaseFilter import BaseFilter +from flask_inputfilter.Filter import BaseFilter class WhitelistFilter(BaseFilter): diff --git a/flask_inputfilter/Filter/WhitespaceCollapseFilter.py b/flask_inputfilter/Filter/WhitespaceCollapseFilter.py index 02c070d..1220f71 100644 --- a/flask_inputfilter/Filter/WhitespaceCollapseFilter.py +++ b/flask_inputfilter/Filter/WhitespaceCollapseFilter.py @@ -1,7 +1,7 @@ import re from typing import Any, Union -from .BaseFilter import BaseFilter +from flask_inputfilter.Filter import BaseFilter class WhitespaceCollapseFilter(BaseFilter): diff --git a/flask_inputfilter/InputFilter.py b/flask_inputfilter/InputFilter.py index 11fb2da..9b20d80 100644 --- a/flask_inputfilter/InputFilter.py +++ b/flask_inputfilter/InputFilter.py @@ -4,11 +4,11 @@ import requests from flask import Response, g, request -from .Condition.BaseCondition import BaseCondition -from .Exception import ValidationError -from .Filter.BaseFilter import BaseFilter -from .Model import ExternalApiConfig -from .Validator.BaseValidator import BaseValidator +from flask_inputfilter.Condition.BaseCondition import BaseCondition +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Filter import BaseFilter +from flask_inputfilter.Model import ExternalApiConfig +from flask_inputfilter.Validator import BaseValidator class InputFilter: diff --git a/flask_inputfilter/Validator/ArrayElementValidator.py b/flask_inputfilter/Validator/ArrayElementValidator.py index e4d75be..af5a5ac 100644 --- a/flask_inputfilter/Validator/ArrayElementValidator.py +++ b/flask_inputfilter/Validator/ArrayElementValidator.py @@ -1,10 +1,10 @@ from typing import TYPE_CHECKING, Any, Optional -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator.BaseValidator import BaseValidator if TYPE_CHECKING: - from ..InputFilter import InputFilter + from flask_inputfilter import InputFilter class ArrayElementValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/ArrayLengthValidator.py b/flask_inputfilter/Validator/ArrayLengthValidator.py index 06fdebd..cf71d69 100644 --- a/flask_inputfilter/Validator/ArrayLengthValidator.py +++ b/flask_inputfilter/Validator/ArrayLengthValidator.py @@ -1,7 +1,7 @@ from typing import Any, Optional -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator.BaseValidator import BaseValidator class ArrayLengthValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/DateAfterValidator.py b/flask_inputfilter/Validator/DateAfterValidator.py index 18c4028..8211612 100644 --- a/flask_inputfilter/Validator/DateAfterValidator.py +++ b/flask_inputfilter/Validator/DateAfterValidator.py @@ -1,8 +1,8 @@ from datetime import date, datetime from typing import Any, Optional, Union -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class DateAfterValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/DateBeforeValidator.py b/flask_inputfilter/Validator/DateBeforeValidator.py index 0be119d..16d7353 100644 --- a/flask_inputfilter/Validator/DateBeforeValidator.py +++ b/flask_inputfilter/Validator/DateBeforeValidator.py @@ -1,8 +1,8 @@ from datetime import date, datetime from typing import Any, Optional, Union -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class DateBeforeValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/DateRangeValidator.py b/flask_inputfilter/Validator/DateRangeValidator.py index 2308712..553e42e 100644 --- a/flask_inputfilter/Validator/DateRangeValidator.py +++ b/flask_inputfilter/Validator/DateRangeValidator.py @@ -1,8 +1,8 @@ from datetime import date, datetime from typing import Any, Optional, Union -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class DateRangeValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/FloatPrecisionValidator.py b/flask_inputfilter/Validator/FloatPrecisionValidator.py index 867afa5..93e2e3a 100644 --- a/flask_inputfilter/Validator/FloatPrecisionValidator.py +++ b/flask_inputfilter/Validator/FloatPrecisionValidator.py @@ -1,8 +1,8 @@ import re from typing import Any, Optional -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class FloatPrecisionValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/InArrayValidator.py b/flask_inputfilter/Validator/InArrayValidator.py index 9f3437f..dd1a6db 100644 --- a/flask_inputfilter/Validator/InArrayValidator.py +++ b/flask_inputfilter/Validator/InArrayValidator.py @@ -1,7 +1,7 @@ from typing import Any, List, Optional -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class InArrayValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/InEnumValidator.py b/flask_inputfilter/Validator/InEnumValidator.py index cd71f04..d8143bc 100644 --- a/flask_inputfilter/Validator/InEnumValidator.py +++ b/flask_inputfilter/Validator/InEnumValidator.py @@ -1,8 +1,8 @@ from enum import Enum from typing import Any, Optional, Type -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class InEnumValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/IsArrayValidator.py b/flask_inputfilter/Validator/IsArrayValidator.py index 47c47e6..72a3c6e 100644 --- a/flask_inputfilter/Validator/IsArrayValidator.py +++ b/flask_inputfilter/Validator/IsArrayValidator.py @@ -1,7 +1,7 @@ from typing import Any, Optional -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class IsArrayValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/IsBase64ImageCorrectSizeValidator.py b/flask_inputfilter/Validator/IsBase64ImageCorrectSizeValidator.py index a95a725..1f5a43e 100644 --- a/flask_inputfilter/Validator/IsBase64ImageCorrectSizeValidator.py +++ b/flask_inputfilter/Validator/IsBase64ImageCorrectSizeValidator.py @@ -1,8 +1,8 @@ import base64 from typing import Any, Optional -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class IsBase64ImageCorrectSizeValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/IsBase64ImageValidator.py b/flask_inputfilter/Validator/IsBase64ImageValidator.py index 41cbeaf..8d13476 100644 --- a/flask_inputfilter/Validator/IsBase64ImageValidator.py +++ b/flask_inputfilter/Validator/IsBase64ImageValidator.py @@ -4,8 +4,8 @@ from PIL import Image -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class IsBase64ImageValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/IsBooleanValidator.py b/flask_inputfilter/Validator/IsBooleanValidator.py index 7892ffd..e940c86 100644 --- a/flask_inputfilter/Validator/IsBooleanValidator.py +++ b/flask_inputfilter/Validator/IsBooleanValidator.py @@ -1,7 +1,7 @@ from typing import Any, Optional -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class IsBooleanValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/IsFloatValidator.py b/flask_inputfilter/Validator/IsFloatValidator.py index b769536..68593ff 100644 --- a/flask_inputfilter/Validator/IsFloatValidator.py +++ b/flask_inputfilter/Validator/IsFloatValidator.py @@ -1,7 +1,7 @@ from typing import Any, Optional -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class IsFloatValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/IsFutureDateValidator.py b/flask_inputfilter/Validator/IsFutureDateValidator.py index f9e5bb5..fc35013 100644 --- a/flask_inputfilter/Validator/IsFutureDateValidator.py +++ b/flask_inputfilter/Validator/IsFutureDateValidator.py @@ -1,8 +1,8 @@ from datetime import date, datetime from typing import Any, Optional -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class IsFutureDateValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/IsHexadecimalValidator.py b/flask_inputfilter/Validator/IsHexadecimalValidator.py index ff0e1bd..9f6285f 100644 --- a/flask_inputfilter/Validator/IsHexadecimalValidator.py +++ b/flask_inputfilter/Validator/IsHexadecimalValidator.py @@ -1,7 +1,7 @@ from typing import Any, Optional -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class IsHexadecimalValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/IsInstanceValidator.py b/flask_inputfilter/Validator/IsInstanceValidator.py index fbf2da4..dc5c295 100644 --- a/flask_inputfilter/Validator/IsInstanceValidator.py +++ b/flask_inputfilter/Validator/IsInstanceValidator.py @@ -1,7 +1,7 @@ from typing import Any, Optional, Type -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class IsInstanceValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/IsIntegerValidator.py b/flask_inputfilter/Validator/IsIntegerValidator.py index 306066f..6a18a19 100644 --- a/flask_inputfilter/Validator/IsIntegerValidator.py +++ b/flask_inputfilter/Validator/IsIntegerValidator.py @@ -1,7 +1,7 @@ from typing import Any, Optional -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class IsIntegerValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/IsJsonValidator.py b/flask_inputfilter/Validator/IsJsonValidator.py index 76b6cd8..09a749d 100644 --- a/flask_inputfilter/Validator/IsJsonValidator.py +++ b/flask_inputfilter/Validator/IsJsonValidator.py @@ -1,8 +1,8 @@ import json from typing import Any, Optional -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class IsJsonValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/IsPastDateValidator.py b/flask_inputfilter/Validator/IsPastDateValidator.py index 1596857..badafa1 100644 --- a/flask_inputfilter/Validator/IsPastDateValidator.py +++ b/flask_inputfilter/Validator/IsPastDateValidator.py @@ -1,8 +1,8 @@ from datetime import date, datetime from typing import Any, Optional -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class IsPastDateValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/IsStringValidator.py b/flask_inputfilter/Validator/IsStringValidator.py index 813ae50..015268a 100644 --- a/flask_inputfilter/Validator/IsStringValidator.py +++ b/flask_inputfilter/Validator/IsStringValidator.py @@ -1,7 +1,7 @@ from typing import Any, Optional -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class IsStringValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/IsUUIDValidator.py b/flask_inputfilter/Validator/IsUUIDValidator.py index 51b8396..e17ba66 100644 --- a/flask_inputfilter/Validator/IsUUIDValidator.py +++ b/flask_inputfilter/Validator/IsUUIDValidator.py @@ -1,8 +1,8 @@ import uuid from typing import Any, Optional -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class IsUUIDValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/IsWeekdayValidator.py b/flask_inputfilter/Validator/IsWeekdayValidator.py index de57176..eff3b7a 100644 --- a/flask_inputfilter/Validator/IsWeekdayValidator.py +++ b/flask_inputfilter/Validator/IsWeekdayValidator.py @@ -1,8 +1,8 @@ from datetime import date, datetime from typing import Any, Optional -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class IsWeekdayValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/IsWeekendValidator.py b/flask_inputfilter/Validator/IsWeekendValidator.py index f9845e9..a284d2e 100644 --- a/flask_inputfilter/Validator/IsWeekendValidator.py +++ b/flask_inputfilter/Validator/IsWeekendValidator.py @@ -1,8 +1,8 @@ from datetime import date, datetime from typing import Any, Optional -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class IsWeekendValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/LengthValidator.py b/flask_inputfilter/Validator/LengthValidator.py index 8fc65a6..29dfa8e 100644 --- a/flask_inputfilter/Validator/LengthValidator.py +++ b/flask_inputfilter/Validator/LengthValidator.py @@ -1,8 +1,8 @@ from enum import Enum from typing import Any, Optional -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class LengthEnum(Enum): diff --git a/flask_inputfilter/Validator/NotInArrayValidator.py b/flask_inputfilter/Validator/NotInArrayValidator.py index 828e936..0479336 100644 --- a/flask_inputfilter/Validator/NotInArrayValidator.py +++ b/flask_inputfilter/Validator/NotInArrayValidator.py @@ -1,7 +1,7 @@ from typing import Any, List, Optional -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class NotInArrayValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/NotValidator.py b/flask_inputfilter/Validator/NotValidator.py index d63f2fe..8fccaf0 100644 --- a/flask_inputfilter/Validator/NotValidator.py +++ b/flask_inputfilter/Validator/NotValidator.py @@ -1,7 +1,7 @@ from typing import Any, Optional -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class NotValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/README.md b/flask_inputfilter/Validator/README.md deleted file mode 100644 index a157455..0000000 --- a/flask_inputfilter/Validator/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# Validator - -The `Validator` class is used to validate the data after the filters have been applied. - -## Available validators - -The following validators are available in the `Validator` module: - -1. [`ArrayElementValidator`](ArrayElementValidator.py) - Validates each element of an array with its own defined InputFilter. -2. [`ArrayLengthValidator`](ArrayLengthValidator.py) - Validates the length of an array. -3. [`DateAfterValidator`](DateAfterValidator.py) - Validates that the date is after a specified date. -4. [`DateBeforeValidator`](DateBeforeValidator.py) - Validates that the date is before a specified date. -5. [`DateRangeValidator`](DateRangeValidator.py) - Validates that the date is within a specified range. -6. [`FloatPrecisionValidator`](FloatPrecisionValidator.py) - Validates the precision of a float. -7. [`InArrayValidator`](InArrayValidator.py) - Validates that the value is in the given array. -8. [`InEnumValidator`](InEnumValidator.py) - Validates that the value is in the given enum. -9. [`IsArrayValidator`](IsArrayValidator.py) - Validates that the value is an array. -10. [`IsBase64ImageCorrectSizeValidator`](IsBase64ImageCorrectSizeValidator.py) - Validates that the value is a base64 encoded string. -11. [`IsBase64ImageValidator`](IsBase64ImageValidator.py) - Validates that the value is a base64 encoded string. -12. [`IsBooleanValidator`](IsBooleanValidator.py) - Validates that the value is a boolean. -13. [`IsFloatValidator`](IsFloatValidator.py) - Validates that the value is a float. -14. [`IsFutureDateValidator`](IsFutureDateValidator.py) - Validates that the value is a future date. -15. [`IsHexadecimalValidator`](IsHexadecimalValidator.py) - Validates that the value is a hexadecimal string. -16. [`IsHorizontalImageValidator`](IsHorizontalImageValidator.py) - Validates that the value is a horizontally flipped image. -17. [`IsInstanceValidator`](IsInstanceValidator.py) - Validates that the value is an instance of a class. -18. [`IsIntegerValidator`](IsIntegerValidator.py) - Validates that the value is an integer. -19. [`IsJsonValidator`](IsJsonValidator.py) - Validates that the value is a json string. -20. [`IsPastDateValidator`](IsPastDateValidator.py) - Validates that the value is a past date. -21. [`IsStringValidator`](IsStringValidator.py) - Validates that the value is a string. -22. [`IsUUIDValidator`](IsUUIDValidator.py) - Validates that the value is a UUID. -23. [`IsVerticalImageValidator`](IsVerticalImageValidator.py) - Validates that the value is a vertically flipped image. -24. [`IsWeekdayValidator`](IsWeekdayValidator.py) - Validates that the value is a weekday. -25. [`IsWeekendValidator`](IsWeekendValidator.py) - Validates that the value is a weekend. -26. [`LengthValidator`](LengthValidator.py) - Validates the length of the value. -27. [`NotInArrayValidator`](NotInArrayValidator.py) - Validates that the value is not in the given array. -28. [`NotValidator`](NotValidator.py) - Validates that inverts the result of another validator. -29. [`RangeValidator`](RangeValidator.py) - Validates that the value is within a specified range. -30. [`RegexValidator`](RegexValidator.py) - Validates that the value matches a regex pattern. diff --git a/flask_inputfilter/Validator/RangeValidator.py b/flask_inputfilter/Validator/RangeValidator.py index 77d78cd..5916e1e 100644 --- a/flask_inputfilter/Validator/RangeValidator.py +++ b/flask_inputfilter/Validator/RangeValidator.py @@ -1,7 +1,7 @@ from typing import Any, Optional -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class RangeValidator(BaseValidator): diff --git a/flask_inputfilter/Validator/RegexValidator.py b/flask_inputfilter/Validator/RegexValidator.py index fd9741d..a4062c1 100644 --- a/flask_inputfilter/Validator/RegexValidator.py +++ b/flask_inputfilter/Validator/RegexValidator.py @@ -1,8 +1,8 @@ import re from typing import Optional -from ..Exception import ValidationError -from .BaseValidator import BaseValidator +from flask_inputfilter.Exception import ValidationError +from flask_inputfilter.Validator import BaseValidator class RegexValidator(BaseValidator): diff --git a/make.bat b/make.bat new file mode 100644 index 0000000..dc1312a --- /dev/null +++ b/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/source/changelog.rst b/source/changelog.rst new file mode 100644 index 0000000..60ae212 --- /dev/null +++ b/source/changelog.rst @@ -0,0 +1,92 @@ +Changelog +========= + +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. +- Documentary + +Filter +"""""" + +- New `Base64ImageDownscaleFilter `_ to reduce the size of an image. +- New `Base64ImageResizeFilter `_ to reduce the file size of an image. + +Validator +""""""""" + +- New `IsHorizontalImageValidator `_ to check if an image is horizontal. +- New `IsVerticalImageValidator `_ to check if an image is vertical. + +Changed +^^^^^^^ + +- Added `UnicodeFormEnum` to show possible config values for `ToNormalizedUnicodeFilter`. + Old config is still supported, but will be removed in a later version. + +[0.0.7.1] - 2025-01-16 +---------------------- + +Changed +^^^^^^^ + +- Updated `setup.py` to fix the issue with the missing subfolders. + +[0.0.7] - 2025-01-14 +-------------------- + +Added +^^^^^ + +- Workflow to run tests on all supported Python versions. [Check it out](.github/workflows/test_env.yaml) +- Added more test coverage for validators and filters. +- Added tracking of coverage in tests. `Check it out `_ +- New functionality for global filters and validators in `InputFilters`. +- New functionality to define custom supported methods. + +Validator +""""""""" + +- New `NotInArrayValidator `_ to check if a value is not in a list. +- New `NotValidator `_ to invert the result of another validator. + +[0.0.6] - 2025-01-12 +-------------------- + +Added +^^^^^ + +- New date validators and filters. + +Removed +^^^^^^^ + +- Dropped support for Python 3.6. + +[0.0.5] - 2025-01-12 +-------------------- + +Added +^^^^^ + +- 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](flask_inputfilter/Model/ExternalApiConfig.py) + +[0.0.4] - 2025-01-09 +-------------------- + +Added +^^^^^ + +- New external API functionality. [Check it out](docs/EXTERNAL_API.md) diff --git a/source/conf.py b/source/conf.py new file mode 100644 index 0000000..c251ae1 --- /dev/null +++ b/source/conf.py @@ -0,0 +1,27 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = "flask-inputfilter" +copyright = "2025, Leander Cain Slotosch" +author = "Leander Cain Slotosch" +release = "0.0.8" + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [] + +templates_path = [] +exclude_patterns = [] + + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = "alabaster" +html_static_path = [] diff --git a/CONTRIBUTING.md b/source/contributing.rst similarity index 65% rename from CONTRIBUTING.md rename to source/contributing.rst index 6a10d35..cc099ae 100644 --- a/CONTRIBUTING.md +++ b/source/contributing.rst @@ -1,9 +1,9 @@ -# Contributing +Contributing +============ -Thank you for considering contributing to the project. To make the process as easy and as effective as possible, -please follow the guidelines below. - -## Reporting Issues +Thank you for considering contributing to the project. To make the process as easy and as effective as possible, please follow the guidelines below. +Reporting Issues +---------------- If you find a bug or have a feature request, please open an issue on the project's GitHub repository. Before submitting an issue, please search the existing issues to see if your issue has already been reported. diff --git a/source/development.rst b/source/development.rst new file mode 100644 index 0000000..6685e93 --- /dev/null +++ b/source/development.rst @@ -0,0 +1,36 @@ +Development +=========== + +Build docker image +------------------- + +.. code-block:: bash + + docker build -t flask-inputfilter . + +Run docker container in interactive mode +---------------------------------------- + +.. code-block:: bash + + docker compose up -d + +.. code-block:: bash + + docker exec -it flask-inputfilter /bin/bash + +Run tests +--------- + +.. code-block:: bash + + docker exec -it flask-inputfilter pytest + +Run linting +----------- + +.. code-block:: bash + + docker exec -it flask-inputfilter sh -c "isort ." + docker exec -it flask-inputfilter sh -c "autoflake --in-place --remove-all-unused-imports --ignore-init-module-imports --recursive ." + docker exec -it flask-inputfilter black . diff --git a/source/guides/create_own.rst b/source/guides/create_own.rst new file mode 100644 index 0000000..214616f --- /dev/null +++ b/source/guides/create_own.rst @@ -0,0 +1,157 @@ +Create own +=========== + +Depending on what you want to build your own version from, there are different +parameters and return values required to work properly. + +- `Condition`_ +- `Filter`_ +- `Validator`_ + +Inside each ``__init__`` method you can add everything as you wish, +due it not being called in the validation process. +Its definition is optional. + +Condition +--------- + +First off with conditions. + +Their validation method is called ``check``. +It expects a dict from the type ``Dict[str, Any]`` whereas the key (``str``) of the +dictionary represents the name of a field and the value (``Any``) the corresponding value. +The dict represents the entirety of all fields present in the InputFilter it is called. + +The return value of the method should be ``bool``, representing if the condition is ``true`` or ``false``. +- ``true``: The condition is met and the validation can proceed +- ``false``: The validation is **not** met and the validation raises a ``ValidationError`` + +Example implementation +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + from typing import Any, Dict + + from flask_inputfilter.Condition import BaseCondition + + + class EqualCondition(BaseCondition): + """ + Condition that checks if two fields are equal. + """ + + def __init__(self, first_field: str, second_field: str) -> None: + self.first_field = first_field + self.second_field = second_field + + def check(self, data: Dict[str, Any]) -> bool: + return data.get(self.first_field) == data.get(self.second_field) + + +Filter +------ + +The next type we look at is filters. + +Their method called in the validation process is ``apply``. +It expects ``Any``, representing the value of the specific field, it is applied to. + +The return value should have the type ``Any``, representing either the successfully filtered value or +the value from the input. +The filter should **not** raise any ``ValidationError``, even if the value is not in the expected format +to be filtered properly. Instead, it should just return the unfiltered value from the parameters. +It is not a filers job to check weather a value meets the wanted format or not, that's what ``validators`` are for. + +Example implementation +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + from datetime import date, datetime + from typing import Any, Union + + from flask_inputfilter.Filter import BaseFilter + + + class ToDateTimeFilter(BaseFilter): + """ + Filter that converts a value to a datetime object. + Supports ISO 8601 formatted strings. + """ + + 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) + + except ValueError: + return value + + else: + return value + + +Validator +--------- + +The last type are the validators. + +This type is the most important one, as it is the one that raises ensures the value meets the wanted format. + +Their method called in the validation process is ``validate``. +It expects ``Any``, representing the value of the specific field, it is applied to. + +The return value should be ``None`` if the value meets the wanted format, and +it should raise a ``ValidationError`` if the validation fails. + +Example implementation +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + from typing import Any, List, Optional + + from flask_inputfilter.Exception import ValidationError + from flask_inputfilter.Validator import BaseValidator + + + class InArrayValidator(BaseValidator): + """ + Validator that checks if a value is in a given list of allowed values. + """ + + def __init__( + self, + haystack: List[Any], + strict: bool = False, + error_message: Optional[str] = None, + ) -> None: + self.haystack = haystack + self.strict = strict + self.error_message = error_message + + def validate(self, value: Any) -> None: + try: + if self.strict: + if value not in self.haystack or not any( + isinstance(value, type(item)) for item in self.haystack + ): + raise ValidationError + + else: + if value not in self.haystack: + raise ValidationError + + except Exception: + raise ValidationError( + self.error_message + or f"Value '{value}' is not in the allowed " + f"values '{self.haystack}'." + ) diff --git a/source/guides/index.rst b/source/guides/index.rst new file mode 100644 index 0000000..0956584 --- /dev/null +++ b/source/guides/index.rst @@ -0,0 +1,8 @@ +Guides +====== + +.. toctree:: + :maxdepth: 2 + :glob: + + create_own diff --git a/source/index.rst b/source/index.rst new file mode 100644 index 0000000..9f19363 --- /dev/null +++ b/source/index.rst @@ -0,0 +1,108 @@ +flask-inputfilter documentation +=============================== + +Overview +-------- + +.. toctree:: + :maxdepth: 1 + + options/index + guides/index + changelog + contributing + development + +Available functions: +-------------------- + +- :doc:`Creating your own Conditions, Filters and Validators ` +- :doc:`Conditions ` +- :doc:`Filter ` +- :doc:`Validator ` +- :doc:`ExternalApi ` + +Installation +------------ + +.. code-block:: bash + + pip install flask-inputfilter + +Quickstart +---------- + +To use the `InputFilter` class, create a new class that inherits from it and define the +fields you want to validate and filter. + +There are numerous filters and validators available, but you can also create your [`own`](CreateOwn.md). + +Definition +^^^^^^^^^^ + +.. code-block:: python + + from flask_inputfilter import InputFilter + from flask_inputfilter.Condition import ExactlyOneOfCondition + from flask_inputfilter.Enum import RegexEnum + from flask_inputfilter.Filter import StringTrimFilter, ToIntegerFilter, ToNullFilter + from flask_inputfilter.Validator import IsIntegerValidator, IsStringValidator, RegexValidator + + class UpdateZipcodeInputFilter(InputFilter): + def __init__(self): + super().__init__() + + self.add( + 'id', + required=True, + filters=[ToIntegerFilter(), ToNullFilter()], + validators=[ + IsIntegerValidator() + ] + ) + + self.add( + 'zipcode', + filters=[StringTrimFilter()], + validators=[ + RegexValidator( + RegexEnum.POSTAL_CODE.value, + 'The zipcode is not in the correct format.' + ) + ] + ) + + self.add( + 'city', + filters=[StringTrimFilter()], + validators=[ + IsStringValidator() + ] + ) + + self.addCondition( + ExactlyOneOfCondition(['zipcode', 'city']) + ) + +Usage +^^^^^ + +To use the `InputFilter` class, call the `validate` method on the class instance. +After calling `validate`, the validated data will be available in `g.validated_data`. +If the data is invalid, a 400 response with an error message will be returned. + +.. code-block:: python + + from flask import Flask, g + from your-path import UpdateZipcodeInputFilter + + app = Flask(__name__) + + @app.route('/update-zipcode', methods=['POST']) + @UpdateZipcodeInputFilter.validate() + def updateZipcode(): + data = g.validated_data + + # Do something with validated data + id = data.get('id') + zipcode = data.get('zipcode') diff --git a/source/options/condition.rst b/source/options/condition.rst new file mode 100644 index 0000000..30bf431 --- /dev/null +++ b/source/options/condition.rst @@ -0,0 +1,63 @@ +Condition +========= + +The `Condition` module contains the conditions that can be used to validate the input data. + +Conditions +---------- + +The `addCondition` method is used to add a condition between fields. + +Example +------- + +.. code-block:: python + + from flask_inputfilter import InputFilter + 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( + 'username', + filters=[StringTrimFilter()], + validators=[IsStringValidator()] + ) + + self.add( + 'name', + filters=[StringTrimFilter()], + validators=[IsStringValidator()] + ) + + self.addCondition( + OneOfCondition(['id', 'name']) + ) + +Available Conditions +-------------------- + +The following conditions are available in the `Condition` module: + +1. `ArrayLengthEqualCondition` - Validates that the length of the array is equal to the given value. +2. `ArrayLongerThanCondition` - Validates that the length of the array is longer than the given value. +3. `CustomCondition` - A custom condition that can be used to validate the input data. +4. `EqualCondition` - Validates that the input is equal to the given value. +5. `ExactlyNOfCondition` - Validates that exactly `n` of the given conditions are true. +6. `ExactlyNOfMatchesCondition` - Validates that exactly `n` of the given matches are true. +7. `ExactlyOneOfCondition` - Validates that exactly one of the given conditions is true. +8. `ExactlyOneOfMatchesCondition` - Validates that exactly one of the given matches is true. +9. `IntegerBiggerThanCondition` - Validates that the integer is bigger than the given value. +10. `NOfCondition` - Validates that at least `n` of the given conditions are true. +11. `NOfMatchesCondition` - Validates that at least `n` of the given matches are true. +12. `NotEqualCondition` - Validates that the input is not equal to the given value. +13. `OneOfCondition` - Validates that at least one of the given conditions is true. +14. `OneOfMatchesCondition` - Validates that at least one of the given matches is true. +15. `RequiredIfCondition` - Validates that the input is required if the given field has a specific value. +16. `StringLongerThanCondition` - Validates that the string is longer than the given value. +17. `TemporalOrderCondition` - Validates that the input is in correct temporal order. diff --git a/source/options/external_api.rst b/source/options/external_api.rst new file mode 100644 index 0000000..095264b --- /dev/null +++ b/source/options/external_api.rst @@ -0,0 +1,127 @@ +External API +============ + +This documentation provides a comprehensive overview of the external API functionality available in the `InputFilter` class. It covers the configuration, core methods, and examples of usage for interacting with external APIs. + +Overview +-------- + +The `InputFilter` class includes a mechanism for fetching data from external APIs during the input validation process. +This feature allows dynamic data retrieval based on user inputs, such as validating fields or fetching related data from an external service. + +.. note:: + + The external API functionality runs **after** all other filters and validators have been executed. + This means the data fetched from the external API will not be validated or filtered. + +Configuration +------------- + +The external API functionality is configured via the ``external_api`` parameter in the ``add`` method. This parameter accepts a dictionary with the following structure: + +Fields of `ExternalApiConfig` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: External API Configuration Fields + :header-rows: 1 + + * - Field + - Type + - Description + * - ``url`` + - ``str`` + - The URL of the external API, with optional placeholders in ``{{ }}`` format. + * - ``method`` + - ``str`` + - The HTTP method to use (e.g., ``GET``, ``POST``). + * - ``params`` + - ``Optional[Dict[str, str]]`` + - Query parameters for the API, with placeholders allowed. + * - ``data_key`` + - ``Optional[str]`` + - Key in the JSON response to extract the required data. + * - ``api_key`` + - ``Optional[str]`` + - API key for authorization, sent in the ``Authorization`` header. + +Examples +-------- + +Basic External API Integration +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + from flask_inputfilter.InputFilter import InputFilter + + class MyInputFilter(InputFilter): + def __init__(self): + super().__init__() + + self.add( + "user_id", required=True + ) + self.add( + "is_active", + required=True, + external_api={ + "url": "https://api.example.com/users/{{user_id}}/status", + "method": "GET", + "data_key": "is_active", + }, + ) + + # Example usage + filter_instance = MyInputFilter() + validated_data = filter_instance.validateData({"user_id": 123}) + print(validated_data["is_active"]) # True or False based on API response + +Using Query Parameters +^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + self.add( + "is_valid", + required=True, + external_api={ + "url": "https://api.example.com/validate", + "method": "GET", + "params": {"user": "{{user_id}}", "hash": "{{hash}}"}, + "data_key": "is_valid", + }, + ) + +This configuration sends the ``user_id`` and ``hash`` as query parameters, replacing the placeholders with validated data. + +Handling Fallback Values +^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + self.add( + "user_info", + required=True, + fallback={"name": "unknown", "age": 0}, + external_api={ + "url": "https://api.example.com/user/{{user_id}}", + "method": "GET", + "data_key": "user", + }, + ) + +Error Handling +-------------- + +- **ValidationError** is raised when: + - The API call returns a non-200 status code. + - A required field is missing and no fallback/default is provided. + - Validation of the field value fails. + +Best Practices +-------------- + +- **Required Fields:** Clearly define required fields and provide fallback values where necessary. +- **Placeholders:** Ensure placeholders in URLs and parameters match the keys in ``validated_data``. +- **Fallbacks:** Always provide fallback values for critical fields to avoid disruptions in case of API failure. +- **Security:** Use HTTPS for API calls and secure sensitive data like API keys. diff --git a/source/options/filter.rst b/source/options/filter.rst new file mode 100644 index 0000000..cf4d010 --- /dev/null +++ b/source/options/filter.rst @@ -0,0 +1,69 @@ +Filter +====== + +The ``Filter`` module contains the filters that can be used to filter the input data. + +Filters +------- + +Filters can be added into the ``add`` method for a specific field or as a global filter for all fields in ``addGlobalFilter``. + +The global filters will be executed before the specific field filtering. + +Example +------- + +.. code:: python + + from flask_inputfilter import InputFilter + from flask_inputfilter.Filter import StringTrimFilter, ToLowerFilter + + class TestInputFilter(InputFilter): + def __init__(self): + super().__init__() + + self.add( + 'username', + required=True, + filters=[StringTrimFilter()] + ) + + self.add( + 'name', + required=True, + filters=[StringTrimFilter()] + ) + + self.addGlobalFilter(ToLowerFilter()) + +Available Filters +----------------- + +The following filters are available in the ``Filter`` module: + +1. `ArrayExplodeFilter <../flask_inputfilter/Filter/ArrayExplodeFilter.py>`_ - Explodes the input string into an array. +2. `Base64ImageDownscaleFilter <../flask_inputfilter/Filter/Base64ImageDownscaleFilter.py>`_ - Downscale the base64 image. +3. `Base64ImageResizeFilter <../flask_inputfilter/Filter/Base64ImageResizeFilter.py>`_ - Resize the base64 image. +4. `BlacklistFilter <../flask_inputfilter/Filter/BlacklistFilter.py>`_ - Filters the string based on the blacklist. +5. `RemoveEmojisFilter <../flask_inputfilter/Filter/RemoveEmojisFilter.py>`_ - Removes the emojis from the string. +6. `SlugifyFilter <../flask_inputfilter/Filter/SlugifyFilter.py>`_ - Converts the string to a slug. +7. `StringTrimFilter <../flask_inputfilter/Filter/StringTrimFilter.py>`_ - Trims the whitespace from the beginning and end of the string. +8. `ToAlphaNumericFilter <../flask_inputfilter/Filter/ToAlphaNumericFilter.py>`_ - Converts the string to an alphanumeric string. +9. `ToBooleanFilter <../flask_inputfilter/Filter/ToBooleanFilter.py>`_ - Converts the string to a boolean value. +10. `ToCamelCaseFilter <../flask_inputfilter/Filter/ToCamelCaseFilter.py>`_ - Converts the string to camel case. +11. `ToDateFilter <../flask_inputfilter/Filter/ToDateFilter.py>`_ - Converts a string to a date value. +12. `ToDateTimeFilter <../flask_inputfilter/Filter/ToDateTimeFilter.py>`_ - Converts a string to a datetime value. +13. `ToEnumFilter <../flask_inputfilter/Filter/ToEnumFilter.py>`_ - Converts a string or integer to an enum value. +14. `ToFloatFilter <../flask_inputfilter/Filter/ToFloatFilter.py>`_ - Converts a string to a float value. +15. `ToIntegerFilter <../flask_inputfilter/Filter/ToIntegerFilter.py>`_ - Converts a string to an integer value. +16. `ToIsoFilter <../flask_inputfilter/Filter/ToIsoFilter.py>`_ - Converts a string to an ISO8601 date time value. +17. `ToLowerFilter <../flask_inputfilter/Filter/ToLowerFilter.py>`_ - Converts a string to lowercase. +18. `ToNormalizedUnicodeFilter <../flask_inputfilter/Filter/ToNormalizedUnicodeFilter.py>`_ - Normalizes a unicode string. +19. `ToNullFilter <../flask_inputfilter/Filter/ToNullFilter.py>`_ - Converts the string to ``None`` if it is already ``None`` or ``''`` (empty string). +20. `ToPascaleCaseFilter <../flask_inputfilter/Filter/ToPascaleCaseFilter.py>`_ - Converts the string to pascal case. +21. `ToSnakeCaseFilter <../flask_inputfilter/Filter/ToSnakeCaseFilter.py>`_ - Converts the string to snake case. +22. `ToStringFilter <../flask_inputfilter/Filter/ToStringFilter.py>`_ - Converts the input to a string value. +23. `ToUpperFilter <../flask_inputfilter/Filter/ToUpperFilter.py>`_ - Converts the string to uppercase. +24. `TruncateFilter <../flask_inputfilter/Filter/TruncateFilter.py>`_ - Truncates the string to the specified length. +25. `WhitelistFilter <../flask_inputfilter/Filter/WhitelistFilter.py>`_ - Filters the string based on the whitelist. +26. `WhitespaceCollapseFilter <../flask_inputfilter/Filter/WhitespaceCollapseFilter.py>`_ - Collapses the whitespace in the string. diff --git a/source/options/index.rst b/source/options/index.rst new file mode 100644 index 0000000..f96aba4 --- /dev/null +++ b/source/options/index.rst @@ -0,0 +1,69 @@ +Options +======= + +.. toctree:: + :maxdepth: 2 + + condition + validator + external_api + filter + +The `add` method supports several options: + +- `Required`_ +- `Filter`_ +- `Validator`_ +- `Default`_ +- `Fallback`_ +- `Steps`_ +- `ExternalApi`_ + +Required +-------- + +The ``required`` option specifies whether the field must be included in the input data. +If the field is missing, a ``ValidationError`` will be raised with an appropriate error message. + +Filter +------ + +The ``filters`` option allows you to specify one or more filters to apply to the field value. +Filters are applied in the order they are defined. + +For more information view the :doc:`Filter ` documentation. + +Validator +--------- + +The ``validators`` option allows you to specify one or more validators to apply to the field value. +Validators are applied in the order they are defined. + +For more information view the :doc:`Validator ` documentation. + +Default +------- + +The ``default`` option allows you to specify a default value to use if the field is not +present in the input data. + +Fallback +-------- + +The ``fallback`` option specifies a value to use if validation fails or required data +is missing. Note that if the field is optional and absent, ``fallback`` will not apply; +use ``default`` in such cases. + +Steps +----- + +The ``steps`` option allows you to specify a list of different filters and validator to apply to the field value. +It respects the order of the list. + +ExternalApi +----------- + +The ``external_api`` option allows you to specify an external API to call for the field value. +The API call is made when the field is validated, and the response is used as the field value. + +For more information view the :doc:`ExternalApi ` documentation. diff --git a/source/options/validator.rst b/source/options/validator.rst new file mode 100644 index 0000000..96887ad --- /dev/null +++ b/source/options/validator.rst @@ -0,0 +1,75 @@ +Validator +========= + +The `Validator` class is used to validate the data after the filters have been applied. + +Validators +---------- + +Validators can be added into the `add` method for a specific field or as a global validator for all fields in `addGlobalValidator`. + +The global validation will be executed before the specific field validation. + +Example +------- + +Here is an example of how to use validators in an `InputFilter`: + +.. code-block:: python + + from flask_inputfilter import InputFilter + from flask_inputfilter.Validator import IsIntegerValidator, RangeValidator + + + class UpdatePointsInputFilter(InputFilter): + def __init__(self): + super().__init__() + + self.add( + 'id', + required=True + ) + + self.add( + 'points', + required=True, + validators=[RangeValidator(min_value=0, max_value=10)] + ) + + self.addGlobalValidator(IsIntegerValidator()) + +Available Validators +-------------------- + +The following validators are available in the `Validator` module: + +1. :doc:`ArrayElementValidator <../flask_inputfilter/Validator/ArrayElementValidator>` +2. :doc:`ArrayLengthValidator <../flask_inputfilter/Validator/ArrayLengthValidator>` +3. :doc:`DateAfterValidator <../flask_inputfilter/Validator/DateAfterValidator>` +4. :doc:`DateBeforeValidator <../flask_inputfilter/Validator/DateBeforeValidator>` +5. :doc:`DateRangeValidator <../flask_inputfilter/Validator/DateRangeValidator>` +6. :doc:`FloatPrecisionValidator <../flask_inputfilter/Validator/FloatPrecisionValidator>` +7. :doc:`InArrayValidator <../flask_inputfilter/Validator/InArrayValidator>` +8. :doc:`InEnumValidator <../flask_inputfilter/Validator/InEnumValidator>` +9. :doc:`IsArrayValidator <../flask_inputfilter/Validator/IsArrayValidator>` +10. :doc:`IsBase64ImageCorrectSizeValidator <../flask_inputfilter/Validator/IsBase64ImageCorrectSizeValidator>` +11. :doc:`IsBase64ImageValidator <../flask_inputfilter/Validator/IsBase64ImageValidator>` +12. :doc:`IsBooleanValidator <../flask_inputfilter/Validator/IsBooleanValidator>` +13. :doc:`IsFloatValidator <../flask_inputfilter/Validator/IsFloatValidator>` +14. :doc:`IsFutureDateValidator <../flask_inputfilter/Validator/IsFutureDateValidator>` +15. :doc:`IsHexadecimalValidator <../flask_inputfilter/Validator/IsHexadecimalValidator>` +16. :doc:`IsHorizontalImageValidator <../flask_inputfilter/Validator/IsHorizontalImageValidator>` +17. :doc:`IsInstanceValidator <../flask_inputfilter/Validator/IsInstanceValidator>` +18. :doc:`IsIntegerValidator <../flask_inputfilter/Validator/IsIntegerValidator>` +19. :doc:`IsJsonValidator <../flask_inputfilter/Validator/IsJsonValidator>` +20. :doc:`IsPastDateValidator <../flask_inputfilter/Validator/IsPastDateValidator>` +21. :doc:`IsStringValidator <../flask_inputfilter/Validator/IsStringValidator>` +22. :doc:`IsUUIDValidator <../flask_inputfilter/Validator/IsUUIDValidator>` +23. :doc:`IsVerticalImageValidator <../flask_inputfilter/Validator/IsVerticalImageValidator>` +24. :doc:`IsWeekdayValidator <../flask_inputfilter/Validator/IsWeekdayValidator>` +25. :doc:`IsWeekendValidator <../flask_inputfilter/Validator/IsWeekendValidator>` +26. :doc:`LengthValidator <../flask_inputfilter/Validator/LengthValidator>` +27. :doc:`NotInArrayValidator <../flask_inputfilter/Validator/NotInArrayValidator>` +28. :doc:`NotValidator <../flask_inputfilter/Validator/NotValidator>` +29. :doc:`RangeValidator <../flask_inputfilter/Validator/RangeValidator>` +30. :doc:`RegexValidator <../flask_inputfilter/Validator/RegexValidator>`