From 00b6e058052d25f0d72662cf20cc61804af9897d Mon Sep 17 00:00:00 2001
From: Sam
Date: Wed, 1 Dec 2021 19:22:47 +0100
Subject: [PATCH 1/6] API - authentication decorators return user directly
instead of user id
---
Makefile | 2 +
fittrackee/application/app_config.py | 5 +-
fittrackee/users/auth.py | 65 ++++++++++++-------------
fittrackee/users/decorators.py | 10 ++--
fittrackee/users/users.py | 18 ++-----
fittrackee/users/utils.py | 6 +--
fittrackee/workouts/records.py | 7 ++-
fittrackee/workouts/sports.py | 27 ++++-------
fittrackee/workouts/stats.py | 10 ++--
fittrackee/workouts/workouts.py | 71 +++++++++++-----------------
10 files changed, 88 insertions(+), 133 deletions(-)
diff --git a/Makefile b/Makefile
index 03e8b22a..4b950ff5 100644
--- a/Makefile
+++ b/Makefile
@@ -11,6 +11,8 @@ build-client: lint-client
check-all: lint-all type-check test-python test-client
+check-python: lint-python type-check test-python
+
clean:
rm -rf .mypy_cache
rm -rf .pytest_cache
diff --git a/fittrackee/application/app_config.py b/fittrackee/application/app_config.py
index e3e97f70..c97c9278 100644
--- a/fittrackee/application/app_config.py
+++ b/fittrackee/application/app_config.py
@@ -10,6 +10,7 @@ from fittrackee.responses import (
handle_error_and_return_response,
)
from fittrackee.users.decorators import authenticate_as_admin
+from fittrackee.users.models import User
from .models import AppConfig
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'])
@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
@@ -95,8 +96,6 @@ def update_application_config(auth_user_id: int) -> Union[Dict, HttpResponse]:
"status": "success"
}
- :param integer auth_user_id: authenticate user id (from JSON Web Token)
-
: Union[Dict, HttpResponse]:
@auth_blueprint.route('/auth/logout', methods=['GET'])
@authenticate
-def logout_user(auth_user_id: int) -> Union[Dict, HttpResponse]:
+def logout_user(auth_user: User) -> Union[Dict, HttpResponse]:
"""
user logout
@@ -274,7 +274,7 @@ def logout_user(auth_user_id: int) -> Union[Dict, HttpResponse]:
auth_token = auth_header.split(' ')[1]
resp = User.decode_auth_token(auth_token)
- if isinstance(auth_user_id, str):
+ if isinstance(resp, str):
return UnauthorizedErrorResponse(resp)
return {
@@ -286,7 +286,7 @@ def logout_user(auth_user_id: int) -> Union[Dict, HttpResponse]:
@auth_blueprint.route('/auth/profile', methods=['GET'])
@authenticate
def get_authenticated_user_profile(
- auth_user_id: int,
+ auth_user: User,
) -> Union[Dict, HttpResponse]:
"""
get authenticated user info
@@ -381,13 +381,12 @@ def get_authenticated_user_profile(
- invalid token, please log in again
"""
- user = User.query.filter_by(id=auth_user_id).first()
- return {'status': 'success', 'data': user.serialize()}
+ return {'status': 'success', 'data': auth_user.serialize()}
@auth_blueprint.route('/auth/profile/edit', methods=['POST'])
@authenticate
-def edit_user(auth_user_id: int) -> Union[Dict, HttpResponse]:
+def edit_user(auth_user: User) -> Union[Dict, HttpResponse]:
"""
edit authenticated user
@@ -523,24 +522,23 @@ def edit_user(auth_user_id: int) -> Union[Dict, HttpResponse]:
).decode()
try:
- user = User.query.filter_by(id=auth_user_id).first()
- user.first_name = first_name
- user.last_name = last_name
- user.bio = bio
- user.location = location
- user.birth_date = (
+ auth_user.first_name = first_name
+ auth_user.last_name = last_name
+ auth_user.bio = bio
+ auth_user.location = location
+ auth_user.birth_date = (
datetime.datetime.strptime(birth_date, '%Y-%m-%d')
if birth_date
else None
)
if password is not None and password != '':
- user.password = password
+ auth_user.password = password
db.session.commit()
return {
'status': 'success',
'message': 'user profile updated',
- 'data': user.serialize(),
+ 'data': auth_user.serialize(),
}
# 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'])
@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
@@ -670,17 +668,16 @@ def edit_user_preferences(auth_user_id: int) -> Union[Dict, HttpResponse]:
weekm = post_data.get('weekm')
try:
- user = User.query.filter_by(id=auth_user_id).first()
- user.imperial_units = imperial_units
- user.language = language
- user.timezone = timezone
- user.weekm = weekm
+ auth_user.imperial_units = imperial_units
+ auth_user.language = language
+ auth_user.timezone = timezone
+ auth_user.weekm = weekm
db.session.commit()
return {
'status': 'success',
'message': 'user preferences updated',
- 'data': user.serialize(),
+ 'data': auth_user.serialize(),
}
# 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'])
@authenticate
def edit_user_sport_preferences(
- auth_user_id: int,
+ auth_user: User,
) -> Union[Dict, HttpResponse]:
"""
edit authenticated user sport preferences
@@ -758,12 +755,12 @@ def edit_user_sport_preferences(
try:
user_sport = UserSportPreference.query.filter_by(
- user_id=auth_user_id,
+ user_id=auth_user.id,
sport_id=sport_id,
).first()
if not user_sport:
user_sport = UserSportPreference(
- user_id=auth_user_id,
+ user_id=auth_user.id,
sport_id=sport_id,
stopped_speed_threshold=sport.stopped_speed_threshold,
)
@@ -792,7 +789,7 @@ def edit_user_sport_preferences(
@auth_blueprint.route('/auth/picture', methods=['POST'])
@authenticate
-def edit_picture(auth_user_id: int) -> Union[Dict, HttpResponse]:
+def edit_picture(auth_user: User) -> Union[Dict, HttpResponse]:
"""
update authenticated user picture
@@ -848,23 +845,22 @@ def edit_picture(auth_user_id: int) -> Union[Dict, HttpResponse]:
file = request.files['file']
filename = secure_filename(file.filename) # type: ignore
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):
os.makedirs(dirpath)
absolute_picture_path = os.path.join(dirpath, filename)
relative_picture_path = os.path.join(
- 'pictures', str(auth_user_id), filename
+ 'pictures', str(auth_user.id), filename
)
try:
- user = User.query.filter_by(id=auth_user_id).first()
- if user.picture is not None:
- old_picture_path = get_absolute_file_path(user.picture)
+ if auth_user.picture is not None:
+ old_picture_path = get_absolute_file_path(auth_user.picture)
if os.path.isfile(get_absolute_file_path(old_picture_path)):
os.remove(old_picture_path)
file.save(absolute_picture_path)
- user.picture = relative_picture_path
+ auth_user.picture = relative_picture_path
db.session.commit()
return {
'status': 'success',
@@ -879,7 +875,7 @@ def edit_picture(auth_user_id: int) -> Union[Dict, HttpResponse]:
@auth_blueprint.route('/auth/picture', methods=['DELETE'])
@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
@@ -908,11 +904,10 @@ def del_picture(auth_user_id: int) -> Union[Tuple[Dict, int], HttpResponse]:
"""
try:
- user = User.query.filter_by(id=auth_user_id).first()
- picture_path = get_absolute_file_path(user.picture)
+ picture_path = get_absolute_file_path(auth_user.picture)
if os.path.isfile(picture_path):
os.remove(picture_path)
- user.picture = None
+ auth_user.picture = None
db.session.commit()
return {'status': 'no content'}, 204
except (exc.IntegrityError, ValueError) as e:
diff --git a/fittrackee/users/decorators.py b/fittrackee/users/decorators.py
index 8f260e4f..b3a841a5 100644
--- a/fittrackee/users/decorators.py
+++ b/fittrackee/users/decorators.py
@@ -13,11 +13,10 @@ def authenticate(f: Callable) -> Callable:
def decorated_function(
*args: Any, **kwargs: Any
) -> Union[Callable, HttpResponse]:
- verify_admin = False
- response_object, resp = verify_user(request, verify_admin)
+ response_object, user = verify_user(request, verify_admin=False)
if response_object:
return response_object
- return f(resp, *args, **kwargs)
+ return f(user, *args, **kwargs)
return decorated_function
@@ -27,10 +26,9 @@ def authenticate_as_admin(f: Callable) -> Callable:
def decorated_function(
*args: Any, **kwargs: Any
) -> Union[Callable, HttpResponse]:
- verify_admin = True
- response_object, resp = verify_user(request, verify_admin)
+ response_object, user = verify_user(request, verify_admin=True)
if response_object:
return response_object
- return f(resp, *args, **kwargs)
+ return f(user, *args, **kwargs)
return decorated_function
diff --git a/fittrackee/users/users.py b/fittrackee/users/users.py
index 44faf458..8d0089be 100644
--- a/fittrackee/users/users.py
+++ b/fittrackee/users/users.py
@@ -27,7 +27,7 @@ USER_PER_PAGE = 10
@users_blueprint.route('/users', methods=['GET'])
@authenticate
-def get_users(auth_user_id: int) -> Dict:
+def get_users(auth_user: User) -> Dict:
"""
Get all users
@@ -144,8 +144,6 @@ def get_users(auth_user_id: int) -> Dict:
"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 per_page: number of users per page (default: 10, max: 50)
:query string q: query on user name
@@ -219,7 +217,7 @@ def get_users(auth_user_id: int) -> Dict:
@users_blueprint.route('/users/', methods=['GET'])
@authenticate
def get_single_user(
- auth_user_id: int, user_name: str
+ auth_user: User, user_name: str
) -> Union[Dict, HttpResponse]:
"""
Get single user details
@@ -306,7 +304,6 @@ def get_single_user(
"status": "success"
}
- :param integer auth_user_id: authenticate user id (from JSON Web Token)
:param integer user_name: user name
:reqheader Authorization: OAuth 2.0 Bearer Token
@@ -371,9 +368,7 @@ def get_picture(user_name: str) -> Any:
@users_blueprint.route('/users/', methods=['PATCH'])
@authenticate_as_admin
-def update_user(
- auth_user_id: int, user_name: str
-) -> Union[Dict, HttpResponse]:
+def update_user(auth_user: User, user_name: str) -> Union[Dict, HttpResponse]:
"""
Update user to add admin rights
@@ -461,7 +456,6 @@ def update_user(
"status": "success"
}
- :param integer auth_user_id: authenticate user id (from JSON Web Token)
:param string user_name: user name
:', methods=['DELETE'])
@authenticate
def delete_user(
- auth_user_id: int, user_name: str
+ auth_user: User, user_name: str
) -> Union[Tuple[Dict, int], HttpResponse]:
"""
Delete a user account
@@ -524,7 +518,6 @@ def delete_user(
HTTP/1.1 204 NO CONTENT
Content-Type: application/json
- :param integer auth_user_id: authenticate user id (from JSON Web Token)
:param string user_name: user name
:reqheader Authorization: OAuth 2.0 Bearer Token
@@ -543,12 +536,11 @@ def delete_user(
"""
try:
- auth_user = User.query.filter_by(id=auth_user_id).first()
user = User.query.filter_by(username=user_name).first()
if not user:
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()
if (
user.admin is True
diff --git a/fittrackee/users/utils.py b/fittrackee/users/utils.py
index c938a522..6f6a1984 100644
--- a/fittrackee/users/utils.py
+++ b/fittrackee/users/utils.py
@@ -62,7 +62,7 @@ def register_controls(
def verify_user(
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
rights if 'verify_admin' is True
@@ -78,9 +78,9 @@ def verify_user(
user = User.query.filter_by(id=resp).first()
if not user:
return UnauthorizedErrorResponse(default_message), None
- if verify_admin and not is_admin(resp):
+ if verify_admin and not user.admin:
return ForbiddenErrorResponse(), None
- return None, resp
+ return None, user
def can_view_workout(
diff --git a/fittrackee/workouts/records.py b/fittrackee/workouts/records.py
index f0905415..5714fa8e 100644
--- a/fittrackee/workouts/records.py
+++ b/fittrackee/workouts/records.py
@@ -3,6 +3,7 @@ from typing import Dict
from flask import Blueprint
from fittrackee.users.decorators import authenticate
+from fittrackee.users.models import User
from .models import Record
@@ -11,7 +12,7 @@ records_blueprint = Blueprint('records', __name__)
@records_blueprint.route('/records', methods=['GET'])
@authenticate
-def get_records(auth_user_id: int) -> Dict:
+def get_records(auth_user: User) -> Dict:
"""
Get all records for authenticated user.
@@ -95,8 +96,6 @@ def get_records(auth_user_id: int) -> Dict:
"status": "success"
}
- :param integer auth_user_id: authenticate user id (from JSON Web Token)
-
:reqheader Authorization: OAuth 2.0 Bearer Token
:statuscode 200: success
@@ -107,7 +106,7 @@ def get_records(auth_user_id: int) -> Dict:
"""
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())
.all()
)
diff --git a/fittrackee/workouts/sports.py b/fittrackee/workouts/sports.py
index e91cf973..9236068c 100644
--- a/fittrackee/workouts/sports.py
+++ b/fittrackee/workouts/sports.py
@@ -20,7 +20,7 @@ sports_blueprint = Blueprint('sports', __name__)
@sports_blueprint.route('/sports', methods=['GET'])
@authenticate
-def get_sports(auth_user_id: int) -> Dict:
+def get_sports(auth_user: User) -> Dict:
"""
Get all sports
@@ -165,8 +165,6 @@ def get_sports(auth_user_id: int) -> Dict:
"status": "success"
}
- :param integer auth_user_id: authenticate user id (from JSON Web Token)
-
:reqheader Authorization: OAuth 2.0 Bearer Token
:statuscode 200: success
@@ -176,16 +174,15 @@ def get_sports(auth_user_id: int) -> Dict:
- 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_data = []
for sport in sports:
sport_preferences = UserSportPreference.query.filter_by(
- user_id=user.id, sport_id=sport.id
+ user_id=auth_user.id, sport_id=sport.id
).first()
sports_data.append(
sport.serialize(
- is_admin=user.admin,
+ is_admin=auth_user.admin,
sport_preferences=sport_preferences.serialize()
if sport_preferences
else None,
@@ -199,7 +196,7 @@ def get_sports(auth_user_id: int) -> Dict:
@sports_blueprint.route('/sports/', methods=['GET'])
@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
@@ -273,7 +270,6 @@ def get_sport(auth_user_id: int, sport_id: int) -> Union[Dict, HttpResponse]:
"status": "not found"
}
- :param integer auth_user_id: authenticate user id (from JSON Web Token)
:param integer sport_id: sport id
: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
"""
- user = User.query.filter_by(id=int(auth_user_id)).first()
sport = Sport.query.filter_by(id=sport_id).first()
if sport:
sport_preferences = UserSportPreference.query.filter_by(
- user_id=user.id, sport_id=sport.id
+ user_id=auth_user.id, sport_id=sport.id
).first()
return {
'status': 'success',
'data': {
'sports': [
sport.serialize(
- is_admin=user.admin,
+ is_admin=auth_user.admin,
sport_preferences=sport_preferences.serialize()
if sport_preferences
else None,
@@ -310,9 +305,7 @@ def get_sport(auth_user_id: int, sport_id: int) -> Union[Dict, HttpResponse]:
@sports_blueprint.route('/sports/', methods=['PATCH'])
@authenticate_as_admin
-def update_sport(
- auth_user_id: int, sport_id: int
-) -> Union[Dict, HttpResponse]:
+def update_sport(auth_user: User, sport_id: int) -> Union[Dict, HttpResponse]:
"""
Update a sport
Authenticated user must be an admin
@@ -364,7 +357,6 @@ def update_sport(
"status": "not found"
}
- :param integer auth_user_id: authenticate user id (from JSON Web Token)
:param integer sport_id: sport id
:/by_time', methods=['GET'])
@authenticate
def get_workouts_by_time(
- auth_user_id: int, user_name: str
+ auth_user: User, user_name: str
) -> Union[Dict, HttpResponse]:
"""
Get workouts statistics for a user by time
@@ -258,7 +258,6 @@ def get_workouts_by_time(
"status": "success"
}
- :param integer auth_user_id: authenticate user id (from JSON Web Token)
:param integer user_name: user name
:query string from: start date (format: ``%Y-%m-%d``)
@@ -287,7 +286,7 @@ def get_workouts_by_time(
@stats_blueprint.route('/stats//by_sport', methods=['GET'])
@authenticate
def get_workouts_by_sport(
- auth_user_id: int, user_name: str
+ auth_user: User, user_name: str
) -> Union[Dict, HttpResponse]:
"""
Get workouts statistics for a user by sport
@@ -361,7 +360,6 @@ def get_workouts_by_sport(
"status": "success"
}
- :param integer auth_user_id: authenticate user id (from JSON Web Token)
:param integer user_name: user name
:query integer sport_id: sport id
@@ -383,7 +381,7 @@ def get_workouts_by_sport(
@stats_blueprint.route('/stats/all', methods=['GET'])
@authenticate_as_admin
-def get_application_stats(auth_user_id: int) -> Dict:
+def get_application_stats(auth_user: User) -> Dict:
"""
Get all application statistics
@@ -411,8 +409,6 @@ def get_application_stats(auth_user_id: int) -> Dict:
"status": "success"
}
- :param integer auth_user_id: authenticate user id (from JSON Web Token)
-
:reqheader Authorization: OAuth 2.0 Bearer Token
:statuscode 200: success
diff --git a/fittrackee/workouts/workouts.py b/fittrackee/workouts/workouts.py
index 607a65cc..192e7217 100644
--- a/fittrackee/workouts/workouts.py
+++ b/fittrackee/workouts/workouts.py
@@ -56,7 +56,7 @@ MAX_WORKOUTS_PER_PAGE = 100
@workouts_blueprint.route('/workouts', methods=['GET'])
@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.
@@ -171,8 +171,6 @@ def get_workouts(auth_user_id: int) -> Union[Dict, HttpResponse]:
"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 per_page: number of workouts per page
(default: 5, max: 100)
@@ -200,10 +198,9 @@ def get_workouts(auth_user_id: int) -> Union[Dict, HttpResponse]:
"""
try:
- user = User.query.filter_by(id=auth_user_id).first()
params = request.args.copy()
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_to = params.get('distance_to')
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
workouts_pagination = (
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.workout_date >= date_from if date_from else True,
Workout.workout_date < date_to + timedelta(seconds=1)
@@ -302,7 +299,7 @@ def get_workouts(auth_user_id: int) -> Union[Dict, HttpResponse]:
)
@authenticate
def get_workout(
- auth_user_id: int, workout_short_id: str
+ auth_user: User, workout_short_id: str
) -> Union[Dict, HttpResponse]:
"""
Get an workout
@@ -372,8 +369,6 @@ def get_workout(
},
"status": "not found"
}
-
- :param integer auth_user_id: authenticate user id (from JSON Web Token)
:param string workout_short_id: workout short id
:reqheader Authorization: OAuth 2.0 Bearer Token
@@ -392,7 +387,7 @@ def get_workout(
if not workout:
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:
return error_response
@@ -403,7 +398,7 @@ def get_workout(
def get_workout_data(
- auth_user_id: int,
+ auth_user: User,
workout_short_id: str,
data_type: str,
segment_id: Optional[int] = None,
@@ -417,7 +412,7 @@ def get_workout_data(
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:
return error_response
if not workout.gpx or workout.gpx == '':
@@ -467,7 +462,7 @@ def get_workout_data(
)
@authenticate
def get_workout_gpx(
- auth_user_id: int, workout_short_id: str
+ auth_user: User, workout_short_id: str
) -> Union[Dict, HttpResponse]:
"""
Get gpx file for an workout displayed on map with Leaflet
@@ -494,7 +489,6 @@ def get_workout_gpx(
"status": "success"
}
- :param integer auth_user_id: authenticate user id (from JSON Web Token)
:param string workout_short_id: workout short id
:reqheader Authorization: OAuth 2.0 Bearer Token
@@ -510,7 +504,7 @@ def get_workout_gpx(
: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(
@@ -518,7 +512,7 @@ def get_workout_gpx(
)
@authenticate
def get_workout_chart_data(
- auth_user_id: int, workout_short_id: str
+ auth_user: User, workout_short_id: str
) -> Union[Dict, HttpResponse]:
"""
Get chart data from an workout gpx file, to display it with Recharts
@@ -564,7 +558,6 @@ def get_workout_chart_data(
"status": "success"
}
- :param integer auth_user_id: authenticate user id (from JSON Web Token)
:param string workout_short_id: workout short id
:reqheader Authorization: OAuth 2.0 Bearer Token
@@ -580,7 +573,7 @@ def get_workout_chart_data(
: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(
@@ -589,7 +582,7 @@ def get_workout_chart_data(
)
@authenticate
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]:
"""
Get gpx file for an workout segment displayed on map with Leaflet
@@ -616,7 +609,6 @@ def get_segment_gpx(
"status": "success"
}
- :param integer auth_user_id: authenticate user id (from JSON Web Token)
:param string workout_short_id: workout short id
:param integer segment_id: segment id
@@ -632,7 +624,7 @@ def get_segment_gpx(
: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(
@@ -642,7 +634,7 @@ def get_segment_gpx(
)
@authenticate
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]:
"""
Get chart data from an workout gpx file, to display it with Recharts
@@ -688,7 +680,6 @@ def get_segment_chart_data(
"status": "success"
}
- :param integer auth_user_id: authenticate user id (from JSON Web Token)
:param string workout_short_id: workout short id
:param integer segment_id: segment id
@@ -705,7 +696,7 @@ def get_segment_chart_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
def download_workout_gpx(
- auth_user_id: int, workout_short_id: str
+ auth_user: User, workout_short_id: str
) -> Union[HttpResponse, Response]:
"""
Download gpx file
@@ -732,7 +723,6 @@ def download_workout_gpx(
HTTP/1.1 200 OK
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
:statuscode 200: success
@@ -746,7 +736,7 @@ def download_workout_gpx(
"""
workout_uuid = decode_short_id(workout_short_id)
workout = Workout.query.filter_by(
- uuid=workout_uuid, user_id=auth_user_id
+ uuid=workout_uuid, user_id=auth_user.id
).first()
if not workout:
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'])
@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
@@ -944,8 +934,6 @@ def post_workout(auth_user_id: int) -> Union[Tuple[Dict, int], HttpResponse]:
"status": "success"
}
- :param integer auth_user_id: authenticate user id (from JSON Web Token)
-
:form file: gpx file (allowed extensions: .gpx, .zip)
: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']
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 = {
'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:
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:
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'])
@authenticate
def post_workout_no_gpx(
- auth_user_id: int,
+ auth_user: User,
) -> Union[Tuple[Dict, int], HttpResponse]:
"""
Post an workout without gpx file
@@ -1114,8 +1102,6 @@ def post_workout_no_gpx(
"status": "success"
}
- :param integer auth_user_id: authenticate user id (from JSON Web Token)
-
: Union[Dict, HttpResponse]:
"""
Update an workout
@@ -1265,7 +1250,6 @@ def update_workout(
"status": "success"
}
- :param integer auth_user_id: authenticate user id (from JSON Web Token)
:param string workout_short_id: workout short id
: Union[Tuple[Dict, int], HttpResponse]:
"""
Delete an workout
@@ -1339,7 +1323,6 @@ def delete_workout(
HTTP/1.1 204 NO CONTENT
Content-Type: application/json
- :param integer auth_user_id: authenticate user id (from JSON Web Token)
:param string workout_short_id: workout short id
:reqheader Authorization: OAuth 2.0 Bearer Token
@@ -1359,7 +1342,7 @@ def delete_workout(
workout = Workout.query.filter_by(uuid=workout_uuid).first()
if not workout:
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:
return error_response
From 9e7fbd6eb4e31d12336887fee4276fe7651a0d5e Mon Sep 17 00:00:00 2001
From: Sam
Date: Wed, 1 Dec 2021 19:31:42 +0100
Subject: [PATCH 2/6] API - remove useless user queries
---
fittrackee/tests/workouts/test_gpx_utils.py | 4 ++--
fittrackee/users/utils.py | 8 --------
fittrackee/workouts/utils.py | 15 +++++++--------
fittrackee/workouts/workouts.py | 2 +-
4 files changed, 10 insertions(+), 19 deletions(-)
diff --git a/fittrackee/tests/workouts/test_gpx_utils.py b/fittrackee/tests/workouts/test_gpx_utils.py
index 7c80d5de..df5da68c 100644
--- a/fittrackee/tests/workouts/test_gpx_utils.py
+++ b/fittrackee/tests/workouts/test_gpx_utils.py
@@ -46,7 +46,7 @@ class TestStoppedSpeedThreshold:
) as gpx_track_segment_mock:
process_files(
- auth_user_id=user_1.id,
+ auth_user=user_1,
folders=folders,
workout_data={'sport_id': sport_id},
workout_file=gpx_file_storage,
@@ -76,7 +76,7 @@ class TestStoppedSpeedThreshold:
) as gpx_track_segment_mock:
process_files(
- auth_user_id=user_1.id,
+ auth_user=user_1,
folders=folders,
workout_data={'sport_id': sport_1_cycling.id},
workout_file=gpx_file_storage,
diff --git a/fittrackee/users/utils.py b/fittrackee/users/utils.py
index 6f6a1984..cd881874 100644
--- a/fittrackee/users/utils.py
+++ b/fittrackee/users/utils.py
@@ -12,14 +12,6 @@ from fittrackee.responses import (
from .models import User
-def is_admin(user_id: int) -> bool:
- """
- Return if user has admin rights
- """
- user = User.query.filter_by(id=user_id).first()
- return user.admin
-
-
def is_valid_email(email: str) -> bool:
"""
Return if email format is valid
diff --git a/fittrackee/workouts/utils.py b/fittrackee/workouts/utils.py
index 95488cbf..015b825b 100644
--- a/fittrackee/workouts/utils.py
+++ b/fittrackee/workouts/utils.py
@@ -299,9 +299,9 @@ def process_one_gpx_file(
gpx_data, map_data, weather_data = get_gpx_info(
params['file_path'], stopped_speed_threshold
)
- auth_user_id = params['user'].id
+ auth_user = params['auth_user']
new_filepath = get_new_file_path(
- auth_user_id=auth_user_id,
+ auth_user_id=auth_user.id,
workout_date=gpx_data['start'],
old_filename=filename,
sport=params['sport_label'],
@@ -311,7 +311,7 @@ def process_one_gpx_file(
gpx_data['filename'] = new_filepath
map_filepath = get_new_file_path(
- auth_user_id=auth_user_id,
+ auth_user_id=auth_user.id,
workout_date=gpx_data['start'],
extension='.png',
sport=params['sport_label'],
@@ -325,7 +325,7 @@ def process_one_gpx_file(
try:
new_workout = create_workout(
- params['user'], params['workout_data'], gpx_data
+ auth_user, params['workout_data'], gpx_data
)
new_workout.map = map_filepath
new_workout.map_id = get_map_hash(map_filepath)
@@ -380,7 +380,7 @@ def process_zip_archive(
def process_files(
- auth_user_id: int,
+ auth_user: User,
workout_data: Dict,
workout_file: FileStorage,
folders: Dict,
@@ -399,9 +399,8 @@ def process_files(
'error',
f"Sport id: {workout_data.get('sport_id')} does not exist",
)
- user = User.query.filter_by(id=auth_user_id).first()
sport_preferences = UserSportPreference.query.filter_by(
- user_id=user.id, sport_id=sport.id
+ user_id=auth_user.id, sport_id=sport.id
).first()
stopped_speed_threshold = (
sport.stopped_speed_threshold
@@ -410,7 +409,7 @@ def process_files(
)
common_params = {
- 'user': user,
+ 'auth_user': auth_user,
'workout_data': workout_data,
'file_path': file_path,
'sport_label': sport.label,
diff --git a/fittrackee/workouts/workouts.py b/fittrackee/workouts/workouts.py
index 192e7217..e0828c11 100644
--- a/fittrackee/workouts/workouts.py
+++ b/fittrackee/workouts/workouts.py
@@ -980,7 +980,7 @@ def post_workout(auth_user: User) -> Union[Tuple[Dict, int], HttpResponse]:
try:
new_workouts = process_files(
- auth_user.id, workout_data, workout_file, folders
+ auth_user, workout_data, workout_file, folders
)
if len(new_workouts) > 0:
response_object = {
From c4d19089e40ed0197fd192b6ccdb71ac153bf9a6 Mon Sep 17 00:00:00 2001
From: Sam
Date: Wed, 1 Dec 2021 19:39:45 +0100
Subject: [PATCH 3/6] update documentation
---
docs/api/configuration.html | 15 ++++-------
docs/api/records.html | 11 +++-----
docs/api/sports.html | 13 +++------
docs/api/stats.html | 13 +++------
docs/api/users.html | 18 ++++---------
docs/api/workouts.html | 53 +++++++++++--------------------------
docs/searchindex.js | 2 +-
fittrackee/users/utils.py | 4 +--
8 files changed, 37 insertions(+), 92 deletions(-)
diff --git a/docs/api/configuration.html b/docs/api/configuration.html
index 2b5f4f41..7837f53a 100644
--- a/docs/api/configuration.html
+++ b/docs/api/configuration.html
@@ -191,13 +191,8 @@
-- Parameters
+- Request JSON Object
-
-
-- Request JSON Object
-
gpx_limit_import (integer) – max number of files in zip archive
is_registration_enabled (boolean) – is registration enabled ?
max_single_file_size (integer) – max size of a single file
@@ -205,13 +200,13 @@
max_users (integer) – max users allowed to register on instance
-- Request Headers
-
+- Request Headers
+-
-- Status Codes
-
- Parameters
-
diff --git a/docs/api/stats.html b/docs/api/stats.html
index d1bb4f5f..9b767bd2 100644
--- a/docs/api/stats.html
+++ b/docs/api/stats.html
@@ -206,7 +206,6 @@
- Parameters
-
@@ -321,7 +320,6 @@
- Parameters
-
@@ -378,18 +376,13 @@
-- Parameters
+- Request Headers
-
-
-- Request Headers
--
-- Status Codes
-