Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""LICENSE
2Copyright 2020 Hermann Krumrey <hermann@krumreyh.com>
4This file is part of stockstert.
6stockstert is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 3 of the License, or
9(at your option) any later version.
11stockstert is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
16You should have received a copy of the GNU General Public License
17along with stockstert. If not, see <http://www.gnu.org/licenses/>.
18LICENSE"""
20from functools import wraps
21from typing import Callable
22from flask import jsonify, make_response, request
23from werkzeug.exceptions import Unauthorized
24from stockstert.exceptions import ApiException
27def api(func: Callable) -> Callable:
28 """
29 Decorator that handles common API patterns and ensures that
30 the JSON response will always follow a certain pattern
31 :param func: The function to wrap
32 :return: The wrapper function
33 """
35 @wraps(func)
36 def wrapper(*args, **kwargs):
37 """
38 Tries running the function and checks for errors
39 :param args: args
40 :param kwargs: kwargs
41 :return: The JSON response including an appropriate HTTP status code
42 """
43 code = 200
44 response = {"status": "ok"}
46 try:
47 if request.method in ["POST", "PUT"] and \
48 (not request.content_type.startswith("application/json")
49 or not request.is_json
50 or not isinstance(request.get_json(silent=True), dict)):
51 raise ApiException(
52 "Not in JSON format", "Not in JSON format"
53 )
55 response["data"] = func(*args, **kwargs)
57 except (KeyError, TypeError, ValueError, ApiException) as e:
59 response["status"] = "error"
61 if isinstance(e, ApiException):
62 code = e.status_code
63 response["reason"] = e.reason
65 else: # pragma: no cover
66 code = 400
67 response["reason"] = "Bad Request"
69 return make_response(jsonify(response), code)
71 return wrapper
74def api_login_required(func: Callable) -> Callable:
75 """
76 Decorator to make unauthorized API calls respond with JSON properly
77 :param func: The function to wrap
78 :return: The wrapped function
79 """
81 @wraps(func)
82 def wrapper(*args, **kwargs):
83 """
84 Checks if flask-login throws an Unauthorized exception. If so,
85 re-wrap the response in JSON
86 :param args: The function arguments
87 :param kwargs: The function keyword arguments
88 :return: The newly wrapped response,
89 or just the plain response if authorized
90 """
92 try:
93 resp = func(*args, **kwargs)
94 return resp
95 except Unauthorized:
96 return make_response(
97 jsonify({"status": "error", "reason": "Unauthorized"}), 401
98 )
100 return wrapper