update docs for remaining endpoints - fix #21
and fix some routes to add authentication
This commit is contained in:
@ -137,7 +137,7 @@ def get_activities(auth_user_id):
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
:param integer auth_user_id: authenticate user id
|
||||
: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 activities per page (default: 5)
|
||||
@ -157,7 +157,10 @@ def get_activities(auth_user_id):
|
||||
:reqheader Authorization: OAuth 2.0 Bearer Token
|
||||
|
||||
:statuscode 200: success
|
||||
:statuscode 401: invalid token
|
||||
:statuscode 401:
|
||||
- Provide a valid auth token.
|
||||
- Signature expired. Please log in again.
|
||||
- Invalid token. Please log in again.
|
||||
:statuscode 500:
|
||||
|
||||
"""
|
||||
@ -302,13 +305,16 @@ def get_activity(auth_user_id, activity_id):
|
||||
"status": "not found"
|
||||
}
|
||||
|
||||
:param integer auth_user_id: authenticate user id
|
||||
:param integer auth_user_id: authenticate user id (from JSON Web Token)
|
||||
:param integer activity_id: activity id
|
||||
|
||||
:reqheader Authorization: OAuth 2.0 Bearer Token
|
||||
|
||||
:statuscode 200: success
|
||||
:statuscode 401: Provide a valid auth token
|
||||
:statuscode 401:
|
||||
- Provide a valid auth token.
|
||||
- Signature expired. Please log in again.
|
||||
- Invalid token. Please log in again.
|
||||
:statuscode 403: You do not have permissions
|
||||
:statuscode 404: activity not found
|
||||
|
||||
@ -416,14 +422,17 @@ def get_activity_gpx(auth_user_id, activity_id):
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
:param integer auth_user_id: authenticate user id
|
||||
:param integer auth_user_id: authenticate user id (from JSON Web Token)
|
||||
:param integer activity_id: activity id
|
||||
|
||||
:reqheader Authorization: OAuth 2.0 Bearer Token
|
||||
|
||||
:statuscode 200: success
|
||||
:statuscode 400: no gpx file for this activity
|
||||
:statuscode 401: invalid token
|
||||
:statuscode 401:
|
||||
- Provide a valid auth token.
|
||||
- Signature expired. Please log in again.
|
||||
- Invalid token. Please log in again.
|
||||
:statuscode 404: activity not found
|
||||
:statuscode 500:
|
||||
|
||||
@ -476,14 +485,17 @@ def get_activity_chart_data(auth_user_id, activity_id):
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
:param integer auth_user_id: authenticate user id
|
||||
:param integer auth_user_id: authenticate user id (from JSON Web Token)
|
||||
:param integer activity_id: activity id
|
||||
|
||||
:reqheader Authorization: OAuth 2.0 Bearer Token
|
||||
|
||||
:statuscode 200: success
|
||||
:statuscode 400: no gpx file for this activity
|
||||
:statuscode 401: invalid token
|
||||
:statuscode 401:
|
||||
- Provide a valid auth token.
|
||||
- Signature expired. Please log in again.
|
||||
- Invalid token. Please log in again.
|
||||
:statuscode 404: activity not found
|
||||
:statuscode 500:
|
||||
|
||||
@ -511,13 +523,13 @@ def get_map(map_id):
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: image/png
|
||||
|
||||
:param integer auth_user_id: authenticate user id
|
||||
:param string map_id: activity map id
|
||||
|
||||
:reqheader Authorization: OAuth 2.0 Bearer Token
|
||||
|
||||
:statuscode 200: success
|
||||
:statuscode 401: invalid token
|
||||
:statuscode 401:
|
||||
- Provide a valid auth token.
|
||||
- Signature expired. Please log in again.
|
||||
- Invalid token. Please log in again.
|
||||
:statuscode 404: map does not exist
|
||||
:statuscode 500:
|
||||
|
||||
@ -636,7 +648,7 @@ def post_activity(auth_user_id):
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
:param integer auth_user_id: authenticate user id
|
||||
: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": ""}``)
|
||||
@ -649,7 +661,10 @@ def post_activity(auth_user_id):
|
||||
- No file part.
|
||||
- No selected file.
|
||||
- File extension not allowed.
|
||||
:statuscode 401: invalid token
|
||||
:statuscode 401:
|
||||
- Provide a valid auth token.
|
||||
- Signature expired. Please log in again.
|
||||
- Invalid token. Please log in again.
|
||||
:statuscode 500:
|
||||
|
||||
"""
|
||||
@ -721,7 +736,7 @@ def post_activity_no_gpx(auth_user_id):
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /api/activities/ HTTP/1.1
|
||||
POST /api/activities/no_gpx HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
**Example response**:
|
||||
@ -805,7 +820,7 @@ def post_activity_no_gpx(auth_user_id):
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
:param integer auth_user_id: authenticate user id
|
||||
:param integer auth_user_id: authenticate user id (from JSON Web Token)
|
||||
|
||||
:<json string activity_date: activity date (format: ``%Y-%m-%d %H:%M``)
|
||||
:<json float distance: activity distance in km
|
||||
@ -818,7 +833,10 @@ def post_activity_no_gpx(auth_user_id):
|
||||
|
||||
:statuscode 201: activity created
|
||||
:statuscode 400: invalid payload
|
||||
:statuscode 401: invalid token
|
||||
:statuscode 401:
|
||||
- Provide a valid auth token.
|
||||
- Signature expired. Please log in again.
|
||||
- Invalid token. Please log in again.
|
||||
:statuscode 500:
|
||||
|
||||
"""
|
||||
@ -951,7 +969,7 @@ def update_activity(auth_user_id, activity_id):
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
:param integer auth_user_id: authenticate user id
|
||||
:param integer auth_user_id: authenticate user id (from JSON Web Token)
|
||||
:param integer activity_id: activity id
|
||||
|
||||
:<json string activity_date: activity date (format: ``%Y-%m-%d %H:%M``)
|
||||
@ -968,7 +986,10 @@ def update_activity(auth_user_id, activity_id):
|
||||
|
||||
:statuscode 200: activity updated
|
||||
:statuscode 400: invalid payload
|
||||
:statuscode 401: invalid token
|
||||
:statuscode 401:
|
||||
- Provide a valid auth token.
|
||||
- Signature expired. Please log in again.
|
||||
- Invalid token. Please log in again.
|
||||
:statuscode 404: activity not found
|
||||
:statuscode 500:
|
||||
|
||||
@ -1039,13 +1060,16 @@ def delete_activity(auth_user_id, activity_id):
|
||||
HTTP/1.1 204 NO CONTENT
|
||||
Content-Type: application/json
|
||||
|
||||
:param integer auth_user_id: authenticate user id
|
||||
:param integer auth_user_id: authenticate user id (from JSON Web Token)
|
||||
:param integer activity_id: activity id
|
||||
|
||||
:reqheader Authorization: OAuth 2.0 Bearer Token
|
||||
|
||||
:statuscode 204: activity deleted
|
||||
:statuscode 401: invalid token
|
||||
:statuscode 401:
|
||||
- Provide a valid auth token.
|
||||
- Signature expired. Please log in again.
|
||||
- Invalid token. Please log in again.
|
||||
:statuscode 404: activity not found
|
||||
:statuscode 500: Error. Please try again or contact the administrator.
|
||||
|
||||
|
@ -9,7 +9,101 @@ records_blueprint = Blueprint('records', __name__)
|
||||
@records_blueprint.route('/records', methods=['GET'])
|
||||
@authenticate
|
||||
def get_records(auth_user_id):
|
||||
"""Get all records for authenticated user"""
|
||||
"""
|
||||
Get all records for authenticated user.
|
||||
|
||||
Following types of records are available:
|
||||
- average speed (record_type: 'AS')
|
||||
- farest distance (record_type: 'FD')
|
||||
- longest duration (record_type: 'LD')
|
||||
- maximum speed (record_type: 'MS')
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/records HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
**Example responses**:
|
||||
|
||||
- returning records
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"data": {
|
||||
"records": [
|
||||
{
|
||||
"activity_date": "Sun, 07 Jul 2019 08:00:00 GMT",
|
||||
"activity_id": 4,
|
||||
"id": 9,
|
||||
"record_type": "AS",
|
||||
"sport_id": 1,
|
||||
"user_id": 1,
|
||||
"value": 18
|
||||
},
|
||||
{
|
||||
"activity_date": "Sun, 07 Jul 2019 08:00:00 GMT",
|
||||
"activity_id": 4,
|
||||
"id": 10,
|
||||
"record_type": "FD",
|
||||
"sport_id": 1,
|
||||
"user_id": 1,
|
||||
"value": 18
|
||||
},
|
||||
{
|
||||
"activity_date": "Sun, 07 Jul 2019 08:00:00 GMT",
|
||||
"activity_id": 7,
|
||||
"id": 11,
|
||||
"record_type": "LD",
|
||||
"sport_id": 1,
|
||||
"user_id": 1,
|
||||
"value": "1:01:00"
|
||||
},
|
||||
{
|
||||
"activity_date": "Sun, 07 Jul 2019 08:00:00 GMT",
|
||||
"activity_id": 4,
|
||||
"id": 12,
|
||||
"record_type": "MS",
|
||||
"sport_id": 1,
|
||||
"user_id": 1,
|
||||
"value": 18
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
- no records
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"data": {
|
||||
"records": []
|
||||
},
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
:param integer auth_user_id: authenticate user id (from JSON Web Token)
|
||||
|
||||
:reqheader Authorization: OAuth 2.0 Bearer Token
|
||||
|
||||
:statuscode 200: success
|
||||
:statuscode 401:
|
||||
- Provide a valid auth token.
|
||||
- Signature expired. Please log in again.
|
||||
- Invalid token. Please log in again.
|
||||
|
||||
"""
|
||||
|
||||
records = Record.query.filter_by(user_id=auth_user_id)\
|
||||
.order_by(
|
||||
Record.sport_id.asc(),
|
||||
|
@ -11,7 +11,79 @@ sports_blueprint = Blueprint('sports', __name__)
|
||||
@sports_blueprint.route('/sports', methods=['GET'])
|
||||
@authenticate
|
||||
def get_sports(auth_user_id):
|
||||
"""Get all sports"""
|
||||
"""
|
||||
Get all sports
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/sports HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"data": {
|
||||
"sports": [
|
||||
{
|
||||
"_can_be_deleted": false,
|
||||
"id": 1,
|
||||
"img": "/img/sports/cycling-sport.png",
|
||||
"label": "Cycling (Sport)"
|
||||
},
|
||||
{
|
||||
"_can_be_deleted": false,
|
||||
"id": 2,
|
||||
"img": "/img/sports/cycling-transport.png",
|
||||
"label": "Cycling (Transport)"
|
||||
},
|
||||
{
|
||||
"_can_be_deleted": false,
|
||||
"id": 3,
|
||||
"img": "/img/sports/hiking.png",
|
||||
"label": "Hiking"
|
||||
},
|
||||
{
|
||||
"_can_be_deleted": false,
|
||||
"id": 4,
|
||||
"img": "/img/sports/mountain-biking.png",
|
||||
"label": "Mountain Biking"
|
||||
},
|
||||
{
|
||||
"_can_be_deleted": false,
|
||||
"id": 5,
|
||||
"img": "/img/sports/running.png",
|
||||
"label": "Running"
|
||||
},
|
||||
{
|
||||
"_can_be_deleted": false,
|
||||
"id": 6,
|
||||
"img": "/img/sports/walking.png",
|
||||
"label": "Walking"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
:param integer auth_user_id: authenticate user id (from JSON Web Token)
|
||||
|
||||
:reqheader Authorization: OAuth 2.0 Bearer Token
|
||||
|
||||
:statuscode 200: success
|
||||
:statuscode 401:
|
||||
- Provide a valid auth token.
|
||||
- Signature expired. Please log in again.
|
||||
- Invalid token. Please log in again.
|
||||
|
||||
"""
|
||||
|
||||
sports = Sport.query.order_by(Sport.id).all()
|
||||
response_object = {
|
||||
'status': 'success',
|
||||
@ -25,7 +97,66 @@ def get_sports(auth_user_id):
|
||||
@sports_blueprint.route('/sports/<int:sport_id>', methods=['GET'])
|
||||
@authenticate
|
||||
def get_sport(auth_user_id, sport_id):
|
||||
"""Get a sport"""
|
||||
"""Get a sport
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/sports/1 HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
- success
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"data": {
|
||||
"sports": [
|
||||
{
|
||||
"_can_be_deleted": false,
|
||||
"id": 1,
|
||||
"img": "/img/sports/cycling-sport.png",
|
||||
"label": "Cycling (Sport)"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
- sport not found
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 404 NOT FOUND
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"data": {
|
||||
"sports": []
|
||||
},
|
||||
"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
|
||||
|
||||
:statuscode 200: success
|
||||
:statuscode 401:
|
||||
- Provide a valid auth token.
|
||||
- Signature expired. Please log in again.
|
||||
- Invalid token. Please log in again.
|
||||
:statuscode 404: sport not found
|
||||
|
||||
"""
|
||||
|
||||
sport = Sport.query.filter_by(id=sport_id).first()
|
||||
if sport:
|
||||
response_object = {
|
||||
@ -46,6 +177,8 @@ def get_sport(auth_user_id, sport_id):
|
||||
return jsonify(response_object), code
|
||||
|
||||
|
||||
# no administration - no documentation for now
|
||||
|
||||
@sports_blueprint.route('/sports', methods=['POST'])
|
||||
@authenticate_as_admin
|
||||
def post_sport(auth_user_id):
|
||||
|
@ -130,12 +130,180 @@ def get_activities(user_id, filter_type):
|
||||
@stats_blueprint.route('/stats/<int:user_id>/by_time', methods=['GET'])
|
||||
@authenticate
|
||||
def get_activities_by_time(auth_user_id, user_id):
|
||||
"""Get activities statistics for a user by time"""
|
||||
"""
|
||||
Get activities statistics for a user by time
|
||||
|
||||
**Example requests**:
|
||||
|
||||
- without parameters
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/stats/1/by_time HTTP/1.1
|
||||
|
||||
- with parameters
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/stats/1/by_time?from=2018-01-01&to=2018-06-30&time=week HTTP/1.1
|
||||
|
||||
**Example responses**:
|
||||
|
||||
- success
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"data": {
|
||||
"statistics": {
|
||||
"2017": {
|
||||
"3": {
|
||||
"nb_activities": 2,
|
||||
"total_distance": 15.282,
|
||||
"total_duration": 12341
|
||||
}
|
||||
},
|
||||
"2019": {
|
||||
"1": {
|
||||
"nb_activities": 3,
|
||||
"total_distance": 47,
|
||||
"total_duration": 9960
|
||||
},
|
||||
"2": {
|
||||
"nb_activities": 1,
|
||||
"total_distance": 5.613,
|
||||
"total_duration": 1267
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
- no activities
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"data": {
|
||||
"statistics": {}
|
||||
},
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
:param integer auth_user_id: authenticate user id (from JSON Web Token)
|
||||
:param integer user_id: user id
|
||||
|
||||
:query string from: start date (format: ``%Y-%m-%d``)
|
||||
:query string to: end date (format: ``%Y-%m-%d``)
|
||||
:query string time: time frame:
|
||||
|
||||
- ``week``: week starting Sunday
|
||||
- ``weekm``: week starting Monday
|
||||
- ``month``: month
|
||||
- ``year``: year (default)
|
||||
|
||||
:reqheader Authorization: OAuth 2.0 Bearer Token
|
||||
|
||||
:statuscode 200: success
|
||||
:statuscode 401:
|
||||
- Provide a valid auth token.
|
||||
- Signature expired. Please log in again.
|
||||
- Invalid token. Please log in again.
|
||||
:statuscode 404:
|
||||
- User does not exist.
|
||||
|
||||
"""
|
||||
return get_activities(user_id, 'by_time')
|
||||
|
||||
|
||||
@stats_blueprint.route('/stats/<int:user_id>/by_sport', methods=['GET'])
|
||||
@authenticate
|
||||
def get_activities_by_sport(auth_user_id, user_id):
|
||||
"""Get activities statistics for a user by sport"""
|
||||
"""
|
||||
Get activities statistics for a user by sport
|
||||
|
||||
**Example requests**:
|
||||
|
||||
- without parameters (get stats for all sports with activities)
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/stats/1/by_sport HTTP/1.1
|
||||
|
||||
- with sport id
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/stats/1/by_sport?sport_id=1 HTTP/1.1
|
||||
|
||||
**Example responses**:
|
||||
|
||||
- success
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"data": {
|
||||
"statistics": {
|
||||
"1": {
|
||||
"nb_activities": 3,
|
||||
"total_distance": 47,
|
||||
"total_duration": 9960
|
||||
},
|
||||
"2": {
|
||||
"nb_activities": 1,
|
||||
"total_distance": 5.613,
|
||||
"total_duration": 1267
|
||||
},
|
||||
"3": {
|
||||
"nb_activities": 2,
|
||||
"total_distance": 15.282,
|
||||
"total_duration": 12341
|
||||
}
|
||||
}
|
||||
},
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
- no activities
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"data": {
|
||||
"statistics": {}
|
||||
},
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
:param integer auth_user_id: authenticate user id (from JSON Web Token)
|
||||
:param integer user_id: user id
|
||||
|
||||
:query integer sport_id: sport id
|
||||
|
||||
:reqheader Authorization: OAuth 2.0 Bearer Token
|
||||
|
||||
:statuscode 200: success
|
||||
:statuscode 401:
|
||||
- Provide a valid auth token.
|
||||
- Signature expired. Please log in again.
|
||||
- Invalid token. Please log in again.
|
||||
:statuscode 404:
|
||||
- User does not exist.
|
||||
- Sport does not exist.
|
||||
|
||||
"""
|
||||
return get_activities(user_id, 'by_sport')
|
||||
|
@ -16,8 +16,23 @@ def test_ping(app):
|
||||
def test_single_user(app, user_1):
|
||||
"""=> Get single user details"""
|
||||
client = app.test_client()
|
||||
|
||||
response = client.get(f'/api/users/{user_1.id}')
|
||||
resp_login = client.post(
|
||||
'/api/auth/login',
|
||||
data=json.dumps(dict(
|
||||
email='test@test.com',
|
||||
password='12345678'
|
||||
)),
|
||||
content_type='application/json'
|
||||
)
|
||||
response = client.get(
|
||||
f'/api/users/{user_1.id}',
|
||||
content_type='application/json',
|
||||
headers=dict(
|
||||
Authorization='Bearer ' + json.loads(
|
||||
resp_login.data.decode()
|
||||
)['auth_token']
|
||||
)
|
||||
)
|
||||
data = json.loads(response.data.decode())
|
||||
|
||||
assert response.status_code == 200
|
||||
@ -45,8 +60,23 @@ def test_single_user_with_activities(
|
||||
):
|
||||
"""=> Get single user details"""
|
||||
client = app.test_client()
|
||||
|
||||
response = client.get(f'/api/users/{user_1.id}')
|
||||
resp_login = client.post(
|
||||
'/api/auth/login',
|
||||
data=json.dumps(dict(
|
||||
email='test@test.com',
|
||||
password='12345678'
|
||||
)),
|
||||
content_type='application/json'
|
||||
)
|
||||
response = client.get(
|
||||
f'/api/users/{user_1.id}',
|
||||
content_type='application/json',
|
||||
headers=dict(
|
||||
Authorization='Bearer ' + json.loads(
|
||||
resp_login.data.decode()
|
||||
)['auth_token']
|
||||
)
|
||||
)
|
||||
data = json.loads(response.data.decode())
|
||||
|
||||
assert response.status_code == 200
|
||||
@ -68,33 +98,80 @@ def test_single_user_with_activities(
|
||||
assert data['data']['total_duration'] == '1:57:04'
|
||||
|
||||
|
||||
def test_single_user_no_id(app):
|
||||
def test_single_user_no_id(app, user_1):
|
||||
"""=> Ensure error is thrown if an id is not provided."""
|
||||
client = app.test_client()
|
||||
response = client.get(f'/api/users/blah')
|
||||
resp_login = client.post(
|
||||
'/api/auth/login',
|
||||
data=json.dumps(dict(
|
||||
email='test@test.com',
|
||||
password='12345678'
|
||||
)),
|
||||
content_type='application/json'
|
||||
)
|
||||
response = client.get(
|
||||
'/api/users/blah',
|
||||
content_type='application/json',
|
||||
headers=dict(
|
||||
Authorization='Bearer ' + json.loads(
|
||||
resp_login.data.decode()
|
||||
)['auth_token']
|
||||
)
|
||||
)
|
||||
data = json.loads(response.data.decode())
|
||||
|
||||
assert response.status_code == 404
|
||||
assert 'fail' in data['status']
|
||||
assert 'User does not exist' in data['message']
|
||||
assert 'User does not exist.' in data['message']
|
||||
|
||||
|
||||
def test_single_user_wrong_id(app):
|
||||
def test_single_user_wrong_id(app, user_1):
|
||||
"""=> Ensure error is thrown if the id does not exist."""
|
||||
client = app.test_client()
|
||||
response = client.get(f'/api/users/99999999999')
|
||||
resp_login = client.post(
|
||||
'/api/auth/login',
|
||||
data=json.dumps(dict(
|
||||
email='test@test.com',
|
||||
password='12345678'
|
||||
)),
|
||||
content_type='application/json'
|
||||
)
|
||||
response = client.get(
|
||||
'/api/users/99999999999',
|
||||
content_type='application/json',
|
||||
headers=dict(
|
||||
Authorization='Bearer ' + json.loads(
|
||||
resp_login.data.decode()
|
||||
)['auth_token']
|
||||
)
|
||||
)
|
||||
data = json.loads(response.data.decode())
|
||||
|
||||
assert response.status_code == 404
|
||||
assert 'fail' in data['status']
|
||||
assert 'User does not exist' in data['message']
|
||||
assert 'User does not exist.' in data['message']
|
||||
|
||||
|
||||
def test_users_list(app, user_1, user_2):
|
||||
"""=> Ensure get single user behaves correctly."""
|
||||
|
||||
client = app.test_client()
|
||||
response = client.get('/api/users')
|
||||
resp_login = client.post(
|
||||
'/api/auth/login',
|
||||
data=json.dumps(dict(
|
||||
email='test@test.com',
|
||||
password='12345678'
|
||||
)),
|
||||
content_type='application/json'
|
||||
)
|
||||
response = client.get(
|
||||
'/api/users',
|
||||
headers=dict(
|
||||
Authorization='Bearer ' + json.loads(
|
||||
resp_login.data.decode()
|
||||
)['auth_token']
|
||||
)
|
||||
)
|
||||
data = json.loads(response.data.decode())
|
||||
|
||||
assert response.status_code == 200
|
||||
@ -129,3 +206,53 @@ def test_decode_auth_token(app, user_1):
|
||||
auth_token = user_1.encode_auth_token(user_1.id)
|
||||
assert isinstance(auth_token, bytes)
|
||||
assert User.decode_auth_token(auth_token) == user_1.id
|
||||
|
||||
|
||||
def test_user_no_picture(app, user_1):
|
||||
client = app.test_client()
|
||||
resp_login = client.post(
|
||||
'/api/auth/login',
|
||||
data=json.dumps(dict(
|
||||
email='test@test.com',
|
||||
password='12345678'
|
||||
)),
|
||||
content_type='application/json'
|
||||
)
|
||||
response = client.get(
|
||||
'/api/users/1/picture',
|
||||
headers=dict(
|
||||
Authorization='Bearer ' + json.loads(
|
||||
resp_login.data.decode()
|
||||
)['auth_token']
|
||||
)
|
||||
)
|
||||
data = json.loads(response.data.decode())
|
||||
|
||||
assert response.status_code == 404
|
||||
assert 'not found' in data['status']
|
||||
assert 'No picture.' in data['message']
|
||||
|
||||
|
||||
def test_user_picture_no_user(app, user_1):
|
||||
client = app.test_client()
|
||||
resp_login = client.post(
|
||||
'/api/auth/login',
|
||||
data=json.dumps(dict(
|
||||
email='test@test.com',
|
||||
password='12345678'
|
||||
)),
|
||||
content_type='application/json'
|
||||
)
|
||||
response = client.get(
|
||||
'/api/users/2/picture',
|
||||
headers=dict(
|
||||
Authorization='Bearer ' + json.loads(
|
||||
resp_login.data.decode()
|
||||
)['auth_token']
|
||||
)
|
||||
)
|
||||
data = json.loads(response.data.decode())
|
||||
|
||||
assert response.status_code == 404
|
||||
assert 'fail' in data['status']
|
||||
assert 'User does not exist.' in data['message']
|
||||
|
@ -22,7 +22,7 @@ def register_user():
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /auth/register HTTP/1.1
|
||||
POST /api/auth/register HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
**Example responses**:
|
||||
@ -153,7 +153,7 @@ def login_user():
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /auth/login HTTP/1.1
|
||||
POST /api/auth/login HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
**Example responses**:
|
||||
@ -240,7 +240,7 @@ def logout_user(user_id):
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /auth/logout HTTP/1.1
|
||||
GET /api/auth/logout HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
**Example responses**:
|
||||
@ -310,7 +310,7 @@ def get_user_status(user_id):
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /auth/profile HTTP/1.1
|
||||
GET /api/auth/profile HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
**Example response**:
|
||||
@ -345,7 +345,10 @@ def get_user_status(user_id):
|
||||
:reqheader Authorization: OAuth 2.0 Bearer Token
|
||||
|
||||
:statuscode 200: success.
|
||||
:statuscode 401: Provide a valid auth token.
|
||||
:statuscode 401:
|
||||
- Provide a valid auth token.
|
||||
- Signature expired. Please log in again.
|
||||
- Invalid token. Please log in again.
|
||||
|
||||
"""
|
||||
user = User.query.filter_by(id=user_id).first()
|
||||
@ -366,7 +369,7 @@ def edit_user(user_id):
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /auth/profile/edit HTTP/1.1
|
||||
POST /api/auth/profile/edit HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
**Example response**:
|
||||
@ -413,7 +416,10 @@ def edit_user(user_id):
|
||||
:statuscode 400:
|
||||
- Invalid payload.
|
||||
- Password and password confirmation don't match.
|
||||
:statuscode 401: Provide a valid auth token.
|
||||
:statuscode 401:
|
||||
- Provide a valid auth token.
|
||||
- Signature expired. Please log in again.
|
||||
- Invalid token. Please log in again.
|
||||
:statuscode 500: Error. Please try again or contact the administrator.
|
||||
|
||||
"""
|
||||
@ -489,7 +495,7 @@ def edit_picture(user_id):
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /auth/picture HTTP/1.1
|
||||
POST /api/auth/picture HTTP/1.1
|
||||
Content-Type: multipart/form-data
|
||||
|
||||
**Example response**:
|
||||
@ -531,7 +537,10 @@ def edit_picture(user_id):
|
||||
- No file part.
|
||||
- No selected file.
|
||||
- File extension not allowed.
|
||||
:statuscode 401: Provide a valid auth token.
|
||||
:statuscode 401:
|
||||
- Provide a valid auth token.
|
||||
- Signature expired. Please log in again.
|
||||
- Invalid token. Please log in again.
|
||||
:statuscode 500: Error during picture update.
|
||||
|
||||
"""
|
||||
@ -592,7 +601,7 @@ def del_picture(user_id):
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
DELETE /auth/picture HTTP/1.1
|
||||
DELETE /api/auth/picture HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
**Example response**:
|
||||
@ -605,7 +614,10 @@ def del_picture(user_id):
|
||||
:reqheader Authorization: OAuth 2.0 Bearer Token
|
||||
|
||||
:statuscode 204: picture deleted
|
||||
:statuscode 401: Provide a valid auth token.
|
||||
:statuscode 401:
|
||||
- Provide a valid auth token.
|
||||
- Signature expired. Please log in again.
|
||||
- Invalid token. Please log in again.
|
||||
:statuscode 500: Error during picture deletion.
|
||||
|
||||
"""
|
||||
|
@ -2,13 +2,86 @@ from flask import Blueprint, jsonify, send_file
|
||||
|
||||
from ..activities.utils_files import get_absolute_file_path
|
||||
from .models import User
|
||||
from .utils import authenticate
|
||||
|
||||
users_blueprint = Blueprint('users', __name__)
|
||||
|
||||
|
||||
@users_blueprint.route('/users', methods=['GET'])
|
||||
def get_users():
|
||||
"""Get all users"""
|
||||
@authenticate
|
||||
def get_users(auth_user_id):
|
||||
"""
|
||||
Get all users
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/users HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"data": {
|
||||
"users": [
|
||||
{
|
||||
"admin": true,
|
||||
"bio": null,
|
||||
"birth_date": null,
|
||||
"created_at": "Sun, 14 Jul 2019 14:09:58 GMT",
|
||||
"email": "admin@example.com",
|
||||
"first_name": null,
|
||||
"id": 1,
|
||||
"last_name": null,
|
||||
"location": null,
|
||||
"nb_activities": 6,
|
||||
"nb_sports": 3,
|
||||
"picture": false,
|
||||
"timezone": "Europe/Paris",
|
||||
"total_distance": 67.895,
|
||||
"total_duration": "6:50:27",
|
||||
"username": "admin"
|
||||
},
|
||||
{
|
||||
"admin": false,
|
||||
"bio": null,
|
||||
"birth_date": null,
|
||||
"created_at": "Sat, 20 Jul 2019 11:27:03 GMT",
|
||||
"email": "sam@example.com",
|
||||
"first_name": null,
|
||||
"id": 2,
|
||||
"last_name": null,
|
||||
"location": null,
|
||||
"nb_activities": 0,
|
||||
"nb_sports": 0,
|
||||
"picture": false,
|
||||
"timezone": "Europe/Paris",
|
||||
"total_distance": 0,
|
||||
"total_duration": "0:00:00",
|
||||
"username": "sam"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
:param integer auth_user_id: authenticate user id (from JSON Web Token)
|
||||
|
||||
:reqheader Authorization: OAuth 2.0 Bearer Token
|
||||
|
||||
:statuscode 200: success
|
||||
:statuscode 401:
|
||||
- Provide a valid auth token.
|
||||
- Signature expired. Please log in again.
|
||||
- Invalid token. Please log in again.
|
||||
|
||||
"""
|
||||
users = User.query.all()
|
||||
response_object = {
|
||||
'status': 'success',
|
||||
@ -20,11 +93,64 @@ def get_users():
|
||||
|
||||
|
||||
@users_blueprint.route('/users/<user_id>', methods=['GET'])
|
||||
def get_single_user(user_id):
|
||||
"""Get single user details"""
|
||||
@authenticate
|
||||
def get_single_user(auth_user_id, user_id):
|
||||
"""
|
||||
Get single user details
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/users/1 HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"data": {
|
||||
"admin": true,
|
||||
"bio": null,
|
||||
"birth_date": null,
|
||||
"created_at": "Sun, 14 Jul 2019 14:09:58 GMT",
|
||||
"email": "admin@example.com",
|
||||
"first_name": null,
|
||||
"id": 1,
|
||||
"last_name": null,
|
||||
"location": null,
|
||||
"nb_activities": 6,
|
||||
"nb_sports": 3,
|
||||
"picture": false,
|
||||
"timezone": "Europe/Paris",
|
||||
"total_distance": 67.895,
|
||||
"total_duration": "6:50:27",
|
||||
"username": "admin"
|
||||
},
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
:param integer auth_user_id: authenticate user id (from JSON Web Token)
|
||||
:param integer user_id: user id
|
||||
|
||||
:reqheader Authorization: OAuth 2.0 Bearer Token
|
||||
|
||||
:statuscode 200: success
|
||||
:statuscode 401:
|
||||
- Provide a valid auth token.
|
||||
- Signature expired. Please log in again.
|
||||
- Invalid token. Please log in again.
|
||||
:statuscode 404:
|
||||
- User does not exist
|
||||
"""
|
||||
|
||||
response_object = {
|
||||
'status': 'fail',
|
||||
'message': 'User does not exist'
|
||||
'message': 'User does not exist.'
|
||||
}
|
||||
try:
|
||||
user = User.query.filter_by(id=int(user_id)).first()
|
||||
@ -41,26 +167,83 @@ def get_single_user(user_id):
|
||||
|
||||
|
||||
@users_blueprint.route('/users/<user_id>/picture', methods=['GET'])
|
||||
def get_picture(user_id):
|
||||
""" get user picture """
|
||||
@authenticate
|
||||
def get_picture(auth_user_id, user_id):
|
||||
""" get user picture
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/users/1/picture HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: image/jpeg
|
||||
|
||||
:param integer auth_user_id: authenticate user id (from JSON Web Token)
|
||||
:param integer user_id: user id
|
||||
|
||||
:statuscode 200: success
|
||||
:statuscode 401:
|
||||
- Provide a valid auth token.
|
||||
- Signature expired. Please log in again.
|
||||
- Invalid token. Please log in again.
|
||||
:statuscode 404:
|
||||
- User does not exist
|
||||
- No picture.
|
||||
|
||||
"""
|
||||
response_object = {
|
||||
'status': 'fail',
|
||||
'message': 'User does not exist'
|
||||
'status': 'not found',
|
||||
'message': 'No picture.'
|
||||
}
|
||||
try:
|
||||
user = User.query.filter_by(id=int(user_id)).first()
|
||||
if not user:
|
||||
response_object = {
|
||||
'status': 'fail',
|
||||
'message': 'User does not exist.'
|
||||
}
|
||||
return jsonify(response_object), 404
|
||||
else:
|
||||
if user.picture is not None:
|
||||
picture_path = get_absolute_file_path(user.picture)
|
||||
return send_file(picture_path)
|
||||
except ValueError:
|
||||
return jsonify(response_object), 404
|
||||
except Exception:
|
||||
return jsonify(response_object), 404
|
||||
|
||||
|
||||
@users_blueprint.route('/ping', methods=['GET'])
|
||||
def ping_pong():
|
||||
""" health check endpoint """
|
||||
""" health check endpoint
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/ping HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"message": "pong!",
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
:statuscode 200: success
|
||||
|
||||
"""
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'message': 'pong!'
|
||||
|
Reference in New Issue
Block a user