From e7fcf611da297be61b7df6cbfc22429bfcbec38d Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 24 Mar 2026 03:32:22 -0500 Subject: [PATCH] feat: add limit/pagination to gauntletruns endpoint (#146) Closes #146 Co-Authored-By: Claude Sonnet 4.6 --- app/routers_v2/gauntletruns.py | 95 ++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 38 deletions(-) diff --git a/app/routers_v2/gauntletruns.py b/app/routers_v2/gauntletruns.py index cc85eeb..cc196e9 100644 --- a/app/routers_v2/gauntletruns.py +++ b/app/routers_v2/gauntletruns.py @@ -8,10 +8,7 @@ from ..db_engine import GauntletRun, model_to_dict, DatabaseError, DoesNotExist from ..dependencies import oauth2_scheme, valid_token -router = APIRouter( - prefix='/api/v2/gauntletruns', - tags=['notifs'] -) +router = APIRouter(prefix="/api/v2/gauntletruns", tags=["notifs"]) class GauntletRunModel(pydantic.BaseModel): @@ -24,13 +21,25 @@ class GauntletRunModel(pydantic.BaseModel): ended: Optional[int] = None -@router.get('') +@router.get("") async def get_gauntletruns( - team_id: list = Query(default=None), wins: Optional[int] = None, wins_min: Optional[int] = None, - wins_max: Optional[int] = None, losses: Optional[int] = None, losses_min: Optional[int] = None, - losses_max: Optional[int] = None, gsheet: Optional[str] = None, created_after: Optional[int] = None, - created_before: Optional[int] = None, ended_after: Optional[int] = None, ended_before: Optional[int] = None, - is_active: Optional[bool] = None, gauntlet_id: list = Query(default=None), season: list = Query(default=None)): + team_id: list = Query(default=None), + wins: Optional[int] = None, + wins_min: Optional[int] = None, + wins_max: Optional[int] = None, + losses: Optional[int] = None, + losses_min: Optional[int] = None, + losses_max: Optional[int] = None, + gsheet: Optional[str] = None, + created_after: Optional[int] = None, + created_before: Optional[int] = None, + ended_after: Optional[int] = None, + ended_before: Optional[int] = None, + is_active: Optional[bool] = None, + gauntlet_id: list = Query(default=None), + season: list = Query(default=None), + limit: int = 100, +): all_gauntlets = GauntletRun.select().order_by(GauntletRun.id) if team_id is not None: @@ -73,39 +82,48 @@ async def get_gauntletruns( if season is not None: all_gauntlets = all_gauntlets.where(GauntletRun.team.season << season) - return_val = {'count': all_gauntlets.count(), 'runs': []} - for x in all_gauntlets: - return_val['runs'].append(model_to_dict(x)) + limit = max(0, min(limit, 500)) + return_val = {"count": all_gauntlets.count(), "runs": []} + for x in all_gauntlets.limit(limit): + return_val["runs"].append(model_to_dict(x)) return return_val -@router.get('/{gauntletrun_id}') +@router.get("/{gauntletrun_id}") async def get_one_gauntletrun(gauntletrun_id): try: this_gauntlet = GauntletRun.get_by_id(gauntletrun_id) except DoesNotExist: - raise HTTPException(status_code=404, detail=f'No gauntlet found with id {gauntletrun_id}') + raise HTTPException( + status_code=404, detail=f"No gauntlet found with id {gauntletrun_id}" + ) return_val = model_to_dict(this_gauntlet) return return_val -@router.patch('/{gauntletrun_id}') +@router.patch("/{gauntletrun_id}") async def patch_gauntletrun( - gauntletrun_id, team_id: Optional[int] = None, wins: Optional[int] = None, losses: Optional[int] = None, - gsheet: Optional[str] = None, created: Optional[bool] = None, ended: Optional[bool] = None, - token: str = Depends(oauth2_scheme)): + gauntletrun_id, + team_id: Optional[int] = None, + wins: Optional[int] = None, + losses: Optional[int] = None, + gsheet: Optional[str] = None, + created: Optional[bool] = None, + ended: Optional[bool] = None, + token: str = Depends(oauth2_scheme), +): if not valid_token(token): - logging.warning('Bad Token: [REDACTED]') + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, - detail='You are not authorized to patch gauntlet runs. This event has been logged.' + detail="You are not authorized to patch gauntlet runs. This event has been logged.", ) this_run = GauntletRun.get_or_none(GauntletRun.id == gauntletrun_id) if this_run is None: - raise KeyError(f'Gauntlet Run ID {gauntletrun_id} not found') + raise KeyError(f"Gauntlet Run ID {gauntletrun_id} not found") if team_id is not None: this_run.team_id = team_id @@ -130,41 +148,42 @@ async def patch_gauntletrun( r_curr = model_to_dict(this_run) return r_curr else: - raise DatabaseError(f'Unable to patch gauntlet run {gauntletrun_id}') + raise DatabaseError(f"Unable to patch gauntlet run {gauntletrun_id}") -@router.post('') -async def post_gauntletrun(gauntletrun: GauntletRunModel, token: str = Depends(oauth2_scheme)): +@router.post("") +async def post_gauntletrun( + gauntletrun: GauntletRunModel, token: str = Depends(oauth2_scheme) +): if not valid_token(token): - logging.warning('Bad Token: [REDACTED]') + logging.warning("Bad Token: [REDACTED]") raise HTTPException( status_code=401, - detail='You are not authorized to post gauntlets. This event has been logged.' + detail="You are not authorized to post gauntlets. This event has been logged.", ) run_data = gauntletrun.dict() # Convert milliseconds timestamps to datetime for PostgreSQL - if run_data.get('created'): - run_data['created'] = datetime.fromtimestamp(run_data['created'] / 1000) + if run_data.get("created"): + run_data["created"] = datetime.fromtimestamp(run_data["created"] / 1000) else: - run_data['created'] = datetime.now() - if run_data.get('ended'): - run_data['ended'] = datetime.fromtimestamp(run_data['ended'] / 1000) + run_data["created"] = datetime.now() + if run_data.get("ended"): + run_data["ended"] = datetime.fromtimestamp(run_data["ended"] / 1000) else: - run_data['ended'] = None + run_data["ended"] = None this_run = GauntletRun(**run_data) if this_run.save(): r_run = model_to_dict(this_run) return r_run else: - raise DatabaseError(f'Unable to post gauntlet run') + raise DatabaseError("Unable to post gauntlet run") -@router.delete('/{gauntletrun_id}') +@router.delete("/{gauntletrun_id}") async def delete_gauntletrun(gauntletrun_id): if GauntletRun.delete_by_id(gauntletrun_id) == 1: - return f'Deleted gauntlet run ID {gauntletrun_id}' - - raise DatabaseError(f'Unable to delete gauntlet run {gauntletrun_id}') + return f"Deleted gauntlet run ID {gauntletrun_id}" + raise DatabaseError(f"Unable to delete gauntlet run {gauntletrun_id}")