From cb46d6f2c7b292cb097616d8acdd72d4fe33efd8 Mon Sep 17 00:00:00 2001 From: Farbod Date: Sun, 31 Jan 2021 16:55:30 +0330 Subject: [PATCH 1/4] Added route for deleting user's profile --- config.json | 3 ++- resources/user/Profile.py | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 resources/user/Profile.py diff --git a/config.json b/config.json index 7be0af5..5c6cdd0 100644 --- a/config.json +++ b/config.json @@ -8,7 +8,8 @@ "verify_code": "/user/password/verify", "reset_password": "/user/password/reset", "publicity": "/user/publicity", - "activity_list": "/user/activity" + "activity_list": "/user/activity", + "profile": "/user/profile" }, "links":{ "main": "/links", diff --git a/resources/user/Profile.py b/resources/user/Profile.py new file mode 100644 index 0000000..0e63cc8 --- /dev/null +++ b/resources/user/Profile.py @@ -0,0 +1,27 @@ +from flask_restful import Resource +from flask import jsonify, make_response +from flask_jwt_extended import jwt_required, get_jwt_identity +# from flasgger import swag_from + +from src.DbHandler import DbHandler + + +class Profile(Resource): + @jwt_required + # @swag_from('../../yml/publicity_put.yml') + def delete(self): + """ Delete current user's profile """ + current_user_username = get_jwt_identity() + + # status = DbHandler.update_user_publicity(current_user_username, + # publicity) + if status == 'OK': + return make_response( + jsonify(msg="User's profile deleted."), + 200 + ) + else: + return make_response( + jsonify(msg="Server Error!"), + 500 + ) From 031382735e1ba93c945598de516dbfffa0a59bc9 Mon Sep 17 00:00:00 2001 From: Farbod Date: Mon, 1 Feb 2021 11:26:13 +0330 Subject: [PATCH 2/4] Added logic of deleting user profile --- api.py | 5 +++++ common/User.py | 3 ++- resources/user/Profile.py | 3 +-- src/DbHandler.py | 8 ++++++++ 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/api.py b/api.py index adc4543..233b650 100644 --- a/api.py +++ b/api.py @@ -11,6 +11,7 @@ from resources.user.password.ResetPassword import ResetPassword from resources.user.Publicity import Publicity from resources.user.Socializing.Activity import Activity +from resources.user.Profile import Profile from resources.links.LinksDeleter import LinksDeleter from resources.links.LinksAdder import LinksAdder @@ -118,6 +119,10 @@ LinksByUserId, CONFIG.get('routes', {}).get('links', {}).get('by_user_id') ) +api.add_resource( + Profile, + CONFIG.get('routes', {}).get('user', {}).get('profile') +) @app.before_first_request diff --git a/common/User.py b/common/User.py index d6ac867..5806c35 100644 --- a/common/User.py +++ b/common/User.py @@ -15,7 +15,8 @@ class User(db.Model): id = db.Column(db.Integer, primary_key=True, autoincrement=True) username = db.Column(db.String(32), unique=True) password_hash = db.Column(db.String(128)) - links = db.relationship('Link', backref='owner') + links = db.relationship('Link', backref='owner', + cascade="all, delete-orphan") time_created = db.Column(db.DateTime(timezone=True), server_default=func.now()) time_profile_updated = db.Column(db.DateTime) diff --git a/resources/user/Profile.py b/resources/user/Profile.py index 0e63cc8..e93d35c 100644 --- a/resources/user/Profile.py +++ b/resources/user/Profile.py @@ -13,8 +13,7 @@ def delete(self): """ Delete current user's profile """ current_user_username = get_jwt_identity() - # status = DbHandler.update_user_publicity(current_user_username, - # publicity) + status = DbHandler.delete_user_profile(current_user_username) if status == 'OK': return make_response( jsonify(msg="User's profile deleted."), diff --git a/src/DbHandler.py b/src/DbHandler.py index b52d785..3fcb5a2 100644 --- a/src/DbHandler.py +++ b/src/DbHandler.py @@ -377,3 +377,11 @@ def get_user_publicity(user_id: int) -> bool: """ Returns user's publicity. """ return(User.query.with_entities(User.is_public). filter_by(id=user_id).first()[0]) + + @staticmethod + def delete_user_profile(username: str) -> str: + """ Delete user's profile. """ + user_object = User.query.filter_by(username=username).first() + db.session.delete(user_object) + db.session.commit() + return "OK" From 168d2198a518768c783cc670c711733934eeb534 Mon Sep 17 00:00:00 2001 From: Farbod Date: Mon, 1 Feb 2021 11:36:03 +0330 Subject: [PATCH 3/4] Swagger: Updated for deleting user's profile --- resources/user/Profile.py | 4 ++-- yml/user_profile_delete.yml | 43 +++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 yml/user_profile_delete.yml diff --git a/resources/user/Profile.py b/resources/user/Profile.py index e93d35c..d5115e8 100644 --- a/resources/user/Profile.py +++ b/resources/user/Profile.py @@ -1,14 +1,14 @@ from flask_restful import Resource from flask import jsonify, make_response from flask_jwt_extended import jwt_required, get_jwt_identity -# from flasgger import swag_from +from flasgger import swag_from from src.DbHandler import DbHandler class Profile(Resource): @jwt_required - # @swag_from('../../yml/publicity_put.yml') + @swag_from('../../yml/user_profile_delete.yml') def delete(self): """ Delete current user's profile """ current_user_username = get_jwt_identity() diff --git a/yml/user_profile_delete.yml b/yml/user_profile_delete.yml new file mode 100644 index 0000000..e99f327 --- /dev/null +++ b/yml/user_profile_delete.yml @@ -0,0 +1,43 @@ +Deletes profile of the owner of current token, including the links. +--- +openapi: 3.0.0 +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT +security: + - bearerAuth: [] +consumes: + "application/json" +tags: + - name: "User" +parameters: + - in: header + name: Authorization + description: "Send like this: Bearer [TOKEN]" + type: string + required: true + +responses: + 200: + description: "User's profile deleted." + schema: + type: object + properties: + msg: + type: string + description: "User's profile deleted." + example: + msg: "User's profile deleted." + 500: + description: "Server Error!" + schema: + type: object + properties: + msg: + type: string + description: "Server Error!" + example: + msg: "Server Error!" From 4338809bb379f160f85ff3543db665736d5cc229 Mon Sep 17 00:00:00 2001 From: Farbod Date: Mon, 1 Feb 2021 12:20:18 +0330 Subject: [PATCH 4/4] Tests: Added for deleting user's profile --- tests/test_forget_password.py | 19 +++++++++++++++++++ tests/test_user.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/tests/test_forget_password.py b/tests/test_forget_password.py index 5abe753..548fbe6 100644 --- a/tests/test_forget_password.py +++ b/tests/test_forget_password.py @@ -20,6 +20,8 @@ USER_VERIFY_CODE = CONFIG.get('routes', {}).get('user', {}).get('verify_code') USER_RESET_PASSWORD = CONFIG.get('routes', {}).get('user', {}).get( 'reset_password') +USER_PROFILE_ROUTE = CONFIG.get('routes', {}).get('user', {}).get( + 'profile') def generate_random_string(length): @@ -235,4 +237,21 @@ def test_login_with_new_password_accepted(client): headers=HEADERS ) + global TOKEN + TOKEN = json.loads(res.get_data(as_text=True))["access_token"] + assert res.status_code == 200 + + +def test_delete_user_profile_accepted(client): + """curl -i -H "Content-Type: application/json" -H "Authorization: Bearer $x" + -X DELETE localhost:5000/user/profile + """ + headers = { + 'Authorization': f"Bearer {TOKEN}" + } + res = client.delete( + USER_PROFILE_ROUTE, + headers=headers + ) + assert res.status_code == 200 diff --git a/tests/test_user.py b/tests/test_user.py index 0bb73b7..304f6d5 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -19,6 +19,8 @@ 'publicity') USER_ACTIVITY_ROUTE = CONFIG.get('routes', {}).get('user', {}).get( 'activity_list') +USER_PROFILE_ROUTE = CONFIG.get('routes', {}).get('user', {}).get( + 'profile') LINKS_MAIN_ROUTE = CONFIG.get('routes', {}).get('links', {}).get('main') CATEGORIES_MAIN_ROUTE = CONFIG.get( 'routes', {}).get('categories', {}).get('main') @@ -941,3 +943,31 @@ def test_update_categories_of_a_link_rejected(client): data=json.dumps(data) ) assert res.status_code == 404 + + +def test_delete_user_profile_accepted(client): + """curl -i -H "Content-Type: application/json" -H "Authorization: Bearer $x" + -X DELETE localhost:5000/user/profile + """ + headers = { + 'Authorization': f"Bearer {TOKEN}" + } + res = client.delete( + USER_PROFILE_ROUTE, + headers=headers + ) + assert res.status_code == 200 + + +def test_login_deleted_profile_failed(client): + """ User was deleted on previous call """ + data = { + "username": USERNAME, + "password": PASSWORD + } + res = client.post( + USER_LOGIN_ROUTE, + data=json.dumps(data), + headers=HEADERS + ) + assert res.status_code == 401