From 740a8d7c662c130265dc6afc4b7aaab1b52501f5 Mon Sep 17 00:00:00 2001 From: Cesar E Garza Date: Fri, 2 Jan 2026 23:27:28 -0600 Subject: [PATCH] Add tournament teams endpoint to sendou proxy This commit introduces a new endpoint to fetch all teams in a tournament from sendou.ink. The route proxies requests to the sendou.ink turbo-stream endpoint and returns the team data in a structured JSON format. Comprehensive error handling is included to manage potential HTTP and decoding issues, ensuring robust functionality and a seamless user experience. --- src/fast_api_app/routes/sendou_proxy.py | 58 ++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/src/fast_api_app/routes/sendou_proxy.py b/src/fast_api_app/routes/sendou_proxy.py index 1097b80..53e6877 100644 --- a/src/fast_api_app/routes/sendou_proxy.py +++ b/src/fast_api_app/routes/sendou_proxy.py @@ -7,7 +7,7 @@ from __future__ import annotations import logging -from typing import Any, Dict +from typing import Any, Dict, List import httpx import orjson @@ -27,6 +27,7 @@ "features/tournament-bracket/routes/to.$id.matches.$mid" ) ROUTE_KEY_TOURNAMENT_TEAM = "features/tournament/routes/to.$id.teams.$tid" +ROUTE_KEY_TOURNAMENT = "features/tournament/routes/to.$id" async def _fetch_turbo_stream(url: str) -> bytes: @@ -154,3 +155,58 @@ async def get_tournament_team( ) return data + + +@router.get( + "/to/{tournament_id}/teams", + name="sendou-tournament-teams", + summary="Get all teams in a tournament from sendou.ink", + description=( + "Proxies the sendou.ink turbo-stream endpoint and returns the list of teams. " + "Each team includes id, name, seed, and members." + ), +) +async def get_tournament_teams( + tournament_id: int = Path(..., description="Tournament ID on sendou.ink"), +) -> List[Dict[str, Any]]: + """Fetch and decode tournament teams list from sendou.ink.""" + url = f"{SENDOU_BASE_URL}/to/{tournament_id}/teams.data" + + try: + raw_data = await _fetch_turbo_stream(url) + except httpx.HTTPStatusError as e: + logger.warning( + "sendou.ink returned %s for tournament=%s teams", + e.response.status_code, + tournament_id, + ) + raise HTTPException( + status_code=e.response.status_code, + detail=f"sendou.ink returned {e.response.status_code}", + ) + except httpx.RequestError as e: + logger.error("Failed to fetch from sendou.ink: %s", e) + raise HTTPException( + status_code=502, + detail="Failed to fetch from sendou.ink", + ) + + try: + data = _decode_and_extract(raw_data, ROUTE_KEY_TOURNAMENT) + except ValueError as e: + logger.error("Failed to decode turbo-stream data: %s", e) + raise HTTPException( + status_code=502, + detail="Failed to decode sendou.ink response", + ) + + # Extract teams from tournament.ctx.teams + try: + teams = data["data"]["tournament"]["ctx"]["teams"] + except (KeyError, TypeError): + raise HTTPException( + status_code=502, + detail="Unexpected response structure from sendou.ink", + ) + + return teams