diff --git a/src/api/__init__.py b/src/api/__init__.py index 10ff23f..e8628ce 100644 --- a/src/api/__init__.py +++ b/src/api/__init__.py @@ -15,3 +15,4 @@ import api.models import api.endpoints import api.views +import api.utils diff --git a/src/api/controllers/userController.py b/src/api/controllers/userController.py index ee6aebc..3405c21 100644 --- a/src/api/controllers/userController.py +++ b/src/api/controllers/userController.py @@ -1,5 +1,7 @@ from api.models import db, User, UserHasProject, UserLink, UserFeedback from api import app +from flask_jwt_extended import get_jwt_identity +from flask import jsonify class UserController: session = db.session() @@ -11,45 +13,35 @@ def create_user(self, **kwargs): self.session.add(user) self.session.commit() - return user + return user, "OK", 200 except: self.session.rollback() - return None + return None, "Forbidden Attributes", 400 def update_user(self, id, **kwargs): user = User.query.filter_by(id=id).first() if user == None: - return None + return None, "user not found", 404 for key, value in kwargs.items(): if not hasattr(user, key): - return None - - for key, value in kwargs.items(): - setattr(user, key, value) - - db.session.commit() - - return user - - def update_user(self, id, **kwargs): - user = User.query.filter_by(id=id).first() - - if user == None: - return user + return None, "forbidden attribute", 400 for key, value in kwargs.items(): setattr(user, key, value) db.session.commit() - return user + return user, "OK", 200 def get_user(self, **kwargs): user = User.query.filter_by(**kwargs).first() - return user + if user is None: + return None, "User Not Found", 404 + + return user, "OK", 200 def get_all_users(self, **kwargs): all_users = User.query.all() @@ -68,13 +60,12 @@ def delete_user(self, id): user = User.query.filter_by(id=id).first() if user == None: - return None + return None, "user not found", 404 db.session.delete(user) db.session.commit() - return user - + return user, "OK", 200 # User Link def create_link(self, user_id, **kwargs): try: @@ -148,4 +139,7 @@ def delete_feedback(self, user_id, feedback_id): return feedback + def get_user_from_jwt(self): + return self.get_user(id=get_jwt_identity()) + userController = UserController() diff --git a/src/api/utils.py b/src/api/utils.py new file mode 100644 index 0000000..c81853c --- /dev/null +++ b/src/api/utils.py @@ -0,0 +1,8 @@ +from flask import jsonify + +def wrap_response(data, msg, code): + obj = {"msg": msg} + if not data is None: + obj["data"] = data.as_dict() + return jsonify(obj), code + \ No newline at end of file diff --git a/src/api/views/oauthView.py b/src/api/views/oauthView.py index dd2857c..d6c89c5 100644 --- a/src/api/views/oauthView.py +++ b/src/api/views/oauthView.py @@ -62,11 +62,13 @@ def login_callback(blueprint): if blueprint.name == "github": resp = github.get("/user").json() id = resp["id"] - user = userController.get_user(github_id=id) + user, msg, code = userController.get_user(github_id=id) redirect_token = f"?state={session.pop('state', '{}')}" if user: access_token = create_access_token(identity=user) redirect_token += f"&token={access_token}" + else: + redirect_token += f"&msg={msg}&code={code}" return redirect(session.pop("redirect") + redirect_token) @@ -74,17 +76,16 @@ def register_callback(blueprint): if blueprint.name == "github": resp = github.get("/user").json() id = resp["id"] - user = userController.get_user(github_id=id) - if not user: - user = userController.create_user(github_id=id, name=session.pop('username', "Anton")) + user, msg, code = userController.get_user(github_id=id) + redirect_token = f"?state={session.pop('state')}" + if user is None: + user, msg, code = userController.create_user(github_id=id, name=session.pop('username', "Anton")) + + if user is None: + redirect_token += f"&msg={msg}&code={code}" + else: access_token = create_access_token(identity=user) - redirect_token = f"?state={session.pop('state')}&token={access_token}" + redirect_token += f"&token={access_token}" + return redirect(session.pop('redirect') + redirect_token) -# Actually deprecated -# should be /user in userview but I'll leave it until Routes branch adds and merges it -@app.route("/getcurrentuser", methods=["GET"]) -@jwt_required -def getCurrentUser(): - current_user = userController.get_user(id=get_jwt_identity()) - return jsonify(current_user.as_dict()), 200 diff --git a/src/api/views/userView.py b/src/api/views/userView.py index db662af..08f3abf 100644 --- a/src/api/views/userView.py +++ b/src/api/views/userView.py @@ -1,148 +1,76 @@ from flask import request, jsonify, session, Flask, redirect, session, url_for +from flask_jwt_extended import get_jwt_identity, jwt_required from api import app +from api.utils import wrap_response from api.controllers import userController from os import environ # User -@app.route("/users", methods=['POST']) -def create_user(): - """ - Create user - --- - tags: - - User - parameters: - - in: body - name: User - required: true - description: User object containing data for creation - schema: - $ref: "#/definitions/User" - definitions: - - schema: - id: User - properties: - id: - type: integer - description: Id of the user. This property will be assigned a value returned by the database - name: - type: string - description: Name of the user - bio: - type: string - description: Biography of the user - languages: - type: string - description: List of programming languages the user uses - interests: - type: string - description: Interests of the user - location: - type: string - description: Location of the user - occupation: - type: string - description: Formal occupation, eg. student at X or works at Y - projects: - type: array - description: List of projects - items: - $ref: "#/definitions/Project" - links: - type: array - description: List of links - items: - $ref: "#/definitions/UserLink" - project_feedbacks: - type: array - description: List of feedbacks given to projects - items: - $ref: "#/definitions/ProjectFeedback" - user_feedbacks: - type: array - description: List of feedbacks given to users - items: - $ref: "#/definitions/UserFeedback" - received_feedbacks: - type: array - description: List of received feedbacks from users - items: - $ref: "#/definitions/UserFeedback" - responses: - 201: - description: User created successfully - 400: - description: Failed to create user - """ - user = userController.create_user(**request.get_json()) - - if user == None: - return "Failed to create user.", 400 - else: - return jsonify(user.as_dict()), 201 - -@app.route("/users/", methods=['PUT']) -def update_user(id): +@app.route("/user", methods=['PUT']) +@jwt_required +def update_user(): """ Update user - Updates user with `id` using the data in request body + Updates authenticated user with the data in request body --- tags: - User parameters: - - in: path - name: id - type: integer - required: true - description: Id of user to update - in: body name: User required: true - description: User object containing data to update + description: User attributes to update. Any combination of attributes is valid schema: - $ref: "#/definitions/User" + id: UserUpdate + properties: + name: + type: string + description: (Optional) Name of the user + bio: + type: string + description: (Optional) Biography of the user + languages: + type: string + description: (Optional) List of programming languages the user uses + interests: + type: string + description: (Optional) Interests of the user + location: + type: string + description: (Optional) Location of the user + occupation: + type: string + description: (Optional) Formal occupation, eg. student at X or works at Y responses: 200: description: User updated successfully 400: - description: Failed to update user + description: Bad Request. Forbidden Parameters used + 404: + description: User the token belonged to doesn't exist anymore """ - if 'id' in request.get_json(): - return "Failed to update user. Request body can not specify user's id.", 501 - user = userController.update_user(id, **request.get_json()) + if 'id' in request.get_json(): + return wrap_response(None, "Failed to update user. Request body can not specify user's id.", 400) - if user == None: - return "Failed to update user.", 400 - else: - return jsonify(user.as_dict()), 200 + return wrap_response(*userController.update_user(get_jwt_identity(), **request.get_json())) -@app.route("/users/", methods=['GET']) -def get_user(id): +@app.route("/user", methods=['GET']) +@jwt_required +def get_user(): """ Get user - Retreives user with `id` + Retreives authenticated user --- tags: - User - parameters: - - in: path - name: id - type: integer - required: true - description: Id of the user to retrieve responses: 200: description: User object 404: - description: User not found + description: User the token belonged to doesn't exist anymore """ - user = userController.get_user(id=id) - - if user: - return jsonify(user.as_dict()), 200 - else: - return "", 404 + return wrap_response(*userController.get_user_from_jwt()) @app.route("/users", methods=['GET']) def get_all_users(): @@ -162,37 +90,23 @@ def get_all_users(): return jsonify(users), 200 -@app.route("/users/", methods=['DELETE']) -def delete_user(id): +@app.route("/user", methods=['DELETE']) +@jwt_required +def delete_user(): """ Delete user - Deletes user with `id` + Deletes authenticated user --- tags: - User - parameters: - - in: path - name: id - type: integer - required: true - description: Id of the user to delete responses: 200: description: User deleted successfully - 401: - description: Not allowed to delete the specified user 404: - description: User not found + description: User the token belonged to doesn't exist anymore """ - if int(current_user.id) == int(id): - user = userController.delete_user(id) - - if user: - return "", 200 - else: - return "", 404 - else: - return "You cannot delete an other user", 401 + + return wrap_response(*userController.delete_user(get_jwt_identity())) # User Link @app.route("/users//links", methods=['POST']) diff --git a/tests/api/__init__.py b/tests/api/__init__.py index a15adcc..bd7fb7c 100644 --- a/tests/api/__init__.py +++ b/tests/api/__init__.py @@ -10,7 +10,7 @@ - call these functions form any test case. """ - +from flask_jwt_extended import create_access_token from tests import db, Project, User, UserLink, ProjectLink, UserFeedback def create_project_for_test_cases(data): @@ -20,6 +20,10 @@ def create_project_for_test_cases(data): return new_project.as_dict() +def delete_user_for_test_cases(user): + db.session.delete(user) + db.session.commit() + def create_project_link_for_test_cases(data): new_project_link = ProjectLink(**data) db.session.add(new_project_link) @@ -27,6 +31,12 @@ def create_project_link_for_test_cases(data): return new_project_link.as_dict() +def create_access_token_for_test_cases(data): + new_user = User(**data) + db.session.add(new_user) + db.session.commit() + return create_access_token(identity=new_user), new_user + def create_user_for_test_cases(data): new_user = User(**data) db.session.add(new_user) diff --git a/tests/api/views/test_userView.py b/tests/api/views/test_userView.py index c4df5f9..b82ae93 100644 --- a/tests/api/views/test_userView.py +++ b/tests/api/views/test_userView.py @@ -1,6 +1,7 @@ from tests.conftest import client from tests import db, User, UserLink, UserFeedback from tests.api import create_user_for_test_cases, create_user_link_for_test_cases, create_user_feedback_for_test_cases +from tests.api import delete_user_for_test_cases, create_access_token_for_test_cases class TestUserView(object): @@ -14,47 +15,43 @@ class TestUserView(object): 'occupation': 'cashier' } - def test_create_user(self, client): - response = client.post('/users', json=self.valid_data) - assert response.status_code == 201 - - response = client.post('/users', json={}) - assert response.status_code == 400 def test_update_user(self, client): - user_id = create_user_for_test_cases(self.valid_data)["id"] - - response = client.post('/users/1', json={}) - assert response.status_code == 400 + token, _ = create_access_token_for_test_cases(self.valid_data) - # notice: Should we respond to update_user request without json data with status code 200? - # response = client.post('/users/{}'.format(user_id), json={}) - # assert response.status_code == 400 + #for now we will allow empty body. + response = client.put('/user', headers={"Authorization": f"Bearer {token}"}, json={}) + assert response.status_code == 200 - response = client.post('/users/{}'.format(user_id), json={"name": "Updated Name"}) + response = client.put('/user', headers={"Authorization": f"Bearer {token}"}, json={"name": "Updated Name"}) assert response.status_code == 200 - assert response.get_json()['name'] == "Updated Name" + assert response.get_json()['data']['name'] == "Updated Name" def test_delete_user(self, client): - user_id = None - response = client.delete('/users/{}'.format(user_id)) - assert response.status_code == 404 + token, _ = create_access_token_for_test_cases(self.valid_data) - user_id = create_user_for_test_cases(self.valid_data)["id"] - response = client.delete('/users/{}'.format(user_id)) + response = client.delete('/user', headers={"Authorization": f"Bearer {token}"}) assert response.status_code == 200 - def test_get_user(self, client): - user_id = None - response = client.get('/users/{}'.format(user_id)) + response = client.delete('/user', headers={"Authorization": f"Bearer {token}"}) assert response.status_code == 404 + - user = create_user_for_test_cases(self.valid_data) - user_id = user["id"] - response = client.get('/users/{}'.format(user_id)) + def test_get_user(self, client): + token, user = create_access_token_for_test_cases(self.valid_data) + + response = client.get('/user', headers={"Authorization": f"Bearer {token}"}) assert response.status_code == 200 - assert response.get_json() == user + assert response.get_json() == {"data": user.as_dict(), "msg": "OK"} + + delete_user_for_test_cases(user) + + response = client.get('/user', headers={"Authorization": f"Bearer {token}"}) + assert response.status_code == 404 + + + def test_get_all_users(self, client): create_user_for_test_cases(self.valid_data) diff --git a/tests/conftest.py b/tests/conftest.py index 3c9fffd..5927238 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -25,4 +25,5 @@ def client(): pass with app.test_client() as test_client: - yield test_client + with app.app_context(): + yield test_client