feat: add jersey fields to PlayerProfileResponse (#257) #259
No reviewers
Labels
No labels
domain:backend
domain:devops
domain:frontend
status:approved
status:in-progress
status:needs-fix
status:qa
type:bug
type:devops
type:feature
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
ldraney/basketball-api!259
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "257-jersey-profile-fields"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Expose
jersey_option,jersey_size,jersey_number, andjersey_order_statusin the player profile endpoint so the parent-facing UI can render jersey ordering status. These fields already exist on the Player model and were already exposed via AdminPlayerItem — this mirrors them for the authenticated player profile route.Changes
src/basketball_api/routes/players.py— Added 4 jersey fields (jersey_option,jersey_size,jersey_number,jersey_order_status) toPlayerProfileResponseschema and mapped them in_player_profile_response()helper with proper enum-to-string conversiontests/test_players.py— Added 2 new tests:test_player_profile_includes_jersey_fields(verifies all 4 fields return correct values) andtest_player_profile_jersey_fields_null_when_empty(verifies null/default behavior)Test Plan
pytest tests/ -k test_player_profile— both new tests passruff formatandruff checkcleanReview Checklist
routes/players.pyandtests/test_players.py— no unrelated changes_player_profile_response()helper.valueNonewhen unset (exceptjersey_order_statuswhich defaults to"none")Related Notes
Related
forgejo_admin/westside-landing#197— frontend consumer of these fieldsforgejo_admin/basketball-api#248/#249— admin version of same fieldsQA Review -- PR #259
Diff Summary
src/basketball_api/routes/players.py-- 4 fields added to schema + helper mappingtests/test_players.py-- 2 new tests covering populated and empty jersey dataFindings
Schema (PlayerProfileResponse): All 4 jersey fields added as
str | None, consistent with howdivisionand other enum-backed fields are typed. Field placement betweensubscription_statusandteam/parentis logical.Helper mapping (_player_profile_response): Enum-to-string conversion via
.value if X else Nonefollows the existing pattern fordivision.jersey_numberis a plain string passthrough -- correct per the model (String(2)).Note:
jersey_order_statushas a defensiveif player.jersey_order_status else Noneguard. The model column is non-nullable with a server_default of"none", so it should always have a value after DB fetch. The guard is harmless but technically unnecessary. Not a blocker.Tests: Both tests are well-structured, use existing fixtures, and cover the acceptance criteria:
Nonefor nullable fields and"none"forjersey_order_statusAcceptance Criteria
jersey_option,jersey_size,jersey_number,jersey_order_statusVERDICT: APPROVED
PR #259 Review
DOMAIN REVIEW
Tech stack: Python / FastAPI / SQLAlchemy / Pydantic
Schema correctness: All 4 jersey fields added to
PlayerProfileResponsewithstr | Nonetype annotations. This matches the existing pattern used bydivision(enum serialized to string, nullable). Consistent with how the codebase handles enum-backed optional fields.Helper mapping correctness:
_player_profile_response()correctly maps all 4 fields:jersey_optionandjersey_sizeuse.valuewith null guard -- matches thedivisionpattern on line 116.jersey_numberis passed through directly (plainString(2)column, no enum) -- correct.jersey_order_statususes.valuewith null guard. The model declares this asMapped[JerseyOrderStatus](non-optional) withserver_default="none", so the null branch should never execute for database-persisted rows. The defensive guard is fine.Test coverage: Two tests added:
test_player_profile_includes_jersey_fields-- sets all 4 fields onsample_player, verifies correct enum-to-string serialization in the response. Good.test_player_profile_jersey_fields_null_when_empty-- uses the default fixture (no jersey data), verifiesNonefor nullable fields and"none"for the server-defaultedjersey_order_status. Good.Both happy path and null/default edge cases are covered.
PEP compliance: Type hints present. The multi-line conditional for
jersey_order_statusfollows ruff formatting conventions (confirmed by PR body: ruff format + ruff check clean).BLOCKERS
None.
NITS
jersey_order_status: str | Nonein the schema is more permissive than the model guarantees (Mapped[JerseyOrderStatus]with server_default, never null in practice). Considerjersey_order_status: str(non-optional) for tighter API contract accuracy. Not blocking -- the current approach is defensively safe and matches the helper's null guard.SOP COMPLIANCE
257-jersey-profile-fields)PROCESS OBSERVATIONS
Clean, minimal PR. 49 lines added across 2 files. Follows established patterns exactly (mirrors
divisionfield handling). No scope creep. Good cross-repo traceability to the frontend consumer (westside-landing#197) and the admin-side precedent (#248/#249).VERDICT: APPROVED