major-domo-database/app/routers_v3/teams.py
Cal Corum 9ec69f9f2c
All checks were successful
Build Docker Image / build (pull_request) Successful in 3m38s
fix: standardize all collection POST routes to use trailing slash
aiohttp follows 307 redirects but converts POST to GET, silently
dropping the request body. Standardize all @router.post('') to
@router.post('/') so the canonical URL always has a trailing slash,
preventing 307 redirects when clients POST with trailing slashes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 19:34:28 -05:00

128 lines
3.7 KiB
Python

"""
Team Router - Refactored
Thin HTTP layer using TeamService for business logic.
"""
from fastapi import APIRouter, Query, Response, Depends
from typing import List, Optional, Literal
from ..dependencies import (
oauth2_scheme,
PRIVATE_IN_SCHEMA,
handle_db_errors,
cache_result,
)
from ..services.base import BaseService
from ..services.team_service import TeamService
router = APIRouter(prefix="/api/v3/teams", tags=["teams"])
@router.get("")
@handle_db_errors
@cache_result(ttl=10 * 60, key_prefix="teams")
async def get_teams(
season: Optional[int] = None,
owner_id: list = Query(default=None),
manager_id: list = Query(default=None),
team_abbrev: list = Query(default=None),
active_only: Optional[bool] = False,
short_output: Optional[bool] = False,
csv: Optional[bool] = False,
):
"""Get teams with filtering."""
result = TeamService.get_teams(
season=season,
owner_id=owner_id if owner_id else None,
manager_id=manager_id if manager_id else None,
team_abbrev=team_abbrev if team_abbrev else None,
active_only=active_only or False,
short_output=short_output or False,
as_csv=csv or False,
)
if csv:
return Response(content=result, media_type="text/csv")
return result
@router.get("/{team_id}")
@handle_db_errors
@cache_result(ttl=30 * 60, key_prefix="team")
async def get_one_team(team_id: int):
"""Get a single team by ID."""
return TeamService.get_team(team_id)
@router.get("/{team_id}/roster")
@handle_db_errors
@cache_result(ttl=30 * 60, key_prefix="team-roster")
async def get_team_roster_default(team_id: int, sort: Optional[str] = None):
"""Get team roster with IL lists (defaults to current season)."""
return TeamService.get_team_roster(team_id, "current", sort=sort)
@router.get("/{team_id}/roster/{which}")
@handle_db_errors
@cache_result(ttl=30 * 60, key_prefix="team-roster")
async def get_team_roster(
team_id: int, which: Literal["current", "next"], sort: Optional[str] = None
):
"""Get team roster with IL lists."""
return TeamService.get_team_roster(team_id, which, sort=sort)
@router.patch("/{team_id}")
async def patch_team(
team_id: int,
token: str = Depends(oauth2_scheme),
manager1_id: Optional[int] = None,
manager2_id: Optional[int] = None,
gmid: Optional[int] = None,
gmid2: Optional[int] = None,
mascot: Optional[str] = None,
stadium: Optional[str] = None,
thumbnail: Optional[str] = None,
color: Optional[str] = None,
abbrev: Optional[str] = None,
sname: Optional[str] = None,
lname: Optional[str] = None,
dice_color: Optional[str] = None,
division_id: Optional[int] = None,
):
"""Patch a team (partial update)."""
# Build dict of provided fields
data = {
key: value
for key, value in {
"manager1_id": manager1_id,
"manager2_id": manager2_id,
"gmid": gmid,
"gmid2": gmid2,
"mascot": mascot,
"stadium": stadium,
"thumbnail": thumbnail,
"color": color,
"abbrev": abbrev,
"sname": sname,
"lname": lname,
"dice_color": dice_color,
"division_id": division_id,
}.items()
if value is not None
}
return TeamService.update_team(team_id, data, token)
@router.post("/")
async def post_teams(team_list: dict, token: str = Depends(oauth2_scheme)):
"""Create multiple teams."""
return TeamService.create_teams(team_list.get("teams", []), token)
@router.delete("/{team_id}")
async def delete_team(team_id: int, token: str = Depends(oauth2_scheme)):
"""Delete a team."""
return TeamService.delete_team(team_id, token)