Skip to content

Commit 9e09847

Browse files
committed
Implement #43: Allow parameters to accept ingress from multiple possible sources
1 parent abcb480 commit 9e09847

File tree

7 files changed

+835
-120
lines changed

7 files changed

+835
-120
lines changed

README.md

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -61,48 +61,63 @@ def error_handler(err):
6161
"error_message": str(err)
6262
}, 400
6363

64-
@ValidateParameters(error_handler)
6564
@app.route(...)
65+
@ValidateParameters(error_handler)
6666
def api(...)
6767
```
6868

6969
### Specify Parameter types and constraints with type hints and subclasses of Parameter
7070
#### Parameter Class
7171
The `Parameter` class provides a base for validation common among all input types, all location-specific classes extend `Parameter`. These subclasses are:
7272

73-
| Subclass Name | Input Source | Available For |
74-
|---------------|------------------------------------------------------------------------------------------------------------------------|------------------|
75-
| Route | Parameter passed in the pathname of the URL, such as `/users/<int:id>` | All HTTP Methods |
76-
| Form | Parameter in an HTML form or a `FormData` object in the request body, often with `Content-Type: x-www-form-urlencoded` | POST Methods |
77-
| Json | Parameter in the JSON object in the request body, must have header `Content-Type: application/json` | POST Method |
78-
| Query | Parameter in the query of the URL, such as /news_article?id=55 | All HTTP Methods |
79-
| File | Parameter is a file uploaded in the request body | POST Method |
73+
| Subclass Name | Input Source | Available For |
74+
|---------------|------------------------------------------------------------------------------------------------------------------------|---------------------------------|
75+
| Route | Parameter passed in the pathname of the URL, such as `/users/<int:id>` | All HTTP Methods |
76+
| Form | Parameter in an HTML form or a `FormData` object in the request body, often with `Content-Type: x-www-form-urlencoded` | POST Methods |
77+
| Json | Parameter in the JSON object in the request body, must have header `Content-Type: application/json` | POST Method |
78+
| Query | Parameter in the query of the URL, such as /news_article?id=55 | All HTTP Methods |
79+
| File | Parameter is a file uploaded in the request body | POST Method |
80+
| MultiSource | Parameter is in one of the locations provided to the constructor | Dependent on selected locations |
81+
82+
##### MultiSource Parameters
83+
Using the `MultiSource` parameter type, parameters can be accepted from any combination of `Parameter` subclasses. Example usage is as follows:
84+
85+
```py
86+
@app.route("/")
87+
@app.route("/<v>") # If accepting parameters by Route and another type, a path with and without that Route parameter must be specified
88+
@ValidateParameters()
89+
def multi_source_example(
90+
value: int = MultiSource([Route(), Query(), Json()])
91+
)
92+
```
93+
94+
The above example will accept parameters passed to the route through Route, Query, and JSON Body. Validation options must be specified on each constructor in order to be processed.
8095

8196
#### Type Hints and Accepted Input Types
8297
Type Hints allow for inline specification of the input type of a parameter. Some types are only available to certain `Parameter` subclasses.
8398

84-
| Type Hint / Expected Python Type | Notes | `Route` | `Form` | `Json` | `Query` | `File` |
85-
|------------------------------------|--------------------------------------------------------------------------------------------------------------------------------|---------|--------|--------|---------|--------|
86-
| `str` | | Y | Y | Y | Y | N |
87-
| `int` | | Y | Y | Y | Y | N |
88-
| `bool` | | Y | Y | Y | Y | N |
89-
| `float` | | Y | Y | Y | Y | N |
90-
| `typing.List` (must not be `list`) | For `Query` inputs, users can pass via either `value=1&value=2&value=3`, or `value=1,2,3`, both will be transformed to a `list`. | N | Y | Y | Y | N |
91-
| `typing.Union` | | Y | Y | Y | Y | N |
92-
| `typing.Optional` | | Y | Y | Y | Y | Y |
93-
| `datetime.datetime` | received as a `str` in ISO-8601 date-time format | Y | Y | Y | Y | N |
94-
| `datetime.date` | received as a `str` in ISO-8601 full-date format | Y | Y | Y | Y | N |
95-
| `datetime.time` | received as a `str` in ISO-8601 partial-time format | Y | Y | Y | Y | N |
96-
| `dict` | | N | N | Y | N | N |
97-
| `FileStorage` | | N | N | N | N | Y |
99+
| Type Hint / Expected Python Type | Notes | `Route` | `Form` | `Json` | `Query` | `File` |
100+
|------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------|---------|--------|--------|---------|--------|
101+
| `str` | | Y | Y | Y | Y | N |
102+
| `int` | | Y | Y | Y | Y | N |
103+
| `bool` | | Y | Y | Y | Y | N |
104+
| `float` | | Y | Y | Y | Y | N |
105+
| `typing.List` (must not be `list`) | For `Query` and `Form` inputs, users can pass via either `value=1&value=2&value=3`, or `value=1,2,3`, both will be transformed to a `list`. | N | Y | Y | Y | N |
106+
| `typing.Union` | Cannot be used inside of `typing.List` | Y | Y | Y | Y | N |
107+
| `typing.Optional` | | Y | Y | Y | Y | Y |
108+
| `datetime.datetime` | Received as a `str` in ISO-8601 date-time format | Y | Y | Y | Y | N |
109+
| `datetime.date` | Received as a `str` in ISO-8601 full-date format | Y | Y | Y | Y | N |
110+
| `datetime.time` | Received as a `str` in ISO-8601 partial-time format | Y | Y | Y | Y | N |
111+
| `dict` | For `Query` and `Form` inputs, users should pass the stringified JSON | N | N | Y | N | N |
112+
| `FileStorage` | | N | N | N | N | Y |
98113

99114
These can be used in tandem to describe a parameter to validate: `parameter_name: type_hint = ParameterSubclass()`
100115
- `parameter_name`: The field name itself, such as username
101116
- `type_hint`: The expected Python data type
102117
- `ParameterSubclass`: An instance of a subclass of `Parameter`
103118

104119
### Validation with arguments to Parameter
105-
Validation beyond type-checking can be done by passing arguments into the constructor of the `Parameter` subclass. The arguments available for use on each type hint are:
120+
Validation beyond type-checking can be done by passing arguments into the constructor of the `Parameter` subclass (with the exception of `MultiSource`). The arguments available for use on each type hint are:
106121

107122
| Parameter Name | Type of Parameter | Effective On Types | Description |
108123
|-------------------|---------------------------------------------|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|

flask_parameter_validation/parameter_types/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
from .json import Json
44
from .query import Query
55
from .route import Route
6+
from .multi_source import MultiSource
67

78
__all__ = [
8-
"File", "Form", "Json", "Query", "Route"
9+
"File", "Form", "Json", "Query", "Route", "MultiSource"
910
]
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from flask_parameter_validation.parameter_types.parameter import Parameter
2+
3+
4+
class MultiSource(Parameter):
5+
name = "multi_source"
6+
7+
def __init__(self, sources: list[Parameter], default=None, **kwargs):
8+
self.sources = sources
9+
super().__init__(default, **kwargs)

0 commit comments

Comments
 (0)