Merge pull request #2413 from rommapp/misc/improve-api-docs-delete-endpoints

misc: Improve API docs for Delete endpoints
This commit is contained in:
Michael Manganiello
2025-09-09 20:13:19 -03:00
committed by GitHub
7 changed files with 121 additions and 77 deletions

View File

@@ -1,7 +1,9 @@
import json
from io import BytesIO
from typing import Annotated
from fastapi import Request, UploadFile
from fastapi import Path as PathVar
from fastapi import Request, UploadFile, status
from config import str_to_bool
from decorators.auth import protected_route
@@ -388,22 +390,18 @@ async def update_smart_collection(
return SmartCollectionSchema.model_validate(smart_collection)
@protected_route(router.delete, "/{id}", [Scope.COLLECTIONS_WRITE])
async def delete_collection(request: Request, id: int) -> None:
"""Delete collections endpoint
Args:
request (Request): Fastapi Request object
{
"collections": List of rom's ids to delete
}
Raises:
HTTPException: Collection not found
"""
@protected_route(
router.delete,
"/{id}",
[Scope.COLLECTIONS_WRITE],
responses={status.HTTP_404_NOT_FOUND: {}},
)
async def delete_collection(
request: Request,
id: Annotated[int, PathVar(description="Collection internal id.", ge=1)],
) -> None:
"""Delete a collection by ID."""
collection = db_collection_handler.get_collection(id)
if not collection:
raise CollectionNotFoundInDatabaseException(id)
@@ -418,17 +416,18 @@ async def delete_collection(request: Request, id: int) -> None:
)
@protected_route(router.delete, "/smart/{id}", [Scope.COLLECTIONS_WRITE])
async def delete_smart_collection(request: Request, id: int) -> None:
"""Delete smart collection endpoint
Args:
request (Request): Fastapi Request object
id (int): Smart collection id
"""
@protected_route(
router.delete,
"/smart/{id}",
[Scope.COLLECTIONS_WRITE],
responses={status.HTTP_404_NOT_FOUND: {}},
)
async def delete_smart_collection(
request: Request,
id: Annotated[int, PathVar(description="Smart collection internal id.", ge=1)],
) -> None:
"""Delete a smart collection by ID."""
smart_collection = db_collection_handler.get_smart_collection(id)
if not smart_collection:
raise CollectionNotFoundInDatabaseException(id)

View File

@@ -1,4 +1,6 @@
from fastapi import File, HTTPException, Request, UploadFile, status
from typing import Annotated
from fastapi import Body, File, HTTPException, Request, UploadFile, status
from fastapi.responses import FileResponse
from config import DISABLE_DOWNLOAD_ENDPOINT_AUTH
@@ -216,46 +218,46 @@ def get_firmware_content(
@protected_route(router.post, "/delete", [Scope.FIRMWARE_WRITE])
async def delete_firmware(
request: Request,
firmware: Annotated[
list[int],
Body(
description="List of firmware ids to delete from database.",
embed=True,
),
],
delete_from_fs: Annotated[
list[int],
Body(
description="List of firmware ids to delete from filesystem.",
default_factory=list,
embed=True,
),
],
) -> BulkOperationResponse:
"""Delete firmware endpoint
Args:
request (Request): Fastapi Request object.
{
"firmware": List of firmware IDs to delete
}
delete_from_fs (bool, optional): Flag to delete rom from filesystem. Defaults to False.
Returns:
BulkOperationResponse: Bulk operation response with details
"""
data: dict = await request.json()
firmware_ids: list = data["firmware"]
delete_from_fs: list = data["delete_from_fs"]
"""Delete firmware."""
successful_items = 0
failed_items = 0
errors = []
for id in firmware_ids:
firmware = db_firmware_handler.get_firmware(id)
if not firmware:
for id in firmware:
fw = db_firmware_handler.get_firmware(id)
if not fw:
failed_items += 1
errors.append(f"Firmware with ID {id} not found")
continue
try:
log.info(f"Deleting {hl(firmware.file_name)} from database")
log.info(f"Deleting {hl(fw.file_name)} from database")
db_firmware_handler.delete_firmware(id)
if id in delete_from_fs:
log.info(f"Deleting {hl(firmware.file_name)} from filesystem")
log.info(f"Deleting {hl(fw.file_name)} from filesystem")
try:
file_path = f"{firmware.file_path}/{firmware.file_name}"
file_path = f"{fw.file_path}/{fw.file_name}"
await fs_firmware_handler.remove_file(file_path=file_path)
except FileNotFoundError:
error = f"Firmware file {hl(firmware.file_name)} not found for platform {hl(firmware.platform.slug)}"
error = f"Firmware file {hl(fw.file_name)} not found for platform {hl(fw.platform.slug)}"
log.error(error)
errors.append(error)
failed_items += 1

View File

@@ -204,7 +204,7 @@ async def delete_platform(
request: Request,
id: Annotated[int, PathVar(description="Platform id.", ge=1)],
) -> None:
"""Delete a platform."""
"""Delete a platform by ID."""
platform = db_platform_handler.get_platform(id)
if not platform:

View File

@@ -874,13 +874,17 @@ async def delete_roms(
request: Request,
roms: Annotated[
list[int],
Body(description="List of rom ids to delete from database."),
Body(
description="List of rom ids to delete from database.",
embed=True,
),
],
delete_from_fs: Annotated[
list[int],
Body(
description="List of rom ids to delete from filesystem.",
default_factory=list,
embed=True,
),
],
) -> BulkOperationResponse:

View File

@@ -1,6 +1,7 @@
from datetime import datetime, timezone
from typing import Annotated
from fastapi import HTTPException, Request, UploadFile, status
from fastapi import Body, HTTPException, Request, UploadFile, status
from decorators.auth import protected_route
from endpoints.responses.assets import SaveSchema
@@ -223,17 +224,32 @@ async def update_save(request: Request, id: int) -> SaveSchema:
return SaveSchema.model_validate(db_save)
@protected_route(router.post, "/delete", [Scope.ASSETS_WRITE])
async def delete_saves(request: Request) -> list[int]:
data: dict = await request.json()
save_ids: list = data["saves"]
if not save_ids:
@protected_route(
router.post,
"/delete",
[Scope.ASSETS_WRITE],
responses={
status.HTTP_400_BAD_REQUEST: {},
status.HTTP_404_NOT_FOUND: {},
},
)
async def delete_saves(
request: Request,
saves: Annotated[
list[int],
Body(
description="List of save ids to delete from database.",
embed=True,
),
],
) -> list[int]:
"""Delete saves."""
if not saves:
error = "No saves were provided"
log.error(error)
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=error)
for save_id in save_ids:
for save_id in saves:
save = db_save_handler.get_save(user_id=request.user.id, id=save_id)
if not save:
error = f"Save with ID {save_id} not found"
@@ -262,4 +278,4 @@ async def delete_saves(request: Request) -> list[int]:
error = f"Screenshot file {hl(save.screenshot.file_name)} not found for save {hl(save.file_name)}[{hl(save.rom.platform_slug)}]"
log.error(error)
return save_ids
return saves

View File

@@ -1,6 +1,7 @@
from datetime import datetime, timezone
from typing import Annotated
from fastapi import HTTPException, Request, UploadFile, status
from fastapi import Body, HTTPException, Request, UploadFile, status
from decorators.auth import protected_route
from endpoints.responses.assets import StateSchema
@@ -227,17 +228,32 @@ async def update_state(request: Request, id: int) -> StateSchema:
return StateSchema.model_validate(db_state)
@protected_route(router.post, "/delete", [Scope.ASSETS_WRITE])
async def delete_states(request: Request) -> list[int]:
data: dict = await request.json()
state_ids: list = data["states"]
if not state_ids:
@protected_route(
router.post,
"/delete",
[Scope.ASSETS_WRITE],
responses={
status.HTTP_400_BAD_REQUEST: {},
status.HTTP_404_NOT_FOUND: {},
},
)
async def delete_states(
request: Request,
states: Annotated[
list[int],
Body(
description="List of states ids to delete from database.",
embed=True,
),
],
) -> list[int]:
"""Delete states."""
if not states:
error = "No states were provided"
log.error(error)
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=error)
for state_id in state_ids:
for state_id in states:
state = db_state_handler.get_state(user_id=request.user.id, id=state_id)
if not state:
error = f"State with ID {state_id} not found"
@@ -266,4 +282,4 @@ async def delete_states(request: Request) -> list[int]:
error = f"Screenshot file {hl(state.screenshot.file_name)} not found for state {hl(state.file_name)}[{hl(state.rom.platform_slug)}]"
log.error(error)
return state_ids
return states

View File

@@ -336,13 +336,20 @@ async def update_user(
return UserSchema.model_validate(db_user)
@protected_route(router.delete, "/{id}", [Scope.USERS_WRITE])
async def delete_user(request: Request, id: int) -> None:
"""Delete user endpoint
Args:
request (Request): Fastapi Request object
user_id (int): User internal id
@protected_route(
router.delete,
"/{id}",
[Scope.USERS_WRITE],
responses={
status.HTTP_400_BAD_REQUEST: {},
status.HTTP_404_NOT_FOUND: {},
},
)
async def delete_user(
request: Request,
id: Annotated[int, PathVar(description="User internal id.", ge=1)],
) -> None:
"""Delete a user by ID.
Raises:
HTTPException: User is not found in database