From 9dfafe75e23c96326f954bcfa31c6e5ff0b425d7 Mon Sep 17 00:00:00 2001 From: xiomara Date: Fri, 30 Aug 2024 01:34:05 -0400 Subject: [PATCH] feat: create user service to upgrade user to artist account and add patch endpoint --- .../user/artist/artist_repository.py | 36 +++++++++++ .../user/user/user_repository.py | 31 +++++++++- .../user/user/user_service.py | 53 ++++++++++++++++ .../spotify_electron/user/user_controller.py | 47 ++++++++++++++ Backend/tests/test_API/api_test_user.py | 4 ++ Backend/tests/test__user.py | 61 ++++++++++++++++++- 6 files changed, 230 insertions(+), 2 deletions(-) diff --git a/Backend/app/spotify_electron/user/artist/artist_repository.py b/Backend/app/spotify_electron/user/artist/artist_repository.py index a07ee605..e2c8602f 100644 --- a/Backend/app/spotify_electron/user/artist/artist_repository.py +++ b/Backend/app/spotify_electron/user/artist/artist_repository.py @@ -11,6 +11,7 @@ ) from app.spotify_electron.user.user.user_schema import ( UserCreateException, + UserDAO, UserNotFoundException, UserRepositoryException, ) @@ -90,6 +91,41 @@ def create_artist(name: str, photo: str, password: bytes, current_date: str) -> artist_repository_logger.info(f"Artist added to repository: {artist}") +def create_artist_from_user(user_data: UserDAO) -> None: + """Create artist based on existing user data + + Args: + user_data: UserDao object + + Raises: + UserRepositoryException: unexpected error while creating artist + """ + try: + artist_data = { + "name": user_data.name, + "photo": user_data.photo, + "register_date": user_data.register_date, + "password": user_data.password, + "saved_playlists": user_data.saved_playlists, + "playlists": user_data.playlists, + "playback_history": user_data.playback_history, + "uploaded_songs": [], + } + result = user_collection_provider.get_artist_collection().insert_one(artist_data) + + validate_user_create(result) + except UserCreateException as exception: + artist_repository_logger.exception(f"Error inserting Artist {artist_data} in database") + raise UserRepositoryException from exception + except (UserRepositoryException, Exception) as exception: + artist_repository_logger.exception( + f"Unexpected error inserting artist {artist_data} in database" + ) + raise UserRepositoryException from exception + else: + artist_repository_logger.info(f"Artist added to repository: {artist_data}") + + def get_all_artists() -> list[ArtistDAO]: """Get all artists diff --git a/Backend/app/spotify_electron/user/user/user_repository.py b/Backend/app/spotify_electron/user/user/user_repository.py index 2b44ff1a..0d8a2825 100644 --- a/Backend/app/spotify_electron/user/user/user_repository.py +++ b/Backend/app/spotify_electron/user/user/user_repository.py @@ -78,7 +78,36 @@ def create_user(name: str, photo: str, password: bytes, current_date: str) -> No user_repository_logger.exception(f"Error inserting User {user} in database") raise UserRepositoryException from exception except (UserRepositoryException, Exception) as exception: - user_repository_logger.exception(f"Unexpected error inserting user {user} in database") + user_repository_logger.exception( + f"Unexpected error inserting user {user} in database" + ) raise UserRepositoryException from exception else: user_repository_logger.info(f"User added to repository: {user}") + + +def update_user_role(user_name: str, new_role: str) -> None: + """Update user role + + Args: + name (str): user name + new_role (str): user role + + Raises: + UserNotFoundException: user was not found + UserRepositoryException: unexpected error while updating user role + """ + try: + user_collection = user_collection_provider.get_user_collection() + user = user_collection.find_one({"name": user_name}) + validate_user_exists(user) + user_collection.update_one({"name": user_name}, {"$set": {"role": new_role}}) + except UserNotFoundException as exception: + raise UserNotFoundException from exception + except Exception as exception: + user_repository_logger.exception( + f"Error updating User {user_name} role in database" + ) + raise UserRepositoryException from exception + else: + user_repository_logger.info(f"User {user_name} role updated to {new_role}") diff --git a/Backend/app/spotify_electron/user/user/user_service.py b/Backend/app/spotify_electron/user/user/user_service.py index 7e2557e7..c3e2ef21 100644 --- a/Backend/app/spotify_electron/user/user/user_service.py +++ b/Backend/app/spotify_electron/user/user/user_service.py @@ -3,10 +3,12 @@ """ import app.auth.auth_service as auth_service +import app.spotify_electron.user.artist.artist_repository as artist_repository import app.spotify_electron.user.base_user_repository as base_user_repository import app.spotify_electron.user.providers.user_collection_provider as user_collection_provider import app.spotify_electron.user.user.user_repository as user_repository import app.spotify_electron.user.validations.base_user_service_validations as base_user_service +from app.auth.auth_schema import TokenData from app.logging.logging_constants import LOGGING_USER_SERVICE from app.logging.logging_schema import SpotifyElectronLogger from app.spotify_electron.user.user.user_schema import ( @@ -18,6 +20,9 @@ UserServiceException, get_user_dto_from_dao, ) +from app.spotify_electron.user.validations.base_user_repository_validations import ( + validate_user_exists, +) from app.spotify_electron.utils.date.date_utils import get_current_iso8601_date user_service_logger = SpotifyElectronLogger(LOGGING_USER_SERVICE).getLogger() @@ -187,3 +192,51 @@ def search_by_name(name: str) -> list[UserDTO]: f"Unexpected error in User Service getting items by name {name}" ) raise UserServiceException from exception + + +# TODO wrap within DB transaction to be treated as atomic operation +def upgrade_user_to_artist(user_name: str, token: TokenData) -> None: + """Upgrade user account to artist account + + Args: + user_name (str): user name + token (TokenData): token data from user + + Raises: + UserNotFoundException: if the user does not exist + UserServiceException: unexpected error while upgrading user to artist + UserBadNameException: if the user name is invalid + """ + try: + base_user_service.validate_user_name_parameter(user_name) + validate_user_exists(user_name) + auth_service.validate_jwt_user_matches_user(token, user_name) + user_data = user_repository.get_user(user_name) + artist_repository.create_artist_from_user(user_data) + user_repository.update_user_role(user_name, "artist") + new_token_data = { + "access_token": user_name, + "role": "artist", + "token_type": "bearer", + } + new_token = auth_service.create_access_token(new_token_data) + + except UserBadNameException as exception: + user_service_logger.exception(f"Bad User Name Parameter: {user_name}") + raise UserBadNameException from exception + except UserNotFoundException as exception: + user_service_logger.exception(f"User not found: {user_name}") + raise UserNotFoundException from exception + except UserRepositoryException as exception: + user_service_logger.exception( + f"Unexpected error in User Repository upgrading user to artist: {user_name}" + ) + raise UserServiceException from exception + except Exception as exception: + user_service_logger.exception( + f"Unexpected error in User Service upgrading user to artist: {user_name}" + ) + raise UserServiceException from exception + else: + user_service_logger.info(f"Account {user_name} upgraded to artist successfully") + return new_token diff --git a/Backend/app/spotify_electron/user/user_controller.py b/Backend/app/spotify_electron/user/user_controller.py index 5da6c3a1..3c8f252f 100644 --- a/Backend/app/spotify_electron/user/user_controller.py +++ b/Backend/app/spotify_electron/user/user_controller.py @@ -165,6 +165,53 @@ def delete_user(name: str) -> Response: ) +@router.patch("/{name}/upgrade_to_artist") +def upgrade_to_artist( + name: str, token: Annotated[TokenData, Depends(JWTBearer())] +) -> Response: + """Upgrade user account to artist account + + Args: + name (str): user name + token (TokenData): the jwt token. Defaults to None. + """ + try: + new_token = user_service.upgrade_user_to_artist(name, token) + response_data = {"token": new_token} + response_json = json_converter_utils.get_json_from_model(response_data) + return Response( + content=response_json, + media_type="application/json", + status_code=HTTP_200_OK, + ) + except UserBadNameException: + return Response( + status_code=HTTP_400_BAD_REQUEST, + content=PropertiesMessagesManager.userBadName, + ) + except UserNotFoundException: + return Response( + status_code=HTTP_404_NOT_FOUND, + content=PropertiesMessagesManager.userNotFound, + ) + except UserUnauthorizedException: + return Response( + status_code=HTTP_403_FORBIDDEN, + content=PropertiesMessagesManager.userUnauthorized, + ) + except BadJWTTokenProvidedException: + return Response( + status_code=HTTP_403_FORBIDDEN, + content=PropertiesMessagesManager.tokenInvalidCredentials, + headers={"WWW-Authenticate": "Bearer"}, + ) + except (Exception, UserServiceException): + return Response( + status_code=HTTP_500_INTERNAL_SERVER_ERROR, + content=PropertiesMessagesManager.commonInternalServerError, + ) + + @router.patch("/{name}/playback_history") def patch_playback_history( name: str, song_name: str, token: Annotated[TokenData, Depends(JWTBearer())] diff --git a/Backend/tests/test_API/api_test_user.py b/Backend/tests/test_API/api_test_user.py index 2996e077..38726796 100644 --- a/Backend/tests/test_API/api_test_user.py +++ b/Backend/tests/test_API/api_test_user.py @@ -43,3 +43,7 @@ def delete_user(name: str) -> Response: def patch_history_playback(user_name: str, song_name: str) -> Response: return client.patch(f"/users/{user_name}/playback_history/?song_name={song_name}") + + +def upgrade_to_artist(user_name: str, headers=dict[str, str]) -> Response: + return client.patch(f"/users/{user_name}/upgrade_to_artist", headers=headers) diff --git a/Backend/tests/test__user.py b/Backend/tests/test__user.py index 3de8a1c8..bec08a8e 100644 --- a/Backend/tests/test__user.py +++ b/Backend/tests/test__user.py @@ -14,7 +14,13 @@ import app.spotify_electron.user.base_user_service as base_user_service import app.spotify_electron.user.user.user_service as user_service from app.auth.auth_schema import VerifyPasswordException -from tests.test_API.api_test_user import create_user, delete_user, get_user +from tests.test_API.api_test_artist import get_artist +from tests.test_API.api_test_user import ( + create_user, + delete_user, + get_user, + upgrade_to_artist, +) from tests.test_API.api_token import get_user_jwt_header @@ -124,6 +130,59 @@ def test_check_encrypted_password_different(): base_user_service.delete_user(name) +def test_upgrade_user_to_artist_correct(clear_test_data_db): + name = "8232392323623823723" + photo = "https://photo" + password = "hola" + + res_create_user = create_user(name=name, password=password, photo=photo) + assert res_create_user.status_code == HTTP_201_CREATED + + jwt_headers = get_user_jwt_header(username=name, password=password) + + res_get_user = get_user(name=name, headers=jwt_headers) + assert res_get_user.status_code == HTTP_200_OK + assert res_get_user.json()["name"] == name + assert res_get_user.json()["photo"] == photo + + res_upgrade_user = upgrade_to_artist(user_name=name, headers=jwt_headers) + assert res_upgrade_user.status_code == HTTP_200_OK + + new_token_data = res_upgrade_user.json() + assert "token" in new_token_data + new_token = new_token_data["token"] + + decoded_token = auth_service.get_jwt_token_data(new_token) + assert decoded_token.role == "artist" + + res_get_artist = get_artist(name=name, headers=jwt_headers) + assert res_get_artist.status_code == HTTP_200_OK + artist_data = res_get_artist.json() + assert artist_data["name"] == name + assert artist_data["photo"] == photo + + base_user_service.delete_user(name) + + +def test_upgrade_to_artist_user_not_found(): + name = "8232392323623823723" + photo = "https://photo" + password = "hola" + + res_create_user = create_user(name=name, password=password, photo=photo) + assert res_create_user.status_code == HTTP_201_CREATED + + jwt_headers = get_user_jwt_header(username=name, password=password) + res_get_user = get_user(name=name, headers=jwt_headers) + assert res_get_user.status_code == HTTP_200_OK + assert res_get_user.json()["name"] == name + assert res_get_user.json()["photo"] == photo + base_user_service.delete_user(name) + + res_upgrade_user = upgrade_to_artist(user_name=name, headers=jwt_headers) + assert res_upgrade_user.status_code == HTTP_404_NOT_FOUND + + # executes after all tests @pytest.fixture() def clear_test_data_db():