API - authentication decorators return user directly instead of user id

This commit is contained in:
Sam 2021-12-01 19:22:47 +01:00
parent d9048032e4
commit 00b6e05805
10 changed files with 88 additions and 133 deletions

View File

@ -11,6 +11,8 @@ build-client: lint-client
check-all: lint-all type-check test-python test-client check-all: lint-all type-check test-python test-client
check-python: lint-python type-check test-python
clean: clean:
rm -rf .mypy_cache rm -rf .mypy_cache
rm -rf .pytest_cache rm -rf .pytest_cache

View File

@ -10,6 +10,7 @@ from fittrackee.responses import (
handle_error_and_return_response, handle_error_and_return_response,
) )
from fittrackee.users.decorators import authenticate_as_admin from fittrackee.users.decorators import authenticate_as_admin
from fittrackee.users.models import User
from .models import AppConfig from .models import AppConfig
from .utils import update_app_config_from_database, verify_app_config from .utils import update_app_config_from_database, verify_app_config
@ -64,7 +65,7 @@ def get_application_config() -> Union[Dict, HttpResponse]:
@config_blueprint.route('/config', methods=['PATCH']) @config_blueprint.route('/config', methods=['PATCH'])
@authenticate_as_admin @authenticate_as_admin
def update_application_config(auth_user_id: int) -> Union[Dict, HttpResponse]: def update_application_config(auth_user: User) -> Union[Dict, HttpResponse]:
""" """
Update Application config Update Application config
@ -95,8 +96,6 @@ def update_application_config(auth_user_id: int) -> Union[Dict, HttpResponse]:
"status": "success" "status": "success"
} }
:param integer auth_user_id: authenticate user id (from JSON Web Token)
:<json integer gpx_limit_import: max number of files in zip archive :<json integer gpx_limit_import: max number of files in zip archive
:<json boolean is_registration_enabled: is registration enabled ? :<json boolean is_registration_enabled: is registration enabled ?
:<json integer max_single_file_size: max size of a single file :<json integer max_single_file_size: max size of a single file

View File

@ -224,7 +224,7 @@ def login_user() -> Union[Dict, HttpResponse]:
@auth_blueprint.route('/auth/logout', methods=['GET']) @auth_blueprint.route('/auth/logout', methods=['GET'])
@authenticate @authenticate
def logout_user(auth_user_id: int) -> Union[Dict, HttpResponse]: def logout_user(auth_user: User) -> Union[Dict, HttpResponse]:
""" """
user logout user logout
@ -274,7 +274,7 @@ def logout_user(auth_user_id: int) -> Union[Dict, HttpResponse]:
auth_token = auth_header.split(' ')[1] auth_token = auth_header.split(' ')[1]
resp = User.decode_auth_token(auth_token) resp = User.decode_auth_token(auth_token)
if isinstance(auth_user_id, str): if isinstance(resp, str):
return UnauthorizedErrorResponse(resp) return UnauthorizedErrorResponse(resp)
return { return {
@ -286,7 +286,7 @@ def logout_user(auth_user_id: int) -> Union[Dict, HttpResponse]:
@auth_blueprint.route('/auth/profile', methods=['GET']) @auth_blueprint.route('/auth/profile', methods=['GET'])
@authenticate @authenticate
def get_authenticated_user_profile( def get_authenticated_user_profile(
auth_user_id: int, auth_user: User,
) -> Union[Dict, HttpResponse]: ) -> Union[Dict, HttpResponse]:
""" """
get authenticated user info get authenticated user info
@ -381,13 +381,12 @@ def get_authenticated_user_profile(
- invalid token, please log in again - invalid token, please log in again
""" """
user = User.query.filter_by(id=auth_user_id).first() return {'status': 'success', 'data': auth_user.serialize()}
return {'status': 'success', 'data': user.serialize()}
@auth_blueprint.route('/auth/profile/edit', methods=['POST']) @auth_blueprint.route('/auth/profile/edit', methods=['POST'])
@authenticate @authenticate
def edit_user(auth_user_id: int) -> Union[Dict, HttpResponse]: def edit_user(auth_user: User) -> Union[Dict, HttpResponse]:
""" """
edit authenticated user edit authenticated user
@ -523,24 +522,23 @@ def edit_user(auth_user_id: int) -> Union[Dict, HttpResponse]:
).decode() ).decode()
try: try:
user = User.query.filter_by(id=auth_user_id).first() auth_user.first_name = first_name
user.first_name = first_name auth_user.last_name = last_name
user.last_name = last_name auth_user.bio = bio
user.bio = bio auth_user.location = location
user.location = location auth_user.birth_date = (
user.birth_date = (
datetime.datetime.strptime(birth_date, '%Y-%m-%d') datetime.datetime.strptime(birth_date, '%Y-%m-%d')
if birth_date if birth_date
else None else None
) )
if password is not None and password != '': if password is not None and password != '':
user.password = password auth_user.password = password
db.session.commit() db.session.commit()
return { return {
'status': 'success', 'status': 'success',
'message': 'user profile updated', 'message': 'user profile updated',
'data': user.serialize(), 'data': auth_user.serialize(),
} }
# handler errors # handler errors
@ -550,7 +548,7 @@ def edit_user(auth_user_id: int) -> Union[Dict, HttpResponse]:
@auth_blueprint.route('/auth/profile/edit/preferences', methods=['POST']) @auth_blueprint.route('/auth/profile/edit/preferences', methods=['POST'])
@authenticate @authenticate
def edit_user_preferences(auth_user_id: int) -> Union[Dict, HttpResponse]: def edit_user_preferences(auth_user: User) -> Union[Dict, HttpResponse]:
""" """
edit authenticated user preferences edit authenticated user preferences
@ -670,17 +668,16 @@ def edit_user_preferences(auth_user_id: int) -> Union[Dict, HttpResponse]:
weekm = post_data.get('weekm') weekm = post_data.get('weekm')
try: try:
user = User.query.filter_by(id=auth_user_id).first() auth_user.imperial_units = imperial_units
user.imperial_units = imperial_units auth_user.language = language
user.language = language auth_user.timezone = timezone
user.timezone = timezone auth_user.weekm = weekm
user.weekm = weekm
db.session.commit() db.session.commit()
return { return {
'status': 'success', 'status': 'success',
'message': 'user preferences updated', 'message': 'user preferences updated',
'data': user.serialize(), 'data': auth_user.serialize(),
} }
# handler errors # handler errors
@ -691,7 +688,7 @@ def edit_user_preferences(auth_user_id: int) -> Union[Dict, HttpResponse]:
@auth_blueprint.route('/auth/profile/edit/sports', methods=['POST']) @auth_blueprint.route('/auth/profile/edit/sports', methods=['POST'])
@authenticate @authenticate
def edit_user_sport_preferences( def edit_user_sport_preferences(
auth_user_id: int, auth_user: User,
) -> Union[Dict, HttpResponse]: ) -> Union[Dict, HttpResponse]:
""" """
edit authenticated user sport preferences edit authenticated user sport preferences
@ -758,12 +755,12 @@ def edit_user_sport_preferences(
try: try:
user_sport = UserSportPreference.query.filter_by( user_sport = UserSportPreference.query.filter_by(
user_id=auth_user_id, user_id=auth_user.id,
sport_id=sport_id, sport_id=sport_id,
).first() ).first()
if not user_sport: if not user_sport:
user_sport = UserSportPreference( user_sport = UserSportPreference(
user_id=auth_user_id, user_id=auth_user.id,
sport_id=sport_id, sport_id=sport_id,
stopped_speed_threshold=sport.stopped_speed_threshold, stopped_speed_threshold=sport.stopped_speed_threshold,
) )
@ -792,7 +789,7 @@ def edit_user_sport_preferences(
@auth_blueprint.route('/auth/picture', methods=['POST']) @auth_blueprint.route('/auth/picture', methods=['POST'])
@authenticate @authenticate
def edit_picture(auth_user_id: int) -> Union[Dict, HttpResponse]: def edit_picture(auth_user: User) -> Union[Dict, HttpResponse]:
""" """
update authenticated user picture update authenticated user picture
@ -848,23 +845,22 @@ def edit_picture(auth_user_id: int) -> Union[Dict, HttpResponse]:
file = request.files['file'] file = request.files['file']
filename = secure_filename(file.filename) # type: ignore filename = secure_filename(file.filename) # type: ignore
dirpath = os.path.join( dirpath = os.path.join(
current_app.config['UPLOAD_FOLDER'], 'pictures', str(auth_user_id) current_app.config['UPLOAD_FOLDER'], 'pictures', str(auth_user.id)
) )
if not os.path.exists(dirpath): if not os.path.exists(dirpath):
os.makedirs(dirpath) os.makedirs(dirpath)
absolute_picture_path = os.path.join(dirpath, filename) absolute_picture_path = os.path.join(dirpath, filename)
relative_picture_path = os.path.join( relative_picture_path = os.path.join(
'pictures', str(auth_user_id), filename 'pictures', str(auth_user.id), filename
) )
try: try:
user = User.query.filter_by(id=auth_user_id).first() if auth_user.picture is not None:
if user.picture is not None: old_picture_path = get_absolute_file_path(auth_user.picture)
old_picture_path = get_absolute_file_path(user.picture)
if os.path.isfile(get_absolute_file_path(old_picture_path)): if os.path.isfile(get_absolute_file_path(old_picture_path)):
os.remove(old_picture_path) os.remove(old_picture_path)
file.save(absolute_picture_path) file.save(absolute_picture_path)
user.picture = relative_picture_path auth_user.picture = relative_picture_path
db.session.commit() db.session.commit()
return { return {
'status': 'success', 'status': 'success',
@ -879,7 +875,7 @@ def edit_picture(auth_user_id: int) -> Union[Dict, HttpResponse]:
@auth_blueprint.route('/auth/picture', methods=['DELETE']) @auth_blueprint.route('/auth/picture', methods=['DELETE'])
@authenticate @authenticate
def del_picture(auth_user_id: int) -> Union[Tuple[Dict, int], HttpResponse]: def del_picture(auth_user: User) -> Union[Tuple[Dict, int], HttpResponse]:
""" """
delete authenticated user picture delete authenticated user picture
@ -908,11 +904,10 @@ def del_picture(auth_user_id: int) -> Union[Tuple[Dict, int], HttpResponse]:
""" """
try: try:
user = User.query.filter_by(id=auth_user_id).first() picture_path = get_absolute_file_path(auth_user.picture)
picture_path = get_absolute_file_path(user.picture)
if os.path.isfile(picture_path): if os.path.isfile(picture_path):
os.remove(picture_path) os.remove(picture_path)
user.picture = None auth_user.picture = None
db.session.commit() db.session.commit()
return {'status': 'no content'}, 204 return {'status': 'no content'}, 204
except (exc.IntegrityError, ValueError) as e: except (exc.IntegrityError, ValueError) as e:

View File

@ -13,11 +13,10 @@ def authenticate(f: Callable) -> Callable:
def decorated_function( def decorated_function(
*args: Any, **kwargs: Any *args: Any, **kwargs: Any
) -> Union[Callable, HttpResponse]: ) -> Union[Callable, HttpResponse]:
verify_admin = False response_object, user = verify_user(request, verify_admin=False)
response_object, resp = verify_user(request, verify_admin)
if response_object: if response_object:
return response_object return response_object
return f(resp, *args, **kwargs) return f(user, *args, **kwargs)
return decorated_function return decorated_function
@ -27,10 +26,9 @@ def authenticate_as_admin(f: Callable) -> Callable:
def decorated_function( def decorated_function(
*args: Any, **kwargs: Any *args: Any, **kwargs: Any
) -> Union[Callable, HttpResponse]: ) -> Union[Callable, HttpResponse]:
verify_admin = True response_object, user = verify_user(request, verify_admin=True)
response_object, resp = verify_user(request, verify_admin)
if response_object: if response_object:
return response_object return response_object
return f(resp, *args, **kwargs) return f(user, *args, **kwargs)
return decorated_function return decorated_function

View File

@ -27,7 +27,7 @@ USER_PER_PAGE = 10
@users_blueprint.route('/users', methods=['GET']) @users_blueprint.route('/users', methods=['GET'])
@authenticate @authenticate
def get_users(auth_user_id: int) -> Dict: def get_users(auth_user: User) -> Dict:
""" """
Get all users Get all users
@ -144,8 +144,6 @@ def get_users(auth_user_id: int) -> Dict:
"status": "success" "status": "success"
} }
:param integer auth_user_id: authenticate user id (from JSON Web Token)
:query integer page: page if using pagination (default: 1) :query integer page: page if using pagination (default: 1)
:query integer per_page: number of users per page (default: 10, max: 50) :query integer per_page: number of users per page (default: 10, max: 50)
:query string q: query on user name :query string q: query on user name
@ -219,7 +217,7 @@ def get_users(auth_user_id: int) -> Dict:
@users_blueprint.route('/users/<user_name>', methods=['GET']) @users_blueprint.route('/users/<user_name>', methods=['GET'])
@authenticate @authenticate
def get_single_user( def get_single_user(
auth_user_id: int, user_name: str auth_user: User, user_name: str
) -> Union[Dict, HttpResponse]: ) -> Union[Dict, HttpResponse]:
""" """
Get single user details Get single user details
@ -306,7 +304,6 @@ def get_single_user(
"status": "success" "status": "success"
} }
:param integer auth_user_id: authenticate user id (from JSON Web Token)
:param integer user_name: user name :param integer user_name: user name
:reqheader Authorization: OAuth 2.0 Bearer Token :reqheader Authorization: OAuth 2.0 Bearer Token
@ -371,9 +368,7 @@ def get_picture(user_name: str) -> Any:
@users_blueprint.route('/users/<user_name>', methods=['PATCH']) @users_blueprint.route('/users/<user_name>', methods=['PATCH'])
@authenticate_as_admin @authenticate_as_admin
def update_user( def update_user(auth_user: User, user_name: str) -> Union[Dict, HttpResponse]:
auth_user_id: int, user_name: str
) -> Union[Dict, HttpResponse]:
""" """
Update user to add admin rights Update user to add admin rights
@ -461,7 +456,6 @@ def update_user(
"status": "success" "status": "success"
} }
:param integer auth_user_id: authenticate user id (from JSON Web Token)
:param string user_name: user name :param string user_name: user name
:<json boolean admin: does the user have administrator rights :<json boolean admin: does the user have administrator rights
@ -500,7 +494,7 @@ def update_user(
@users_blueprint.route('/users/<user_name>', methods=['DELETE']) @users_blueprint.route('/users/<user_name>', methods=['DELETE'])
@authenticate @authenticate
def delete_user( def delete_user(
auth_user_id: int, user_name: str auth_user: User, user_name: str
) -> Union[Tuple[Dict, int], HttpResponse]: ) -> Union[Tuple[Dict, int], HttpResponse]:
""" """
Delete a user account Delete a user account
@ -524,7 +518,6 @@ def delete_user(
HTTP/1.1 204 NO CONTENT HTTP/1.1 204 NO CONTENT
Content-Type: application/json Content-Type: application/json
:param integer auth_user_id: authenticate user id (from JSON Web Token)
:param string user_name: user name :param string user_name: user name
:reqheader Authorization: OAuth 2.0 Bearer Token :reqheader Authorization: OAuth 2.0 Bearer Token
@ -543,12 +536,11 @@ def delete_user(
""" """
try: try:
auth_user = User.query.filter_by(id=auth_user_id).first()
user = User.query.filter_by(username=user_name).first() user = User.query.filter_by(username=user_name).first()
if not user: if not user:
return UserNotFoundErrorResponse() return UserNotFoundErrorResponse()
if user.id != auth_user_id and not auth_user.admin: if user.id != auth_user.id and not auth_user.admin:
return ForbiddenErrorResponse() return ForbiddenErrorResponse()
if ( if (
user.admin is True user.admin is True

View File

@ -62,7 +62,7 @@ def register_controls(
def verify_user( def verify_user(
current_request: Request, verify_admin: bool current_request: Request, verify_admin: bool
) -> Tuple[Optional[HttpResponse], Optional[int]]: ) -> Tuple[Optional[HttpResponse], Optional[User]]:
""" """
Return user id, if the provided token is valid and if user has admin Return user id, if the provided token is valid and if user has admin
rights if 'verify_admin' is True rights if 'verify_admin' is True
@ -78,9 +78,9 @@ def verify_user(
user = User.query.filter_by(id=resp).first() user = User.query.filter_by(id=resp).first()
if not user: if not user:
return UnauthorizedErrorResponse(default_message), None return UnauthorizedErrorResponse(default_message), None
if verify_admin and not is_admin(resp): if verify_admin and not user.admin:
return ForbiddenErrorResponse(), None return ForbiddenErrorResponse(), None
return None, resp return None, user
def can_view_workout( def can_view_workout(

View File

@ -3,6 +3,7 @@ from typing import Dict
from flask import Blueprint from flask import Blueprint
from fittrackee.users.decorators import authenticate from fittrackee.users.decorators import authenticate
from fittrackee.users.models import User
from .models import Record from .models import Record
@ -11,7 +12,7 @@ records_blueprint = Blueprint('records', __name__)
@records_blueprint.route('/records', methods=['GET']) @records_blueprint.route('/records', methods=['GET'])
@authenticate @authenticate
def get_records(auth_user_id: int) -> Dict: def get_records(auth_user: User) -> Dict:
""" """
Get all records for authenticated user. Get all records for authenticated user.
@ -95,8 +96,6 @@ def get_records(auth_user_id: int) -> Dict:
"status": "success" "status": "success"
} }
:param integer auth_user_id: authenticate user id (from JSON Web Token)
:reqheader Authorization: OAuth 2.0 Bearer Token :reqheader Authorization: OAuth 2.0 Bearer Token
:statuscode 200: success :statuscode 200: success
@ -107,7 +106,7 @@ def get_records(auth_user_id: int) -> Dict:
""" """
records = ( records = (
Record.query.filter_by(user_id=auth_user_id) Record.query.filter_by(user_id=auth_user.id)
.order_by(Record.sport_id.asc(), Record.record_type.asc()) .order_by(Record.sport_id.asc(), Record.record_type.asc())
.all() .all()
) )

View File

@ -20,7 +20,7 @@ sports_blueprint = Blueprint('sports', __name__)
@sports_blueprint.route('/sports', methods=['GET']) @sports_blueprint.route('/sports', methods=['GET'])
@authenticate @authenticate
def get_sports(auth_user_id: int) -> Dict: def get_sports(auth_user: User) -> Dict:
""" """
Get all sports Get all sports
@ -165,8 +165,6 @@ def get_sports(auth_user_id: int) -> Dict:
"status": "success" "status": "success"
} }
:param integer auth_user_id: authenticate user id (from JSON Web Token)
:reqheader Authorization: OAuth 2.0 Bearer Token :reqheader Authorization: OAuth 2.0 Bearer Token
:statuscode 200: success :statuscode 200: success
@ -176,16 +174,15 @@ def get_sports(auth_user_id: int) -> Dict:
- invalid token, please log in again - invalid token, please log in again
""" """
user = User.query.filter_by(id=int(auth_user_id)).first()
sports = Sport.query.order_by(Sport.id).all() sports = Sport.query.order_by(Sport.id).all()
sports_data = [] sports_data = []
for sport in sports: for sport in sports:
sport_preferences = UserSportPreference.query.filter_by( sport_preferences = UserSportPreference.query.filter_by(
user_id=user.id, sport_id=sport.id user_id=auth_user.id, sport_id=sport.id
).first() ).first()
sports_data.append( sports_data.append(
sport.serialize( sport.serialize(
is_admin=user.admin, is_admin=auth_user.admin,
sport_preferences=sport_preferences.serialize() sport_preferences=sport_preferences.serialize()
if sport_preferences if sport_preferences
else None, else None,
@ -199,7 +196,7 @@ def get_sports(auth_user_id: int) -> Dict:
@sports_blueprint.route('/sports/<int:sport_id>', methods=['GET']) @sports_blueprint.route('/sports/<int:sport_id>', methods=['GET'])
@authenticate @authenticate
def get_sport(auth_user_id: int, sport_id: int) -> Union[Dict, HttpResponse]: def get_sport(auth_user: User, sport_id: int) -> Union[Dict, HttpResponse]:
""" """
Get a sport Get a sport
@ -273,7 +270,6 @@ def get_sport(auth_user_id: int, sport_id: int) -> Union[Dict, HttpResponse]:
"status": "not found" "status": "not found"
} }
:param integer auth_user_id: authenticate user id (from JSON Web Token)
:param integer sport_id: sport id :param integer sport_id: sport id
:reqheader Authorization: OAuth 2.0 Bearer Token :reqheader Authorization: OAuth 2.0 Bearer Token
@ -286,18 +282,17 @@ def get_sport(auth_user_id: int, sport_id: int) -> Union[Dict, HttpResponse]:
:statuscode 404: sport not found :statuscode 404: sport not found
""" """
user = User.query.filter_by(id=int(auth_user_id)).first()
sport = Sport.query.filter_by(id=sport_id).first() sport = Sport.query.filter_by(id=sport_id).first()
if sport: if sport:
sport_preferences = UserSportPreference.query.filter_by( sport_preferences = UserSportPreference.query.filter_by(
user_id=user.id, sport_id=sport.id user_id=auth_user.id, sport_id=sport.id
).first() ).first()
return { return {
'status': 'success', 'status': 'success',
'data': { 'data': {
'sports': [ 'sports': [
sport.serialize( sport.serialize(
is_admin=user.admin, is_admin=auth_user.admin,
sport_preferences=sport_preferences.serialize() sport_preferences=sport_preferences.serialize()
if sport_preferences if sport_preferences
else None, else None,
@ -310,9 +305,7 @@ def get_sport(auth_user_id: int, sport_id: int) -> Union[Dict, HttpResponse]:
@sports_blueprint.route('/sports/<int:sport_id>', methods=['PATCH']) @sports_blueprint.route('/sports/<int:sport_id>', methods=['PATCH'])
@authenticate_as_admin @authenticate_as_admin
def update_sport( def update_sport(auth_user: User, sport_id: int) -> Union[Dict, HttpResponse]:
auth_user_id: int, sport_id: int
) -> Union[Dict, HttpResponse]:
""" """
Update a sport Update a sport
Authenticated user must be an admin Authenticated user must be an admin
@ -364,7 +357,6 @@ def update_sport(
"status": "not found" "status": "not found"
} }
:param integer auth_user_id: authenticate user id (from JSON Web Token)
:param integer sport_id: sport id :param integer sport_id: sport id
:<json string is_active: sport active status :<json string is_active: sport active status
@ -387,7 +379,6 @@ def update_sport(
return InvalidPayloadErrorResponse() return InvalidPayloadErrorResponse()
try: try:
user = User.query.filter_by(id=int(auth_user_id)).first()
sport = Sport.query.filter_by(id=sport_id).first() sport = Sport.query.filter_by(id=sport_id).first()
if not sport: if not sport:
return DataNotFoundErrorResponse('sports') return DataNotFoundErrorResponse('sports')
@ -395,14 +386,14 @@ def update_sport(
sport.is_active = sport_data.get('is_active') sport.is_active = sport_data.get('is_active')
db.session.commit() db.session.commit()
sport_preferences = UserSportPreference.query.filter_by( sport_preferences = UserSportPreference.query.filter_by(
user_id=user.id, sport_id=sport.id user_id=auth_user.id, sport_id=sport.id
).first() ).first()
return { return {
'status': 'success', 'status': 'success',
'data': { 'data': {
'sports': [ 'sports': [
sport.serialize( sport.serialize(
is_admin=user.admin, is_admin=auth_user.admin,
sport_preferences=sport_preferences.serialize() sport_preferences=sport_preferences.serialize()
if sport_preferences if sport_preferences
else None, else None,

View File

@ -179,7 +179,7 @@ def get_workouts(
@stats_blueprint.route('/stats/<user_name>/by_time', methods=['GET']) @stats_blueprint.route('/stats/<user_name>/by_time', methods=['GET'])
@authenticate @authenticate
def get_workouts_by_time( def get_workouts_by_time(
auth_user_id: int, user_name: str auth_user: User, user_name: str
) -> Union[Dict, HttpResponse]: ) -> Union[Dict, HttpResponse]:
""" """
Get workouts statistics for a user by time Get workouts statistics for a user by time
@ -258,7 +258,6 @@ def get_workouts_by_time(
"status": "success" "status": "success"
} }
:param integer auth_user_id: authenticate user id (from JSON Web Token)
:param integer user_name: user name :param integer user_name: user name
:query string from: start date (format: ``%Y-%m-%d``) :query string from: start date (format: ``%Y-%m-%d``)
@ -287,7 +286,7 @@ def get_workouts_by_time(
@stats_blueprint.route('/stats/<user_name>/by_sport', methods=['GET']) @stats_blueprint.route('/stats/<user_name>/by_sport', methods=['GET'])
@authenticate @authenticate
def get_workouts_by_sport( def get_workouts_by_sport(
auth_user_id: int, user_name: str auth_user: User, user_name: str
) -> Union[Dict, HttpResponse]: ) -> Union[Dict, HttpResponse]:
""" """
Get workouts statistics for a user by sport Get workouts statistics for a user by sport
@ -361,7 +360,6 @@ def get_workouts_by_sport(
"status": "success" "status": "success"
} }
:param integer auth_user_id: authenticate user id (from JSON Web Token)
:param integer user_name: user name :param integer user_name: user name
:query integer sport_id: sport id :query integer sport_id: sport id
@ -383,7 +381,7 @@ def get_workouts_by_sport(
@stats_blueprint.route('/stats/all', methods=['GET']) @stats_blueprint.route('/stats/all', methods=['GET'])
@authenticate_as_admin @authenticate_as_admin
def get_application_stats(auth_user_id: int) -> Dict: def get_application_stats(auth_user: User) -> Dict:
""" """
Get all application statistics Get all application statistics
@ -411,8 +409,6 @@ def get_application_stats(auth_user_id: int) -> Dict:
"status": "success" "status": "success"
} }
:param integer auth_user_id: authenticate user id (from JSON Web Token)
:reqheader Authorization: OAuth 2.0 Bearer Token :reqheader Authorization: OAuth 2.0 Bearer Token
:statuscode 200: success :statuscode 200: success

View File

@ -56,7 +56,7 @@ MAX_WORKOUTS_PER_PAGE = 100
@workouts_blueprint.route('/workouts', methods=['GET']) @workouts_blueprint.route('/workouts', methods=['GET'])
@authenticate @authenticate
def get_workouts(auth_user_id: int) -> Union[Dict, HttpResponse]: def get_workouts(auth_user: User) -> Union[Dict, HttpResponse]:
""" """
Get workouts for the authenticated user. Get workouts for the authenticated user.
@ -171,8 +171,6 @@ def get_workouts(auth_user_id: int) -> Union[Dict, HttpResponse]:
"status": "success" "status": "success"
} }
:param integer auth_user_id: authenticate user id (from JSON Web Token)
:query integer page: page if using pagination (default: 1) :query integer page: page if using pagination (default: 1)
:query integer per_page: number of workouts per page :query integer per_page: number of workouts per page
(default: 5, max: 100) (default: 5, max: 100)
@ -200,10 +198,9 @@ def get_workouts(auth_user_id: int) -> Union[Dict, HttpResponse]:
""" """
try: try:
user = User.query.filter_by(id=auth_user_id).first()
params = request.args.copy() params = request.args.copy()
page = int(params.get('page', 1)) page = int(params.get('page', 1))
date_from, date_to = get_datetime_from_request_args(params, user) date_from, date_to = get_datetime_from_request_args(params, auth_user)
distance_from = params.get('distance_from') distance_from = params.get('distance_from')
distance_to = params.get('distance_to') distance_to = params.get('distance_to')
duration_from = params.get('duration_from') duration_from = params.get('duration_from')
@ -220,7 +217,7 @@ def get_workouts(auth_user_id: int) -> Union[Dict, HttpResponse]:
per_page = MAX_WORKOUTS_PER_PAGE per_page = MAX_WORKOUTS_PER_PAGE
workouts_pagination = ( workouts_pagination = (
Workout.query.filter( Workout.query.filter(
Workout.user_id == auth_user_id, Workout.user_id == auth_user.id,
Workout.sport_id == sport_id if sport_id else True, Workout.sport_id == sport_id if sport_id else True,
Workout.workout_date >= date_from if date_from else True, Workout.workout_date >= date_from if date_from else True,
Workout.workout_date < date_to + timedelta(seconds=1) Workout.workout_date < date_to + timedelta(seconds=1)
@ -302,7 +299,7 @@ def get_workouts(auth_user_id: int) -> Union[Dict, HttpResponse]:
) )
@authenticate @authenticate
def get_workout( def get_workout(
auth_user_id: int, workout_short_id: str auth_user: User, workout_short_id: str
) -> Union[Dict, HttpResponse]: ) -> Union[Dict, HttpResponse]:
""" """
Get an workout Get an workout
@ -372,8 +369,6 @@ def get_workout(
}, },
"status": "not found" "status": "not found"
} }
:param integer auth_user_id: authenticate user id (from JSON Web Token)
:param string workout_short_id: workout short id :param string workout_short_id: workout short id
:reqheader Authorization: OAuth 2.0 Bearer Token :reqheader Authorization: OAuth 2.0 Bearer Token
@ -392,7 +387,7 @@ def get_workout(
if not workout: if not workout:
return DataNotFoundErrorResponse('workouts') return DataNotFoundErrorResponse('workouts')
error_response = can_view_workout(auth_user_id, workout.user_id) error_response = can_view_workout(auth_user.id, workout.user_id)
if error_response: if error_response:
return error_response return error_response
@ -403,7 +398,7 @@ def get_workout(
def get_workout_data( def get_workout_data(
auth_user_id: int, auth_user: User,
workout_short_id: str, workout_short_id: str,
data_type: str, data_type: str,
segment_id: Optional[int] = None, segment_id: Optional[int] = None,
@ -417,7 +412,7 @@ def get_workout_data(
message=f'workout not found (id: {workout_short_id})', message=f'workout not found (id: {workout_short_id})',
) )
error_response = can_view_workout(auth_user_id, workout.user_id) error_response = can_view_workout(auth_user.id, workout.user_id)
if error_response: if error_response:
return error_response return error_response
if not workout.gpx or workout.gpx == '': if not workout.gpx or workout.gpx == '':
@ -467,7 +462,7 @@ def get_workout_data(
) )
@authenticate @authenticate
def get_workout_gpx( def get_workout_gpx(
auth_user_id: int, workout_short_id: str auth_user: User, workout_short_id: str
) -> Union[Dict, HttpResponse]: ) -> Union[Dict, HttpResponse]:
""" """
Get gpx file for an workout displayed on map with Leaflet Get gpx file for an workout displayed on map with Leaflet
@ -494,7 +489,6 @@ def get_workout_gpx(
"status": "success" "status": "success"
} }
:param integer auth_user_id: authenticate user id (from JSON Web Token)
:param string workout_short_id: workout short id :param string workout_short_id: workout short id
:reqheader Authorization: OAuth 2.0 Bearer Token :reqheader Authorization: OAuth 2.0 Bearer Token
@ -510,7 +504,7 @@ def get_workout_gpx(
:statuscode 500: :statuscode 500:
""" """
return get_workout_data(auth_user_id, workout_short_id, 'gpx') return get_workout_data(auth_user, workout_short_id, 'gpx')
@workouts_blueprint.route( @workouts_blueprint.route(
@ -518,7 +512,7 @@ def get_workout_gpx(
) )
@authenticate @authenticate
def get_workout_chart_data( def get_workout_chart_data(
auth_user_id: int, workout_short_id: str auth_user: User, workout_short_id: str
) -> Union[Dict, HttpResponse]: ) -> Union[Dict, HttpResponse]:
""" """
Get chart data from an workout gpx file, to display it with Recharts Get chart data from an workout gpx file, to display it with Recharts
@ -564,7 +558,6 @@ def get_workout_chart_data(
"status": "success" "status": "success"
} }
:param integer auth_user_id: authenticate user id (from JSON Web Token)
:param string workout_short_id: workout short id :param string workout_short_id: workout short id
:reqheader Authorization: OAuth 2.0 Bearer Token :reqheader Authorization: OAuth 2.0 Bearer Token
@ -580,7 +573,7 @@ def get_workout_chart_data(
:statuscode 500: :statuscode 500:
""" """
return get_workout_data(auth_user_id, workout_short_id, 'chart_data') return get_workout_data(auth_user, workout_short_id, 'chart_data')
@workouts_blueprint.route( @workouts_blueprint.route(
@ -589,7 +582,7 @@ def get_workout_chart_data(
) )
@authenticate @authenticate
def get_segment_gpx( def get_segment_gpx(
auth_user_id: int, workout_short_id: str, segment_id: int auth_user: User, workout_short_id: str, segment_id: int
) -> Union[Dict, HttpResponse]: ) -> Union[Dict, HttpResponse]:
""" """
Get gpx file for an workout segment displayed on map with Leaflet Get gpx file for an workout segment displayed on map with Leaflet
@ -616,7 +609,6 @@ def get_segment_gpx(
"status": "success" "status": "success"
} }
:param integer auth_user_id: authenticate user id (from JSON Web Token)
:param string workout_short_id: workout short id :param string workout_short_id: workout short id
:param integer segment_id: segment id :param integer segment_id: segment id
@ -632,7 +624,7 @@ def get_segment_gpx(
:statuscode 500: :statuscode 500:
""" """
return get_workout_data(auth_user_id, workout_short_id, 'gpx', segment_id) return get_workout_data(auth_user, workout_short_id, 'gpx', segment_id)
@workouts_blueprint.route( @workouts_blueprint.route(
@ -642,7 +634,7 @@ def get_segment_gpx(
) )
@authenticate @authenticate
def get_segment_chart_data( def get_segment_chart_data(
auth_user_id: int, workout_short_id: str, segment_id: int auth_user: User, workout_short_id: str, segment_id: int
) -> Union[Dict, HttpResponse]: ) -> Union[Dict, HttpResponse]:
""" """
Get chart data from an workout gpx file, to display it with Recharts Get chart data from an workout gpx file, to display it with Recharts
@ -688,7 +680,6 @@ def get_segment_chart_data(
"status": "success" "status": "success"
} }
:param integer auth_user_id: authenticate user id (from JSON Web Token)
:param string workout_short_id: workout short id :param string workout_short_id: workout short id
:param integer segment_id: segment id :param integer segment_id: segment id
@ -705,7 +696,7 @@ def get_segment_chart_data(
""" """
return get_workout_data( return get_workout_data(
auth_user_id, workout_short_id, 'chart_data', segment_id auth_user, workout_short_id, 'chart_data', segment_id
) )
@ -714,7 +705,7 @@ def get_segment_chart_data(
) )
@authenticate @authenticate
def download_workout_gpx( def download_workout_gpx(
auth_user_id: int, workout_short_id: str auth_user: User, workout_short_id: str
) -> Union[HttpResponse, Response]: ) -> Union[HttpResponse, Response]:
""" """
Download gpx file Download gpx file
@ -732,7 +723,6 @@ def download_workout_gpx(
HTTP/1.1 200 OK HTTP/1.1 200 OK
Content-Type: application/gpx+xml Content-Type: application/gpx+xml
:param integer auth_user_id: authenticate user id (from JSON Web Token)
:param string workout_short_id: workout short id :param string workout_short_id: workout short id
:statuscode 200: success :statuscode 200: success
@ -746,7 +736,7 @@ def download_workout_gpx(
""" """
workout_uuid = decode_short_id(workout_short_id) workout_uuid = decode_short_id(workout_short_id)
workout = Workout.query.filter_by( workout = Workout.query.filter_by(
uuid=workout_uuid, user_id=auth_user_id uuid=workout_uuid, user_id=auth_user.id
).first() ).first()
if not workout: if not workout:
return DataNotFoundErrorResponse( return DataNotFoundErrorResponse(
@ -852,7 +842,7 @@ def get_map_tile(s: str, z: str, x: str, y: str) -> Tuple[Response, int]:
@workouts_blueprint.route('/workouts', methods=['POST']) @workouts_blueprint.route('/workouts', methods=['POST'])
@authenticate @authenticate
def post_workout(auth_user_id: int) -> Union[Tuple[Dict, int], HttpResponse]: def post_workout(auth_user: User) -> Union[Tuple[Dict, int], HttpResponse]:
""" """
Post an workout with a gpx file Post an workout with a gpx file
@ -944,8 +934,6 @@ def post_workout(auth_user_id: int) -> Union[Tuple[Dict, int], HttpResponse]:
"status": "success" "status": "success"
} }
:param integer auth_user_id: authenticate user id (from JSON Web Token)
:form file: gpx file (allowed extensions: .gpx, .zip) :form file: gpx file (allowed extensions: .gpx, .zip)
:form data: sport id and notes (example: ``{"sport_id": 1, "notes": ""}``) :form data: sport id and notes (example: ``{"sport_id": 1, "notes": ""}``)
@ -983,7 +971,7 @@ def post_workout(auth_user_id: int) -> Union[Tuple[Dict, int], HttpResponse]:
workout_file = request.files['file'] workout_file = request.files['file']
upload_dir = os.path.join( upload_dir = os.path.join(
current_app.config['UPLOAD_FOLDER'], 'workouts', str(auth_user_id) current_app.config['UPLOAD_FOLDER'], 'workouts', str(auth_user.id)
) )
folders = { folders = {
'extract_dir': os.path.join(upload_dir, 'extract'), 'extract_dir': os.path.join(upload_dir, 'extract'),
@ -992,7 +980,7 @@ def post_workout(auth_user_id: int) -> Union[Tuple[Dict, int], HttpResponse]:
try: try:
new_workouts = process_files( new_workouts = process_files(
auth_user_id, workout_data, workout_file, folders auth_user.id, workout_data, workout_file, folders
) )
if len(new_workouts) > 0: if len(new_workouts) > 0:
response_object = { response_object = {
@ -1021,7 +1009,7 @@ def post_workout(auth_user_id: int) -> Union[Tuple[Dict, int], HttpResponse]:
@workouts_blueprint.route('/workouts/no_gpx', methods=['POST']) @workouts_blueprint.route('/workouts/no_gpx', methods=['POST'])
@authenticate @authenticate
def post_workout_no_gpx( def post_workout_no_gpx(
auth_user_id: int, auth_user: User,
) -> Union[Tuple[Dict, int], HttpResponse]: ) -> Union[Tuple[Dict, int], HttpResponse]:
""" """
Post an workout without gpx file Post an workout without gpx file
@ -1114,8 +1102,6 @@ def post_workout_no_gpx(
"status": "success" "status": "success"
} }
:param integer auth_user_id: authenticate user id (from JSON Web Token)
:<json string workout_date: workout date (format: ``%Y-%m-%d %H:%M``) :<json string workout_date: workout date (format: ``%Y-%m-%d %H:%M``)
:<json float distance: workout distance in km :<json float distance: workout distance in km
:<json integer duration: workout duration in seconds :<json integer duration: workout duration in seconds
@ -1145,8 +1131,7 @@ def post_workout_no_gpx(
return InvalidPayloadErrorResponse() return InvalidPayloadErrorResponse()
try: try:
user = User.query.filter_by(id=auth_user_id).first() new_workout = create_workout(auth_user, workout_data)
new_workout = create_workout(user, workout_data)
db.session.add(new_workout) db.session.add(new_workout)
db.session.commit() db.session.commit()
@ -1172,7 +1157,7 @@ def post_workout_no_gpx(
) )
@authenticate @authenticate
def update_workout( def update_workout(
auth_user_id: int, workout_short_id: str auth_user: User, workout_short_id: str
) -> Union[Dict, HttpResponse]: ) -> Union[Dict, HttpResponse]:
""" """
Update an workout Update an workout
@ -1265,7 +1250,6 @@ def update_workout(
"status": "success" "status": "success"
} }
:param integer auth_user_id: authenticate user id (from JSON Web Token)
:param string workout_short_id: workout short id :param string workout_short_id: workout short id
:<json string workout_date: workout date (format: ``%Y-%m-%d %H:%M``) :<json string workout_date: workout date (format: ``%Y-%m-%d %H:%M``)
@ -1300,11 +1284,11 @@ def update_workout(
if not workout: if not workout:
return DataNotFoundErrorResponse('workouts') return DataNotFoundErrorResponse('workouts')
response_object = can_view_workout(auth_user_id, workout.user_id) response_object = can_view_workout(auth_user.id, workout.user_id)
if response_object: if response_object:
return response_object return response_object
workout = edit_workout(workout, workout_data, auth_user_id) workout = edit_workout(workout, workout_data, auth_user.id)
db.session.commit() db.session.commit()
return { return {
'status': 'success', 'status': 'success',
@ -1320,7 +1304,7 @@ def update_workout(
) )
@authenticate @authenticate
def delete_workout( def delete_workout(
auth_user_id: int, workout_short_id: str auth_user: User, workout_short_id: str
) -> Union[Tuple[Dict, int], HttpResponse]: ) -> Union[Tuple[Dict, int], HttpResponse]:
""" """
Delete an workout Delete an workout
@ -1339,7 +1323,6 @@ def delete_workout(
HTTP/1.1 204 NO CONTENT HTTP/1.1 204 NO CONTENT
Content-Type: application/json Content-Type: application/json
:param integer auth_user_id: authenticate user id (from JSON Web Token)
:param string workout_short_id: workout short id :param string workout_short_id: workout short id
:reqheader Authorization: OAuth 2.0 Bearer Token :reqheader Authorization: OAuth 2.0 Bearer Token
@ -1359,7 +1342,7 @@ def delete_workout(
workout = Workout.query.filter_by(uuid=workout_uuid).first() workout = Workout.query.filter_by(uuid=workout_uuid).first()
if not workout: if not workout:
return DataNotFoundErrorResponse('workouts') return DataNotFoundErrorResponse('workouts')
error_response = can_view_workout(auth_user_id, workout.user_id) error_response = can_view_workout(auth_user.id, workout.user_id)
if error_response: if error_response:
return error_response return error_response