refactor: Update response types to use common schemas across API endpoints
Some checks failed
Backend CI / test (push) Failing after 3m51s

This commit is contained in:
JSC
2025-07-31 10:40:03 +02:00
parent dc372b961e
commit c13285ca4e
7 changed files with 101 additions and 37 deletions

View File

@@ -3,6 +3,7 @@
from fastapi import APIRouter from fastapi import APIRouter
from app.core.logging import get_logger from app.core.logging import get_logger
from app.schemas.common import HealthResponse
router = APIRouter() router = APIRouter()
@@ -10,7 +11,7 @@ logger = get_logger(__name__)
@router.get("/") @router.get("/")
def health() -> dict[str, str]: def health() -> HealthResponse:
"""Health check endpoint.""" """Health check endpoint."""
logger.info("Health check endpoint accessed") logger.info("Health check endpoint accessed")
return {"status": "healthy"} return HealthResponse(status="healthy")

View File

@@ -7,7 +7,13 @@ from fastapi import APIRouter, Depends, HTTPException, status
from app.core.dependencies import get_current_active_user_flexible from app.core.dependencies import get_current_active_user_flexible
from app.core.logging import get_logger from app.core.logging import get_logger
from app.models.user import User from app.models.user import User
from app.schemas.player import PlayerModeRequest, PlayerSeekRequest, PlayerVolumeRequest from app.schemas.common import MessageResponse
from app.schemas.player import (
PlayerModeRequest,
PlayerSeekRequest,
PlayerStateResponse,
PlayerVolumeRequest,
)
from app.services.player import get_player_service from app.services.player import get_player_service
logger = get_logger(__name__) logger = get_logger(__name__)
@@ -18,12 +24,12 @@ router = APIRouter(prefix="/player", tags=["player"])
@router.post("/play") @router.post("/play")
async def play( async def play(
current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001 current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001
) -> dict[str, str]: ) -> MessageResponse:
"""Play current sound.""" """Play current sound."""
try: try:
player = get_player_service() player = get_player_service()
await player.play() await player.play()
return {"message": "Playback started"} return MessageResponse(message="Playback started")
except Exception as e: except Exception as e:
logger.exception("Error starting playback") logger.exception("Error starting playback")
raise HTTPException( raise HTTPException(
@@ -36,12 +42,12 @@ async def play(
async def play_at_index( async def play_at_index(
index: int, index: int,
current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001 current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001
) -> dict[str, str]: ) -> MessageResponse:
"""Play sound at specific index.""" """Play sound at specific index."""
try: try:
player = get_player_service() player = get_player_service()
await player.play(index) await player.play(index)
return {"message": f"Playing sound at index {index}"} return MessageResponse(message=f"Playing sound at index {index}")
except ValueError as e: except ValueError as e:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, status_code=status.HTTP_400_BAD_REQUEST,
@@ -58,12 +64,12 @@ async def play_at_index(
@router.post("/pause") @router.post("/pause")
async def pause( async def pause(
current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001 current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001
) -> dict[str, str]: ) -> MessageResponse:
"""Pause playback.""" """Pause playback."""
try: try:
player = get_player_service() player = get_player_service()
await player.pause() await player.pause()
return {"message": "Playback paused"} return MessageResponse(message="Playback paused")
except Exception as e: except Exception as e:
logger.exception("Error pausing playback") logger.exception("Error pausing playback")
raise HTTPException( raise HTTPException(
@@ -75,12 +81,12 @@ async def pause(
@router.post("/stop") @router.post("/stop")
async def stop( async def stop(
current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001 current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001
) -> dict[str, str]: ) -> MessageResponse:
"""Stop playback.""" """Stop playback."""
try: try:
player = get_player_service() player = get_player_service()
await player.stop_playback() await player.stop_playback()
return {"message": "Playback stopped"} return MessageResponse(message="Playback stopped")
except Exception as e: except Exception as e:
logger.exception("Error stopping playback") logger.exception("Error stopping playback")
raise HTTPException( raise HTTPException(
@@ -92,12 +98,12 @@ async def stop(
@router.post("/next") @router.post("/next")
async def next_track( async def next_track(
current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001 current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001
) -> dict[str, str]: ) -> MessageResponse:
"""Skip to next track.""" """Skip to next track."""
try: try:
player = get_player_service() player = get_player_service()
await player.next() await player.next()
return {"message": "Skipped to next track"} return MessageResponse(message="Skipped to next track")
except Exception as e: except Exception as e:
logger.exception("Error skipping to next track") logger.exception("Error skipping to next track")
raise HTTPException( raise HTTPException(
@@ -109,12 +115,12 @@ async def next_track(
@router.post("/previous") @router.post("/previous")
async def previous_track( async def previous_track(
current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001 current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001
) -> dict[str, str]: ) -> MessageResponse:
"""Go to previous track.""" """Go to previous track."""
try: try:
player = get_player_service() player = get_player_service()
await player.previous() await player.previous()
return {"message": "Went to previous track"} return MessageResponse(message="Went to previous track")
except Exception as e: except Exception as e:
logger.exception("Error going to previous track") logger.exception("Error going to previous track")
raise HTTPException( raise HTTPException(
@@ -127,12 +133,12 @@ async def previous_track(
async def seek( async def seek(
request: PlayerSeekRequest, request: PlayerSeekRequest,
current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001 current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001
) -> dict[str, str]: ) -> MessageResponse:
"""Seek to specific position in current track.""" """Seek to specific position in current track."""
try: try:
player = get_player_service() player = get_player_service()
await player.seek(request.position_ms) await player.seek(request.position_ms)
return {"message": f"Seeked to position {request.position_ms}ms"} return MessageResponse(message=f"Seeked to position {request.position_ms}ms")
except Exception as e: except Exception as e:
logger.exception("Error seeking to position %s", request.position_ms) logger.exception("Error seeking to position %s", request.position_ms)
raise HTTPException( raise HTTPException(
@@ -145,12 +151,12 @@ async def seek(
async def set_volume( async def set_volume(
request: PlayerVolumeRequest, request: PlayerVolumeRequest,
current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001 current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001
) -> dict[str, str]: ) -> MessageResponse:
"""Set playback volume.""" """Set playback volume."""
try: try:
player = get_player_service() player = get_player_service()
await player.set_volume(request.volume) await player.set_volume(request.volume)
return {"message": f"Volume set to {request.volume}"} return MessageResponse(message=f"Volume set to {request.volume}")
except Exception as e: except Exception as e:
logger.exception("Error setting volume to %s", request.volume) logger.exception("Error setting volume to %s", request.volume)
raise HTTPException( raise HTTPException(
@@ -163,12 +169,12 @@ async def set_volume(
async def set_mode( async def set_mode(
request: PlayerModeRequest, request: PlayerModeRequest,
current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001 current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001
) -> dict[str, str]: ) -> MessageResponse:
"""Set playback mode.""" """Set playback mode."""
try: try:
player = get_player_service() player = get_player_service()
await player.set_mode(request.mode) await player.set_mode(request.mode)
return {"message": f"Mode set to {request.mode.value}"} return MessageResponse(message=f"Mode set to {request.mode.value}")
except Exception as e: except Exception as e:
logger.exception("Error setting mode to %s", request.mode) logger.exception("Error setting mode to %s", request.mode)
raise HTTPException( raise HTTPException(
@@ -180,12 +186,12 @@ async def set_mode(
@router.post("/reload-playlist") @router.post("/reload-playlist")
async def reload_playlist( async def reload_playlist(
current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001 current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001
) -> dict[str, str]: ) -> MessageResponse:
"""Reload current playlist.""" """Reload current playlist."""
try: try:
player = get_player_service() player = get_player_service()
await player.reload_playlist() await player.reload_playlist()
return {"message": "Playlist reloaded"} return MessageResponse(message="Playlist reloaded")
except Exception as e: except Exception as e:
logger.exception("Error reloading playlist") logger.exception("Error reloading playlist")
raise HTTPException( raise HTTPException(
@@ -197,11 +203,12 @@ async def reload_playlist(
@router.get("/state") @router.get("/state")
async def get_state( async def get_state(
current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001 current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001
) -> dict[str, Any]: ) -> PlayerStateResponse:
"""Get current player state.""" """Get current player state."""
try: try:
player = get_player_service() player = get_player_service()
return player.get_state() state = player.get_state()
return PlayerStateResponse(**state)
except Exception as e: except Exception as e:
logger.exception("Error getting player state") logger.exception("Error getting player state")
raise HTTPException( raise HTTPException(

View File

@@ -8,6 +8,7 @@ from sqlmodel.ext.asyncio.session import AsyncSession
from app.core.database import get_db from app.core.database import get_db
from app.core.dependencies import get_current_active_user_flexible from app.core.dependencies import get_current_active_user_flexible
from app.models.user import User from app.models.user import User
from app.schemas.common import MessageResponse
from app.schemas.playlist import ( from app.schemas.playlist import (
PlaylistAddSoundRequest, PlaylistAddSoundRequest,
PlaylistCreateRequest, PlaylistCreateRequest,
@@ -120,10 +121,10 @@ async def delete_playlist(
playlist_id: int, playlist_id: int,
current_user: Annotated[User, Depends(get_current_active_user_flexible)], current_user: Annotated[User, Depends(get_current_active_user_flexible)],
playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)],
) -> dict[str, str]: ) -> MessageResponse:
"""Delete a playlist.""" """Delete a playlist."""
await playlist_service.delete_playlist(playlist_id, current_user.id) await playlist_service.delete_playlist(playlist_id, current_user.id)
return {"message": "Playlist deleted successfully"} return MessageResponse(message="Playlist deleted successfully")
@router.get("/search/{query}") @router.get("/search/{query}")
@@ -165,7 +166,7 @@ async def add_sound_to_playlist(
request: PlaylistAddSoundRequest, request: PlaylistAddSoundRequest,
current_user: Annotated[User, Depends(get_current_active_user_flexible)], current_user: Annotated[User, Depends(get_current_active_user_flexible)],
playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)],
) -> dict[str, str]: ) -> MessageResponse:
"""Add a sound to a playlist.""" """Add a sound to a playlist."""
await playlist_service.add_sound_to_playlist( await playlist_service.add_sound_to_playlist(
playlist_id=playlist_id, playlist_id=playlist_id,
@@ -173,7 +174,7 @@ async def add_sound_to_playlist(
user_id=current_user.id, user_id=current_user.id,
position=request.position, position=request.position,
) )
return {"message": "Sound added to playlist successfully"} return MessageResponse(message="Sound added to playlist successfully")
@router.delete("/{playlist_id}/sounds/{sound_id}") @router.delete("/{playlist_id}/sounds/{sound_id}")
@@ -182,14 +183,14 @@ async def remove_sound_from_playlist(
sound_id: int, sound_id: int,
current_user: Annotated[User, Depends(get_current_active_user_flexible)], current_user: Annotated[User, Depends(get_current_active_user_flexible)],
playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)],
) -> dict[str, str]: ) -> MessageResponse:
"""Remove a sound from a playlist.""" """Remove a sound from a playlist."""
await playlist_service.remove_sound_from_playlist( await playlist_service.remove_sound_from_playlist(
playlist_id=playlist_id, playlist_id=playlist_id,
sound_id=sound_id, sound_id=sound_id,
user_id=current_user.id, user_id=current_user.id,
) )
return {"message": "Sound removed from playlist successfully"} return MessageResponse(message="Sound removed from playlist successfully")
@router.put("/{playlist_id}/sounds/reorder") @router.put("/{playlist_id}/sounds/reorder")
@@ -198,14 +199,14 @@ async def reorder_playlist_sounds(
request: PlaylistReorderRequest, request: PlaylistReorderRequest,
current_user: Annotated[User, Depends(get_current_active_user_flexible)], current_user: Annotated[User, Depends(get_current_active_user_flexible)],
playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)],
) -> dict[str, str]: ) -> MessageResponse:
"""Reorder sounds in a playlist.""" """Reorder sounds in a playlist."""
await playlist_service.reorder_playlist_sounds( await playlist_service.reorder_playlist_sounds(
playlist_id=playlist_id, playlist_id=playlist_id,
user_id=current_user.id, user_id=current_user.id,
sound_positions=request.sound_positions, sound_positions=request.sound_positions,
) )
return {"message": "Playlist sounds reordered successfully"} return MessageResponse(message="Playlist sounds reordered successfully")
@router.put("/{playlist_id}/set-current") @router.put("/{playlist_id}/set-current")
@@ -223,10 +224,10 @@ async def set_current_playlist(
async def unset_current_playlist( async def unset_current_playlist(
current_user: Annotated[User, Depends(get_current_active_user_flexible)], current_user: Annotated[User, Depends(get_current_active_user_flexible)],
playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)],
) -> dict[str, str]: ) -> MessageResponse:
"""Unset the current playlist.""" """Unset the current playlist."""
await playlist_service.unset_current_playlist(current_user.id) await playlist_service.unset_current_playlist(current_user.id)
return {"message": "Current playlist unset successfully"} return MessageResponse(message="Current playlist unset successfully")
@router.get("/{playlist_id}/stats") @router.get("/{playlist_id}/stats")

View File

@@ -10,7 +10,13 @@ from .auth import (
UserRegisterRequest, UserRegisterRequest,
UserResponse, UserResponse,
) )
from .player import PlayerModeRequest, PlayerSeekRequest, PlayerVolumeRequest from .common import HealthResponse, MessageResponse, StatusResponse
from .player import (
PlayerModeRequest,
PlayerSeekRequest,
PlayerStateResponse,
PlayerVolumeRequest,
)
from .playlist import ( from .playlist import (
PlaylistAddSoundRequest, PlaylistAddSoundRequest,
PlaylistCreateRequest, PlaylistCreateRequest,
@@ -31,9 +37,14 @@ __all__ = [
"UserLoginRequest", "UserLoginRequest",
"UserRegisterRequest", "UserRegisterRequest",
"UserResponse", "UserResponse",
# Common schemas
"HealthResponse",
"MessageResponse",
"StatusResponse",
# Player schemas # Player schemas
"PlayerModeRequest", "PlayerModeRequest",
"PlayerSeekRequest", "PlayerSeekRequest",
"PlayerStateResponse",
"PlayerVolumeRequest", "PlayerVolumeRequest",
# Playlist schemas # Playlist schemas
"PlaylistAddSoundRequest", "PlaylistAddSoundRequest",

21
app/schemas/common.py Normal file
View File

@@ -0,0 +1,21 @@
"""Common response schemas."""
from pydantic import BaseModel, Field
class MessageResponse(BaseModel):
"""Generic message response."""
message: str = Field(description="Response message")
class StatusResponse(BaseModel):
"""Generic status response."""
status: str = Field(description="Status message")
class HealthResponse(BaseModel):
"""Health check response."""
status: str = Field(description="Health status")

View File

@@ -1,5 +1,7 @@
"""Player schemas.""" """Player schemas."""
from typing import Any
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from app.services.player import PlayerMode from app.services.player import PlayerMode
@@ -21,3 +23,24 @@ class PlayerModeRequest(BaseModel):
"""Request model for mode change.""" """Request model for mode change."""
mode: PlayerMode = Field(description="Playback mode") mode: PlayerMode = Field(description="Playback mode")
class PlayerStateResponse(BaseModel):
"""Response model for player state."""
status: str = Field(description="Player status (playing, paused, stopped)")
current_sound: dict[str, Any] | None = Field(
None, description="Current sound information"
)
playlist: dict[str, Any] | None = Field(
None, description="Current playlist information"
)
position_ms: int = Field(description="Current position in milliseconds")
duration_ms: int | None = Field(
None, description="Total duration in milliseconds",
)
volume: int = Field(description="Current volume (0-100)")
mode: str = Field(description="Current playback mode")
index: int | None = Field(
None, description="Current track index in playlist",
)

View File

@@ -1,6 +1,6 @@
"""Playlist schemas.""" """Playlist schemas."""
from pydantic import BaseModel from pydantic import BaseModel, Field
from app.models.playlist import Playlist from app.models.playlist import Playlist
from app.models.sound import Sound from app.models.sound import Sound