Skip to content

Commit

Permalink
Feat: issue#76 POST and GET /user/additional_info
Browse files Browse the repository at this point in the history
  • Loading branch information
mtreacy002 committed Jul 14, 2020
1 parent 5421677 commit b4dfee5
Show file tree
Hide file tree
Showing 11 changed files with 418 additions and 71 deletions.
51 changes: 40 additions & 11 deletions app/api/dao/user_extension.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
from http import HTTPStatus
# from typing import Dict
# from sqlalchemy import func
from flask import json
from app.database.models.bit_schema.user_extension import UserExtensionModel
from app import messages
from app.api.request_api_utils import AUTH_COOKIE


class UserExtensionDAO:

"""Data Access Object for Users_Extension functionalities"""

@staticmethod
def create_user_extension(data):
def create_user_additional_info(data):
"""Creates a user_extension instance for a new registered user.
Arguments:
Expand All @@ -21,17 +21,46 @@ def create_user_extension(data):
A dictionary containing "message" which indicates whether or not the user_exension was created successfully and "code" for the HTTP response code.
"""

user_id = data["user_id"]
is_organization_rep = data["is_organization_rep"]
user_id = AUTH_COOKIE["user_id"].value
timezone = data["timezone"]

additional_info_data = {}

existing_additional_info = UserExtensionModel.find_by_user_id(user_id)
if existing_additional_info:
return messages.ADDITIONAL_INFORMATION_OF_USER_ALREADY_EXIST, HTTPStatus.CONFLICT
user_extension = UserExtensionModel(user_id, timezone)
try:
user_extension.is_organization_rep = data["is_organization_rep"]
additional_info_data["phone"] = data["phone"]
additional_info_data["mobile"] = data["mobile"]
additional_info_data["personal_website"] = data["personal_website"]
except KeyError as e:
return e, HTTPStatus.BAD_REQUEST

user_extension = UserExtensionModel(user_id, is_organization_rep, timezone)
user_extension.additional_info = additional_info_data

user_extension.save_to_db()

response = {
"message": f"{messages.USER_WAS_CREATED_SUCCESSFULLY}",
"code": f"{HTTPStatus.CREATED}",
}
return messages.ADDITIONAL_INFO_SUCCESSFULLY_CREATED, HTTPStatus.CREATED

@staticmethod
def get_user_additional_data_info(user_id):
"""Retrieves a user's additional information using a specified ID.
Arguments:
user_id: The ID of the user to be searched.
Returns:
The UserModel class of the user whose ID was searched, containing their additional information.
"""

return response
result = UserExtensionModel.find_by_user_id(user_id)
return {
"user_id": result.user_id,
"is_organization_rep": result.is_organization_rep,
"timezone": result.timezone.name,
"phone": result.additional_info["phone"],
"mobile": result.additional_info["mobile"],
"personal_website": result.additional_info["personal_website"]
}
43 changes: 40 additions & 3 deletions app/api/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ def add_models_to_namespace(api_namespace):
api_namespace.models[login_request_body_model.name] = login_request_body_model
api_namespace.models[login_response_body_model.name] = login_response_body_model
api_namespace.models[full_user_api_model.name] = full_user_api_model
api_namespace.models[update_user_request_body_model.name] = update_user_request_body_model
api_namespace.models[update_user_details_request_body_model.name] = update_user_details_request_body_model
api_namespace.models[get_user_extension_response_model.name] = get_user_extension_response_model
api_namespace.models[create_user_extension_request_body_model.name] = create_user_extension_request_body_model
api_namespace.models[update_user_extension_request_body_model.name] = update_user_extension_request_body_model

register_user_api_model = Model(
"User registration model",
Expand Down Expand Up @@ -97,8 +100,8 @@ def add_models_to_namespace(api_namespace):
},
)

update_user_request_body_model = Model(
"Update User request data model",
update_user_details_request_body_model = Model(
"Update User details request data model",
{
"name": fields.String(required=False, description="User name"),
"username": fields.String(required=False, description="User username"),
Expand Down Expand Up @@ -126,3 +129,37 @@ def add_models_to_namespace(api_namespace):
),
},
)

get_user_extension_response_model = Model(
"Retrieve additional information response data model",
{
"user_id": fields.Integer(required=True, description="User Id"),
"is_organization_rep": fields.Boolean(required=True, description="User represents organization"),
"timezone": fields.String(required=True, description="User's timezone"),
"phone": fields.String(required=False, description="phone"),
"mobile": fields.String(required=False, description="mobile"),
"personal_website": fields.String(required=False, description="personal_website"),
}
)

create_user_extension_request_body_model = Model(
"Create additional information response data model",
{
"is_organization_rep": fields.Boolean(required=True, description="User represents organization"),
"timezone": fields.String(required=True, description="User's timezone"),
"phone": fields.String(required=False, description="phone"),
"mobile": fields.String(required=False, description="mobile"),
"personal_website": fields.String(required=False, description="personal_website"),
}
)

update_user_extension_request_body_model = Model(
"Update user's additional information request data model",
{
"is_organization_rep": fields.Boolean(required=True, description="User represents organization"),
"timezone": fields.String(required=True, description="User's timezone"),
"phone": fields.String(required=False, description="phone"),
"mobile": fields.String(required=False, description="mobile"),
"personal_website": fields.String(required=False, description="personal_website"),
}
)
1 change: 0 additions & 1 deletion app/api/request_api_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ def validate_token(token):
return messages.TOKEN_IS_INVALID, HTTPStatus.UNAUTHORIZED
if datetime.utcnow().timestamp() > AUTH_COOKIE["Authorization"]["expires"]:
return messages.TOKEN_HAS_EXPIRED, HTTPStatus.UNAUTHORIZED
return ()


@http_response_namedtuple_converter
Expand Down
167 changes: 124 additions & 43 deletions app/api/resources/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,25 @@
get_jwt_identity,
)
from flask_restx import Resource, marshal, Namespace
from app.api.request_api_utils import post_request, get_request, put_request, http_response_checker, AUTH_COOKIE
from app import messages
from app.api.models.user import *
from app.api.request_api_utils import (
post_request,
get_request,
put_request,
http_response_checker,
AUTH_COOKIE,
validate_token)
from app.api.validations.user import *
from app.utils.validation_utils import expected_fields_validator
from app.api.resources.common import auth_header_parser
from app.api.models.user import *
from app.api.dao.user_extension import UserExtensionDAO
from app.utils.validation_utils import expected_fields_validator
from app.database.models.bit_schema.user_extension import UserExtensionModel

users_ns = Namespace("Users", description="Operations related to users")
add_models_to_namespace(users_ns)

UserExtensionDAO = UserExtensionDAO()

@users_ns.route("register")
class UserRegister(Resource):
Expand Down Expand Up @@ -57,7 +66,7 @@ def post(cls):
The endpoint accepts user input related to Mentorship System API (name, username, password, email,
terms_and_conditions_checked(true/false), need_mentoring(true/false),
available_to_mentor(true/false)) and Bridge In Tech API (is_organization_rep and timezone).
available_to_mentor(true/false)).
A success message is displayed and verification email is sent to the user's email ID.
"""

Expand All @@ -71,9 +80,7 @@ def post(cls):
if is_not_valid:
return is_not_valid, HTTPStatus.BAD_REQUEST

result = post_request("/register", data)

return http_response_checker(result)
return http_response_checker(post_request("/register", data))


@users_ns.route("login")
Expand Down Expand Up @@ -115,69 +122,56 @@ def post(cls):
if not password:
return messages.PASSWORD_FIELD_IS_MISSING, HTTPStatus.BAD_REQUEST

result = post_request("/login", data)

return http_response_checker(result)
return http_response_checker(post_request("/login", data))


@users_ns.route("user/personal_details")
class MyProfilePersonalDetails(Resource):
@classmethod
@users_ns.doc("get_user")
@users_ns.response(HTTPStatus.OK, "Successful request", full_user_api_model)
@users_ns.response(
@users_ns.response(
HTTPStatus.UNAUTHORIZED,
f"{messages.TOKEN_HAS_EXPIRED}\n"
f"{messages.TOKEN_IS_INVALID}\n"
f"{messages.AUTHORISATION_TOKEN_IS_MISSING}"
)
@users_ns.response(HTTPStatus.NOT_FOUND, f"{messages.USER_DOES_NOT_EXIST}")
@users_ns.response(HTTPStatus.INTERNAL_SERVER_ERROR, f"{messages.INTERNAL_SERVER_ERROR}")
)
@users_ns.response(HTTPStatus.NOT_FOUND, f"{messages.USER_DOES_NOT_EXIST}")
@users_ns.response(HTTPStatus.INTERNAL_SERVER_ERROR, f"{messages.INTERNAL_SERVER_ERROR}")
@users_ns.route("user/personal_details")
class MyProfilePersonalDetails(Resource):
@classmethod
@users_ns.doc("get_user_personal_details")
@users_ns.response(HTTPStatus.OK, "Successful request", full_user_api_model)
@users_ns.expect(auth_header_parser, validate=True)
def get(cls):
"""
Returns details of current user.
Returns personal details of current user.
A user with valid access token can use this endpoint to view his/her own
A user with valid access token can use this endpoint to view their own
user details. The endpoint doesn't take any other input.
"""
token = request.headers.environ["HTTP_AUTHORIZATION"]

result = get_request("/user", token)

return http_response_checker(result)

return http_response_checker(get_request("/user", token))


@classmethod
@users_ns.doc("update_user_profile")
@users_ns.doc("update_user_personal_details")
@users_ns.response(HTTPStatus.OK, f"{messages.USER_SUCCESSFULLY_UPDATED}")
@users_ns.response(HTTPStatus.BAD_REQUEST, "Invalid input.")
@users_ns.response(
HTTPStatus.UNAUTHORIZED,
f"{messages.TOKEN_HAS_EXPIRED}\n"
f"{messages.TOKEN_IS_INVALID}\n"
f"{messages.AUTHORISATION_TOKEN_IS_MISSING}"
)
@users_ns.response(HTTPStatus.NOT_FOUND, f"{messages.USER_DOES_NOT_EXIST}")
@users_ns.response(HTTPStatus.INTERNAL_SERVER_ERROR, f"{messages.INTERNAL_SERVER_ERROR}")
@users_ns.expect(auth_header_parser, update_user_request_body_model, validate=True,)
@users_ns.expect(auth_header_parser, update_user_details_request_body_model, validate=True,)
def put(cls):
"""
Updates user profile
Updates user personal details
A user with valid access token can use this endpoint to edit his/her own
A user with valid access token can use this endpoint to edit their own
user details. The endpoint takes any of the given parameters (name, username,
bio, location, occupation, organization, slack_username, social_media_links,
skills, interests, resume_url, photo_url, need_mentoring, available_to_mentor).
The response contains a success message.
The response contains a success or error message.
"""

data = request.json

if not data:
return messages.NO_DATA_FOR_UPDATING_PROFILE_WAS_SENT

is_field_valid = expected_fields_validator(data, update_user_request_body_model)
is_field_valid = expected_fields_validator(data, update_user_details_request_body_model)
if not is_field_valid.get("is_field_valid"):
return is_field_valid.get("message"), HTTPStatus.BAD_REQUEST

Expand All @@ -187,8 +181,95 @@ def put(cls):

token = request.headers.environ["HTTP_AUTHORIZATION"]

result = put_request("/user", token, data)
return http_response_checker(result)
return http_response_checker(put_request("/user", token, data))



@users_ns.response(
HTTPStatus.UNAUTHORIZED,
f"{messages.TOKEN_HAS_EXPIRED}\n"
f"{messages.TOKEN_IS_INVALID}\n"
f"{messages.AUTHORISATION_TOKEN_IS_MISSING}"
)
@users_ns.response(HTTPStatus.INTERNAL_SERVER_ERROR, f"{messages.INTERNAL_SERVER_ERROR}")
@users_ns.route("user/additional_info")
class MyProfileAdditionalInfo(Resource):
@classmethod
@users_ns.doc("get_user_additional_info")
@users_ns.response(HTTPStatus.OK, "Successful request", get_user_extension_response_model)
@users_ns.response(HTTPStatus.BAD_REQUEST, "Invalid input.")
@users_ns.response(HTTPStatus.NOT_FOUND, f"{messages.ADDITIONAL_INFORMATION_DOES_NOT_EXIST}")
@users_ns.expect(auth_header_parser, validate=True)
def get(cls):
"""
Returns additional information of current user
A user with valid access token can use this endpoint to view their additional information details.
The endpoint doesn't take any other input. But the user must get their personal details first
before they can send this request for getting the additional information.
"""

token = request.headers.environ["HTTP_AUTHORIZATION"]

is_wrong_token = validate_token(token)

if not is_wrong_token:
result = UserExtensionDAO.get_user_additional_data_info(AUTH_COOKIE["user_id"].value);
if not result:
return messages.ADDITIONAL_INFORMATION_DOES_NOT_EXIST, HTTPStatus.NOT_FOUND
return result
return is_wrong_token


@classmethod
@users_ns.doc("create_user_additional_info")
@users_ns.doc(
responses={
HTTPStatus.INTERNAL_SERVER_ERROR: f"{messages.INTERNAL_SERVER_ERROR['message']}"
}
)
@users_ns.response(
HTTPStatus.CREATED, f"{messages.ADDITIONAL_INFO_SUCCESSFULLY_CREATED}"
)
@users_ns.response(
HTTPStatus.BAD_REQUEST,
f"{messages.USER_ID_IS_NOT_VALID}\n"
f"{messages.IS_ORGANIZATION_REP_FIELD_IS_MISSING}\n"
f"{messages.TIMEZONE_FIELD_IS_MISSING}"
f"{messages.UNEXPECTED_INPUT}"
)
@users_ns.response(
HTTPStatus.INTERNAL_SERVER_ERROR, f"{messages.INTERNAL_SERVER_ERROR}"
)
@users_ns.expect(auth_header_parser, create_user_extension_request_body_model, validate=True)
def post(cls):
"""
Creates user additional information
A user with valid access token can use this endpoint to add additional information to their own data.
The endpoint takes any of the given parameters (is_organization_rep (true or false value), timezone
(with value as per Timezine Enum Name) and additional_info (dictionary of phone, mobile and personal_website)).
The response contains a success or error message. This request only accessible once user confirm
their additional information have not already exist in the data through sending GET request for
additional information.
"""

token = request.headers.environ["HTTP_AUTHORIZATION"]
is_wrong_token = validate_token(token)

if not is_wrong_token:
data = request.json
if not data:
return messages.NO_DATA_FOR_UPDATING_PROFILE_WAS_SENT

is_field_valid = expected_fields_validator(data, update_user_extension_request_body_model)
if not is_field_valid.get("is_field_valid"):
return is_field_valid.get("message"), HTTPStatus.BAD_REQUEST

is_not_valid = validate_update_additional_info_request(data)
if is_not_valid:
return is_not_valid, HTTPStatus.BAD_REQUEST

return UserExtensionDAO.create_user_additional_info(data);

return is_wrong_token

Loading

0 comments on commit b4dfee5

Please sign in to comment.