Skip to content
This repository was archived by the owner on Nov 19, 2024. It is now read-only.

Commit 0105eb3

Browse files
committed
Decorators: Now using error handlers only if registered for validate_params
1 parent 9c9bab0 commit 0105eb3

File tree

7 files changed

+292
-23
lines changed

7 files changed

+292
-23
lines changed

flask_utils/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Increment versions here according to SemVer
2-
__version__ = "0.6.1"
2+
__version__ = "0.7.0"
33

44
from flask_utils.utils import is_it_true
55
from flask_utils.errors import GoneError

flask_utils/decorators.py

Lines changed: 99 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@
88
from typing import get_origin
99
from functools import wraps
1010

11+
from flask import jsonify
1112
from flask import request
13+
from flask import current_app
14+
from flask import make_response
1215
from werkzeug.exceptions import BadRequest
1316
from werkzeug.exceptions import UnsupportedMediaType
1417

1518
from flask_utils.errors import BadRequestError
1619

17-
# TODO: Change validate_params to either use BadRequestError or just return a 400 depending
18-
# on if the error handler is registered or not in the FlaskUtils class
19-
2020
VALIDATE_PARAMS_MAX_DEPTH = 4
2121

2222

@@ -236,41 +236,125 @@ def example():
236236
* Optional
237237
* Union
238238
239+
.. versionchanged:: 0.7.0
240+
The decorator will now use the custom error handlers if ``register_error_handlers`` has been set to ``True``
241+
when initializing the :class:`~flask_utils.extension.FlaskUtils` extension.
242+
239243
.. versionadded:: 0.2.0
240244
"""
241245

242246
def decorator(fn): # type: ignore
243247
@wraps(fn)
244248
def wrapper(*args, **kwargs): # type: ignore
249+
use_error_handlers = False
250+
if current_app.extensions.get("flask_utils") is not None:
251+
if current_app.extensions["flask_utils"].has_error_handlers_registered:
252+
use_error_handlers = True
253+
245254
try:
246255
data = request.get_json()
247256
except BadRequest as e:
248-
raise BadRequestError("The Json Body is malformed.") from e
257+
if use_error_handlers:
258+
raise BadRequestError("The Json Body is malformed.") from e
259+
else:
260+
return make_response(
261+
jsonify(
262+
{
263+
"error": "The Json Body is malformed.",
264+
}
265+
),
266+
400,
267+
)
249268
except UnsupportedMediaType as e:
250-
raise BadRequestError(
251-
"The Content-Type header is missing or is not set to application/json, or the JSON body is missing."
252-
) from e
269+
if use_error_handlers:
270+
raise BadRequestError(
271+
"The Content-Type header is missing or is not set to application/json, "
272+
"or the JSON body is missing."
273+
) from e
274+
else:
275+
return make_response(
276+
jsonify(
277+
{
278+
"error": "The Content-Type header is missing or is not set to application/json, "
279+
"or the JSON body is missing.",
280+
}
281+
),
282+
400,
283+
)
253284

254285
if not data:
255-
raise BadRequestError("Missing json body.")
286+
if use_error_handlers:
287+
raise BadRequestError("Missing json body.")
288+
else:
289+
return make_response(
290+
jsonify(
291+
{
292+
"error": "Missing json body.",
293+
}
294+
),
295+
400,
296+
)
256297

257298
if not isinstance(data, dict):
258-
raise BadRequestError("JSON body must be a dict")
299+
if use_error_handlers:
300+
raise BadRequestError("JSON body must be a dict")
301+
else:
302+
return make_response(
303+
jsonify(
304+
{
305+
"error": "JSON body must be a dict",
306+
}
307+
),
308+
400,
309+
)
259310

260311
for key, type_hint in parameters.items():
261312
if not _is_optional(type_hint) and key not in data:
262-
raise BadRequestError(f"Missing key: {key}", f"Expected keys are: {parameters.keys()}")
313+
if use_error_handlers:
314+
raise BadRequestError(f"Missing key: {key}", f"Expected keys are: {parameters.keys()}")
315+
else:
316+
return make_response(
317+
jsonify(
318+
{
319+
"error": f"Missing key: {key}",
320+
"expected_keys": parameters.keys(),
321+
}
322+
),
323+
400,
324+
)
263325

264326
for key in data:
265327
if key not in parameters:
266-
raise BadRequestError(
267-
f"Unexpected key: {key}.",
268-
f"Expected keys are: {parameters.keys()}",
269-
)
328+
if use_error_handlers:
329+
raise BadRequestError(
330+
f"Unexpected key: {key}.",
331+
f"Expected keys are: {parameters.keys()}",
332+
)
333+
else:
334+
return make_response(
335+
jsonify(
336+
{
337+
"error": f"Unexpected key: {key}.",
338+
"expected_keys": parameters.keys(),
339+
}
340+
),
341+
400,
342+
)
270343

271344
for key in data:
272345
if key in parameters and not _check_type(data[key], parameters[key], allow_empty):
273-
raise BadRequestError(f"Wrong type for key {key}.", f"It should be {parameters[key]}")
346+
if use_error_handlers:
347+
raise BadRequestError(f"Wrong type for key {key}.", f"It should be {parameters[key]}")
348+
else:
349+
return make_response(
350+
jsonify(
351+
{
352+
"error": f"Wrong type for key {key}.",
353+
"expected_type": parameters[key],
354+
}
355+
),
356+
400,
357+
)
274358

275359
return fn(*args, **kwargs)
276360

flask_utils/errors/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def _register_error_handlers(application: Flask) -> None:
2424
2525
.. versionchanged:: 0.5.0
2626
Made the function private. If you want to register the custom error handlers, you need to
27-
pass `register_error_handlers=True` to the :class:`~flask_utils.extension.FlaskUtils` class
27+
pass ``register_error_handlers=True`` to the :class:`~flask_utils.extension.FlaskUtils` class
2828
or to :meth:`~flask_utils.extension.FlaskUtils.init_app`
2929
3030
.. code-block:: python

flask_utils/extension.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,21 +34,30 @@ class FlaskUtils(object):
3434
"""
3535

3636
def __init__(self, app: Optional[Flask] = None, register_error_handlers: bool = True):
37+
self.has_error_handlers_registered = False
38+
3739
if app is not None:
3840
self.init_app(app, register_error_handlers)
3941

4042
def init_app(self, app: Flask, register_error_handlers: bool = True) -> None:
41-
"""Initialize a Flask application for use with this extension instance. This
43+
"""
44+
:param app: The Flask application to initialize.
45+
:param register_error_handlers: Register the custom error handlers. Default is ``True``.
46+
47+
Initialize a Flask application for use with this extension instance. This
4248
must be called before any request is handled by the application.
4349
4450
If the app is created with the factory pattern, this should be called after the app
4551
is created to configure the extension.
4652
47-
If `register_error_handlers` is True, the custom error handlers will be registered and
48-
can then be used in routes to raise errors.
53+
If ``register_error_handlers`` is ``True``, the custom error handlers will be registered and
54+
can then be used in routes to raise errors. This is enabled by default.
55+
The decorator :func:`~flask_utils.decorators.validate_params` will also use the custom error handlers
56+
if set to ``True``.
4957
50-
:param app: The Flask application to initialize.
51-
:param register_error_handlers: Register the custom error handlers. Default is True.
58+
.. versionchanged:: 0.7.0
59+
Setting ``register_error_handlers`` to True will now enable using the custom error handlers
60+
in the :func:`~flask_utils.decorators.validate_params`. decorator.
5261
5362
:Example:
5463
@@ -65,5 +74,6 @@ def init_app(self, app: Flask, register_error_handlers: bool = True) -> None:
6574
"""
6675
if register_error_handlers:
6776
_register_error_handlers(app)
77+
self.has_error_handlers_registered = True
6878

6979
app.extensions["flask_utils"] = self

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ dev-dependencies = [
4545
"pre-commit>=3.5.0",
4646
"mypy>=1.11.2",
4747
"twine>=5.1.1",
48+
"sphinx>=7.1.2",
49+
"pallets-sphinx-themes>=2.1.3",
4850
]
4951

5052

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from flask import Flask
2+
3+
from flask_utils import validate_params
4+
5+
6+
def test_validate_params_without_error_handlers():
7+
app = Flask(__name__)
8+
app.testing = True
9+
10+
@app.route("/example")
11+
@validate_params({"name": str})
12+
def example():
13+
return "OK", 200
14+
15+
response = app.test_client().get("/example")
16+
assert response.status_code == 400
17+
assert (
18+
response.json["error"]
19+
== "The Content-Type header is missing or is not set to application/json, or the JSON body is missing."
20+
)
21+
assert "success" not in response.json
22+
assert "code" not in response.json
23+
assert not isinstance(response.json["error"], dict)
24+
25+
26+
# TODO: Test all possible errors that can be raised by validate_params

0 commit comments

Comments
 (0)