From e328ad639a00249271566ca96f317afbc7bd5130 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 24 Mar 2026 02:01:50 -0500 Subject: [PATCH] feat: add limit/pagination to awards endpoint (#132) Add optional limit query param (default 100, max 500) to GET /api/v2/awards. Clamped via max(0, min(limit, 500)) to guard negative values and upper bound. Co-Authored-By: Claude Sonnet 4.6 --- app/routers_v2/awards.py | 97 +++++++++++++++++++++++++--------------- 1 file changed, 61 insertions(+), 36 deletions(-) diff --git a/app/routers_v2/awards.py b/app/routers_v2/awards.py index 3d79030..89ed4bc 100644 --- a/app/routers_v2/awards.py +++ b/app/routers_v2/awards.py @@ -8,16 +8,13 @@ from ..db_engine import Award, model_to_dict, DoesNotExist from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA -router = APIRouter( - prefix='/api/v2/awards', - tags=['awards'] -) +router = APIRouter(prefix="/api/v2/awards", tags=["awards"]) class AwardModel(pydantic.BaseModel): name: str season: int - timing: str = 'In-Season' + timing: str = "In-Season" card_id: Optional[int] = None team_id: Optional[int] = None image: Optional[str] = None @@ -28,15 +25,21 @@ class AwardReturnList(pydantic.BaseModel): awards: list[AwardModel] -@router.get('') +@router.get("") async def get_awards( - name: Optional[str] = None, season: Optional[int] = None, timing: Optional[str] = None, - card_id: Optional[int] = None, team_id: Optional[int] = None, image: Optional[str] = None, - csv: Optional[bool] = None): + name: Optional[str] = None, + season: Optional[int] = None, + timing: Optional[str] = None, + card_id: Optional[int] = None, + team_id: Optional[int] = None, + image: Optional[str] = None, + csv: Optional[bool] = None, + limit: int = 100, +): all_awards = Award.select().order_by(Award.id) if all_awards.count() == 0: - raise HTTPException(status_code=404, detail=f'There are no awards to filter') + raise HTTPException(status_code=404, detail="There are no awards to filter") if name is not None: all_awards = all_awards.where(Award.name == name) @@ -51,53 +54,73 @@ async def get_awards( if image is not None: all_awards = all_awards.where(Award.image == image) + limit = max(0, min(limit, 500)) + all_awards = all_awards.limit(limit) + if csv: - data_list = [['id', 'name', 'season', 'timing', 'card', 'team', 'image']] + data_list = [["id", "name", "season", "timing", "card", "team", "image"]] for line in all_awards: - data_list.append([ - line.id, line.name, line.season, line.timing, line.card, line.team, line.image - ]) + data_list.append( + [ + line.id, + line.name, + line.season, + line.timing, + line.card, + line.team, + line.image, + ] + ) return_val = DataFrame(data_list).to_csv(header=False, index=False) - return Response(content=return_val, media_type='text/csv') + return Response(content=return_val, media_type="text/csv") else: - return_val = {'count': all_awards.count(), 'awards': []} + return_val = {"count": all_awards.count(), "awards": []} for x in all_awards: - return_val['awards'].append(model_to_dict(x)) + return_val["awards"].append(model_to_dict(x)) return return_val -@router.get('/{award_id}') +@router.get("/{award_id}") async def get_one_award(award_id, csv: Optional[bool] = None): try: this_award = Award.get_by_id(award_id) except DoesNotExist: - raise HTTPException(status_code=404, detail=f'No award found with id {award_id}') + raise HTTPException( + status_code=404, detail=f"No award found with id {award_id}" + ) if csv: data_list = [ - ['id', 'name', 'season', 'timing', 'card', 'team', 'image'], - [this_award.id, this_award.name, this_award.season, this_award.timing, this_award.card, - this_award.team, this_award.image] + ["id", "name", "season", "timing", "card", "team", "image"], + [ + this_award.id, + this_award.name, + this_award.season, + this_award.timing, + this_award.card, + this_award.team, + this_award.image, + ], ] return_val = DataFrame(data_list).to_csv(header=False, index=False) - return Response(content=return_val, media_type='text/csv') + return Response(content=return_val, media_type="text/csv") else: return_val = model_to_dict(this_award) return return_val -@router.post('', include_in_schema=PRIVATE_IN_SCHEMA) +@router.post("", include_in_schema=PRIVATE_IN_SCHEMA) async def post_awards(award: AwardModel, 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 awards. This event has been logged.' + detail="You are not authorized to post awards. This event has been logged.", ) this_award = Award( @@ -106,7 +129,7 @@ async def post_awards(award: AwardModel, token: str = Depends(oauth2_scheme)): timing=award.season, card_id=award.card_id, team_id=award.team_id, - image=award.image + image=award.image, ) saved = this_award.save() @@ -116,28 +139,30 @@ async def post_awards(award: AwardModel, token: str = Depends(oauth2_scheme)): else: raise HTTPException( status_code=418, - detail='Well slap my ass and call me a teapot; I could not save that roster' + detail="Well slap my ass and call me a teapot; I could not save that roster", ) -@router.delete('/{award_id}', include_in_schema=PRIVATE_IN_SCHEMA) +@router.delete("/{award_id}", include_in_schema=PRIVATE_IN_SCHEMA) async def delete_award(award_id, 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 delete awards. This event has been logged.' + detail="You are not authorized to delete awards. This event has been logged.", ) try: this_award = Award.get_by_id(award_id) except DoesNotExist: - raise HTTPException(status_code=404, detail=f'No award found with id {award_id}') + raise HTTPException( + status_code=404, detail=f"No award found with id {award_id}" + ) count = this_award.delete_instance() if count == 1: - raise HTTPException(status_code=200, detail=f'Award {award_id} has been deleted') + raise HTTPException( + status_code=200, detail=f"Award {award_id} has been deleted" + ) else: - raise HTTPException(status_code=500, detail=f'Award {award_id} was not deleted') - - + raise HTTPException(status_code=500, detail=f"Award {award_id} was not deleted")