Skip to content

Commit

Permalink
added update collection endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
zurdi15 committed Jul 2, 2024
1 parent 97f9792 commit 23e0a06
Show file tree
Hide file tree
Showing 12 changed files with 313 additions and 17 deletions.
1 change: 1 addition & 0 deletions backend/alembic/versions/0022_collections_.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def upgrade() -> None:
sa.Column("description", sa.Text(), nullable=True),
sa.Column("path_cover_l", sa.String(length=1000), nullable=True),
sa.Column("path_cover_s", sa.String(length=1000), nullable=True),
sa.Column("url_cover", sa.Text(), nullable=True),
sa.Column("roms", sa.JSON(), nullable=False),
sa.Column("user_id", sa.Integer(), nullable=False),
sa.Column("is_public", sa.Boolean(), nullable=False),
Expand Down
33 changes: 31 additions & 2 deletions backend/endpoints/collections.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import json

from decorators.auth import protected_route
from endpoints.responses import MessageResponse
from endpoints.responses.collection import CollectionSchema
from exceptions.endpoint_exceptions import (
CollectionAlreadyExistsException,
CollectionNotFoundInDatabaseException,
CollectionPermissionError,
)
from fastapi import APIRouter, Request
from handler.database import db_collection_handler
Expand Down Expand Up @@ -75,7 +78,7 @@ def get_collection(request: Request, id: int) -> CollectionSchema:


@protected_route(router.put, "/collections/{id}", ["collections.write"])
async def update_collection(request: Request) -> MessageResponse:
async def update_collection(request: Request, id: int) -> MessageResponse:
"""Update collection endpoint
Args:
Expand All @@ -85,7 +88,33 @@ async def update_collection(request: Request) -> MessageResponse:
MessageResponse: Standard message response
"""

return {"msg": "Enpoint not available yet"}
data = await request.form()
collection = db_collection_handler.get_collection(id)

if collection.user_id != request.user.id:
raise CollectionPermissionError(id)

if not collection:
raise CollectionNotFoundInDatabaseException(id)

roms = collection.roms # Default to the roms list from the database

if "roms" in data:
try:
roms = json.loads(data.get("roms"))
except json.JSONDecodeError:
raise ValueError("Invalid JSON for roms field")

Check failure on line 106 in backend/endpoints/collections.py

View check run for this annotation

Trunk.io / Trunk Check

ruff(B904)

[new] Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling

Check failure on line 106 in backend/endpoints/collections.py

View workflow job for this annotation

GitHub Actions / Trunk Check

ruff(B904)

[new] Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling

cleaned_data = {
"name": data.get("name", collection.name),
"description": data.get("description", collection.description),
"roms": roms,
"is_public": data.get("is_public", collection.is_public),
"user_id": request.user.id,
}

db_collection_handler.update_collection(id, cleaned_data)
return {"msg": "Collection updated successfully!"}


@protected_route(router.delete, "/collections/{id}", ["collections.write"])
Expand Down
1 change: 1 addition & 0 deletions backend/endpoints/responses/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class CollectionSchema(BaseModel):
description: str
path_cover_l: str | None
path_cover_s: str | None
url_cover: str
roms: set[int]
rom_count: int
user_id: int
Expand Down
6 changes: 0 additions & 6 deletions backend/endpoints/responses/rom.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,6 @@ def from_orm_with_request(cls, db_rom: Rom, request: Request) -> RomSchema:

return rom

@classmethod
def from_orm_with_request_list(
cls, db_roms: list[Rom], request: Request
) -> list[RomSchema]:
return [cls.from_orm_with_request(rom, request) for rom in db_roms]

@computed_field # type: ignore
@property
def sort_comparator(self) -> str:
Expand Down
6 changes: 3 additions & 3 deletions backend/endpoints/rom.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def get_roms(
list[RomSchema]: List of roms stored in the database
"""

db_roms = db_rom_handler.get_roms(
roms = db_rom_handler.get_roms(
platform_id=platform_id,
collection_id=collection_id,
search_term=search_term.lower(),
Expand All @@ -119,7 +119,7 @@ def get_roms(
limit=limit,
)

return RomSchema.from_orm_with_request_list(db_roms, request)
return [RomSchema.from_orm_with_request(rom, request) for rom in roms]


@protected_route(
Expand Down Expand Up @@ -370,7 +370,7 @@ async def update_rom(
else:
cleaned_data["url_cover"] = data.get("url_cover", rom.url_cover)
path_cover_s, path_cover_l = fs_resource_handler.get_rom_cover(
overwrite=True,
overwrite=cleaned_data["url_cover"] != rom.url_cover,
rom=rom,
url_cover=cleaned_data.get("url_cover", ""),
)
Expand Down
15 changes: 12 additions & 3 deletions backend/exceptions/endpoint_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,18 @@ def __init__(self, id):
self.message = f"Collection with id '{id}' not found"
super().__init__(self.message)
log.critical(self.message)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=self.message
)
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=self.message)

def __repr__(self) -> str:
return self.message


class CollectionPermissionError(Exception):
def __init__(self, id):
self.message = f"Permission denied for collection with id '{id}'"
super().__init__(self.message)
log.critical(self.message)
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=self.message)

def __repr__(self) -> str:
return self.message
Expand Down
13 changes: 12 additions & 1 deletion backend/handler/database/collections_handler.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from decorators.database import begin_session
from models.collection import Collection
from sqlalchemy import Select, and_, delete, or_, select
from sqlalchemy import Select, delete, select, update
from sqlalchemy.orm import Session

from .base_handler import DBBaseHandler
Expand Down Expand Up @@ -41,6 +41,17 @@ def get_collections(
.all()
)

@begin_session
def update_collection(
self, id: int, data: dict, session: Session = None
) -> Collection:
return session.execute(
update(Collection)
.where(Collection.id == id)
.values(**data)
.execution_options(synchronize_session="evaluate")
)

@begin_session
def delete_collection(self, id: int, session: Session = None) -> int:
return session.execute(
Expand Down
10 changes: 10 additions & 0 deletions backend/models/collection.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

from functools import cached_property

from models.base import BaseModel
from models.user import User
from sqlalchemy import JSON, ForeignKey, String, Text
Expand All @@ -17,6 +19,10 @@ class Collection(BaseModel):
path_cover_l: Mapped[str | None] = mapped_column(Text, default="")
path_cover_s: Mapped[str | None] = mapped_column(Text, default="")

url_cover: Mapped[str | None] = mapped_column(
Text, default="", doc="URL to cover image stored in IGDB"
)

roms: Mapped[set[int]] = mapped_column(
JSON, default=[], doc="Rom id's that belong to this collection"
)
Expand All @@ -33,5 +39,9 @@ def user__username(self) -> str:
def rom_count(self):
return len(self.roms)

@cached_property
def has_cover(self) -> bool:
return bool(self.path_cover_s or self.path_cover_l)

def __repr__(self) -> str:
return self.name
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ function previewImage(event: Event) {
}
async function removeArtwork() {
imagePreviewUrl.value = `/assets/default/cover/big_${theme.global.name.value}_missing_cover.png`;
imagePreviewUrl.value = `/assets/default/cover/big_${theme.global.name.value}_collection.png`;
removeCover.value = true;
}
Expand Down
Loading

0 comments on commit 23e0a06

Please sign in to comment.