Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#246: Playlist update only introduce fields to change #289

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ repos:
- id: check-merge-conflict

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.3.7
rev: v0.8.5
hooks:
- id: ruff
args: [--fix]
Expand Down
28 changes: 17 additions & 11 deletions Backend/app/spotify_electron/playlist/playlist_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,24 +135,30 @@ def create_playlist(


@router.put("/{name}")
def update_playlist( # noqa: PLR0913
def update_playlist_metadata(
name: str,
photo: str,
description: str,
token: Annotated[TokenData, Depends(JWTBearer())],
song_names: list[str] = Body(...),
new_name: str | None = None,
photo: str | None = None,
description: str | None = None,
token: TokenData = Depends(JWTBearer()),
) -> Response:
"""Update playlist
"""Update playlist metadata

Args:
photo (str): playlist new photo
description (str): playlist new description
song_names (list[str], optional): playlist new song names. Defaults to Body(...).
new_name (str | None, optional): playlist new name. Defaults to None.
name (str): playlist name
new_name (str | None): new playlist name
photo (str | None): new photo URL
description (str | None): new description
token (TokenData): User authentication token
"""
try:
playlist_service.update_playlist(name, new_name, photo, description, song_names, token)
playlist_service.update_playlist_metadata(
token=token,
name=name,
new_name=new_name,
photo=photo,
description=description,
)
return Response(None, HTTP_204_NO_CONTENT)
except BadJWTTokenProvidedException:
return Response(
Expand Down
75 changes: 30 additions & 45 deletions Backend/app/spotify_electron/playlist/playlist_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,67 +272,52 @@ def get_playlist_search_by_name(
return playlists


def update_playlist(
def update_playlist_metadata(
name: str,
new_name: str,
photo: str,
description: str,
song_names: list[str],
new_name: str | None = None,
photo: str | None = None,
description: str | None = None,
) -> None:
"""Update playlist
"""Update only specified fields of a playlist's metadata.

Args:
name (str): playlist name
new_name (str): new name
photo (str): new photo
description (str): new description
song_names (list[str]): new song names
new_name (str | None): new name
photo (str | None): new photo
description (str | None): new description

Raises:
PlaylistRepositoryException: unexpected error updating playlist
"""
update_data = {}
try:
collection = get_playlist_collection()
result_update = collection.update_one(
{"name": name},
{
"$set": {
"name": new_name,
"description": description,
"photo": photo,
"song_names": list(set(song_names)),
}
},
)
validate_playlist_update(result_update)

except PlaylistUpdateException as exception:
if new_name:
update_data["name"] = new_name
if photo:
update_data["photo"] = photo
if description:
update_data["description"] = description

if update_data:
collection = get_playlist_collection()
result_update = collection.update_one({"name": name}, {"$set": update_data})
validate_playlist_update(result_update)
else:
return

except PlaylistUpdateException as e:
playlist_repository_logger.exception(
f"Error updating playlist {name}:\n"
f"new_name = {new_name},\n"
f"photo = {photo},\n"
f"description = {description},\n"
f"song_names = {song_names}"
f"Error during playlist update for: {name}, fields: {update_data}"
)
raise PlaylistRepositoryException from exception

except Exception as exception:
raise PlaylistRepositoryException from e
except Exception as e:
playlist_repository_logger.exception(
f"Unexpected error updating playlist {name}:\n"
f"new_name = {new_name},\n"
f"photo = {photo},\n"
f"description = {description},\n"
f"song_names = {song_names}"
f"Unexpected error during playlist update for: {name}, fields: {update_data}"
)
raise PlaylistRepositoryException from exception

raise PlaylistRepositoryException from e
else:
playlist_repository_logger.info(
f"Playlist {name} updated:\n"
f"new_name = {new_name},\n"
f"photo = {photo},\n"
f"description = {description},\n"
f"song_names = {song_names}"
f"Playlist {name} updated successfully with fields: {update_data}"
)


Expand Down
98 changes: 48 additions & 50 deletions Backend/app/spotify_electron/playlist/playlist_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,77 +160,75 @@ def create_playlist(
playlist_service_logger.info(f"Playlist {name} created successfully")


def update_playlist( # noqa: PLR0913
def update_playlist_metadata( # noqa: C901
name: str,
new_name: str | None,
photo: str,
description: str,
song_names: list[str],
token: TokenData,
new_name: str | None = None,
photo: str | None = None,
description: str | None = None,
) -> None:
"""Updates a playlist
"""
Updates specified fields of a playlist's metadata.

Args:
----
name (str): name
new_name (str | None): new playlist name, optional
photo (str): thumbnail photo
description (str): description
song_names (list): list of song names
token (TokenData): token user info
name (str): The current name of the playlist.
new_name (str | None): The new name of the playlist, if provided.
photo (str | None): The new photo URL for the playlist, if provided.
description (str | None): The new description for the playlist, if provided.
token (TokenData): The token containing user information.

Raises:
------
PlaylistBadNameException: invalid playlist name
PlaylistNotFoundException: playlist doesn't exists
PlaylistServiceException: unexpected error while updating playlist

PlaylistBadNameException: If the playlist name (current or new) is invalid.
PlaylistNotFoundException: If the playlist does not exist.
UserUnauthorizedException: If the user is not authorized to update the playlist.
PlaylistServiceException: For unexpected errors during the update.
"""
try:
validate_playlist_name_parameter(name)
validate_playlist_should_exists(name)
if new_name:
validate_playlist_name_parameter(new_name)

playlist = playlist_repository.get_playlist(name)

auth_service.validate_jwt_user_matches_user(token, playlist.owner)

if not new_name:
playlist_repository.update_playlist(
name, name, photo if "http" in photo else "", description, song_names
)
update_data = {}
if photo and "http" in photo:
update_data["photo"] = photo
if description:
update_data["description"] = description

if not update_data and not new_name:
return

validate_playlist_name_parameter(new_name)
playlist_repository.update_playlist(
name,
new_name,
photo if "http" in photo else "",
description,
song_names,
)
playlist_repository.update_playlist_metadata(name, new_name=new_name, **update_data)
if new_name:
base_user_service.update_playlist_name(name, new_name)

base_user_service.update_playlist_name(name, new_name)
except PlaylistBadNameException as exception:
playlist_service_logger.exception(f"Bad Playlist Name Parameter: {name}")
raise PlaylistBadNameException from exception
except PlaylistNotFoundException as exception:
playlist_service_logger.exception(f"Playlist not found: {name}")
raise PlaylistNotFoundException from exception
except UserUnauthorizedException as exception:
playlist_service_logger.exception(f"User is not the owner of playlist: {name}")
raise UserUnauthorizedException from exception
except PlaylistRepositoryException as exception:
except PlaylistBadNameException:
playlist_service_logger.exception(
f"Unexpected error in Playlist Repository updating playlist: {name}"
f"Invalid playlist name parameter for playlist: {name}"
)
raise PlaylistServiceException from exception
except Exception as exception:
playlist_service_logger.exception(
f"Unexpected error in Playlist Service updating playlist: {name}"
)
raise PlaylistServiceException from exception
raise
except PlaylistNotFoundException:
playlist_service_logger.exception(f"Playlist not found: {name}")
raise
except UserUnauthorizedException:
playlist_service_logger.exception(f"User unauthorized to update playlist: {name}")
raise
except PlaylistRepositoryException as e:
playlist_service_logger.exception(f"Repository error while updating playlist: {name}")
raise PlaylistServiceException from e
except Exception as e:
playlist_service_logger.exception(f"Unexpected error while updating playlist: {name}")
raise PlaylistServiceException from e
else:
playlist_service_logger.info(f"Playlist {name} updated successfully")
updated_fields = update_data.copy()
if new_name:
updated_fields["new_name"] = new_name
playlist_service_logger.info(
f"Playlist {name} updated successfully with fields: {updated_fields}"
)


def delete_playlist(name: str) -> None:
Expand Down
34 changes: 18 additions & 16 deletions Backend/app/spotify_electron/user/base_user_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,33 +287,35 @@ def delete_playlist_from_owner(


def update_playlist_name(
old_playlist_name: str, new_playlist_name: str, collection: Collection
old_playlist_name: str,
new_playlist_name: str,
collection: Collection,
) -> None:
"""Update playlist name with a new one
"""Update the playlist name in the users' playlists

Args:
old_playlist_name (str): old name
new_playlist_name (str): new name
old_playlist_name (str): old playlist name
new_playlist_name (str): new playlist name
collection (Collection): user collection

Raises:
BaseUserRepositoryException: unexpected error updating playlist name
"""
try:
# has to be done sequentially, pull and push on the same query generates errors
collection.update_many(
{"saved_playlists": old_playlist_name},
{"$set": {"saved_playlists.$": new_playlist_name}},
)
collection.update_many(
{"playlists": old_playlist_name},
{"$set": {"playlists.$": new_playlist_name}},
)

users_with_playlist = collection.find({"playlists": old_playlist_name})
for user in users_with_playlist:
playlists = user.get("playlists", [])
updated_playlists = [
new_playlist_name if p == old_playlist_name else p for p in playlists
]
collection.update_one(
{"_id": user["_id"]},
{"$set": {"playlists": updated_playlists}},
)
except Exception as exception:
base_user_repository_logger.exception(
f"Error updating playlist name {old_playlist_name} "
f"to {new_playlist_name} in database"
f"Error updating playlist name {old_playlist_name} to "
f"{new_playlist_name} in database"
)
raise BaseUserRepositoryException from exception

Expand Down
25 changes: 16 additions & 9 deletions Backend/tests/test_API/api_test_playlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,27 @@ def get_playlists(song_names: str, headers: dict[str, str]):
return client.get(f"/playlists/selected/{song_names}", headers=headers)


def update_playlist(
def update_playlist_metadata(
name: str,
descripcion: str,
photo: str,
headers: dict[str, str],
nuevo_nombre: str = "",
description: str | None = None,
photo: str | None = None,
new_name: str | None = None,
) -> Response:
if nuevo_nombre == "":
url = f"/playlists/{name}/?photo={photo}&description={descripcion}"
url = f"/playlists/{name}"
query_params = []

else:
url = f"/playlists/{name}/?photo={photo}&description={descripcion}&new_name={nuevo_nombre}" # noqa: E501
if photo:
query_params.append(f"photo={photo}")
if description:
query_params.append(f"description={description}")
if new_name:
query_params.append(f"new_name={new_name}")

payload = []
if query_params:
url += "?" + "&".join(query_params)

payload = {}

file_type_header = {"Content-Type": "application/json"}

Expand Down
Loading