POST /api/jersey-public-orders — public submission endpoint #430
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#430
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
Type
Feature
Lineage
Depends on
basketball-api#429(migration 031). Part of System B production rollout. Architecture inarch-jersey-intake. Revised 2026-04-10 (three times): Keycloak-gated perfeedback_funnel_requires_auth.md; migration number corrected to 031 (head is 030); auth dependency corrected from non-existentkeycloak_userto realget_current_userfrombasketball_api.auth; dropped non-existentschemas/directory target.Repo
forgejo_admin/basketball-apiUser Story
As a signed-in player or parent (via westside-basketball Keycloak realm)
I want a POST endpoint accepting my jersey intake submission
So that my order lands in
jersey_public_orderswith my verified Keycloak identity attachedContext
Verified 2026-04-10 against live basketball-api source:
get_current_userlives insrc/basketball_api/auth.pylines 77–159. Returns aUserdataclass with.sub,.email,.username,.roles. Validates RS256 via JWKS against thewestside-basketballrealm.get_current_user:routes/subscriptions.py(line 367:user: User = Depends(get_current_user)),routes/account.py,routes/players.py,routes/teams.py,routes/tryouts.py,routes/upload.py,routes/coaches_api.pyroutes/checkout.pyis NOT a valid reference for Keycloak user auth — it only usesget_dbandrequire_admin. Ignore earlier versions of this ticket that pointed at it.schemas/directory exists in basketball-api. Seeroutes/checkout.pylines 38–63 orroutes/jersey.pyfor inline convention.routes/__init__.pyis empty and unused; routers are imported directly inmain.py. Do NOT modifyroutes/__init__.py.030_add_registration_type_to_registrations.py.#429creates migration 031. This ticket depends on 031 merging first.File Targets
Files to create:
src/basketball_api/routes/jersey_public.py— new route module with POST endpoint, Pydantic request/response schemas declared inline at the top of the file (matchroutes/subscriptions.pystyle)Files to modify:
src/basketball_api/main.py— register the new router (match the existing router registration pattern near lines 67–94)Files the agent should NOT touch:
routes/checkout.py— System C, irrelevant to this ticketroutes/jersey.py— System A, hands offroutes/__init__.py— empty and unusedsrc/basketball_api/models.py— already modified by #429 to addJerseyPublicOrder; do not touchsrc/basketball_api/auth.py— read-only referenceEndpoint spec
Note: no 403 path on this endpoint — it's authenticated-but-not-admin-gated. Any valid user of the
westside-basketballrealm may POST.Keycloak integration
from basketball_api.auth import User, get_current_userasync def create_jersey_public_order(..., user: User = Depends(get_current_user), db: Session = Depends(get_db))user.suband insert intojersey_public_orders.submitter_keycloak_subemailandplayer_namein the request body are user-editable — do NOT override withuser.email/user.username(parent may submit for child)Pydantic schemas (inline, in routes/jersey_public.py)
Acceptance Criteria
Authorizationheader → 401id,status='pending',created_atsubmitter_keycloak_subequal touser.subfrom the JWTplayer_nameandemailstored as request-body values (NOT overridden withuser.username/user.email)kqortier→ 400preferred_number_Npattern → 400submission_ippopulated fromX-Forwarded-For(first entry) orrequest.client.hostwestsidekingsandqueens.tail5b443a.ts.netmain.pywith prefix/api/jersey-public-ordersroutes/jersey_public.py(no newschemas/directory)Test Expectations
submitter_keycloak_submatches mockeduser.subget_current_userpytest tests/ -k jersey_publicConstraints
get_current_user— do NOT roll new authroutes/subscriptions.pyandroutes/checkout.pysrc/basketball_api/schemas/— no such directory existsroutes/__init__.py— it's unusedidfor frontend confirmationget_current_user— no real Keycloak in CIChecklist
basketball-apimainget_current_userfeedback_funnel_requires_auth.mdRelated
westside-basketball— projectstory:WS-S31— admin public jersey intake linkarch-jersey-intake— architecture docfeedback_funnel_requires_auth.md— why this is Keycloak-gatedbasketball-api#429(migration 031)routes/subscriptions.pyline 367 forget_current_userusage patternsrc/basketball_api/auth.pylines 77–159 for the auth primitiveScope Review: NEEDS_REFINEMENT
Review note:
review-948-2026-04-10Architectural intent is sound (Keycloak-authenticated intake, persist JWT sub as submitter). Problems are in file/symbol references — an agent following the ticket literally will hit dead ends.
[BODY] fixes required:
keycloak_userdep does NOT exist in this repo.routes/checkout.pydoes NOT use any Keycloak user dep (onlyDepends(get_db)andDepends(require_admin)). The real primitive isget_current_userinsrc/basketball_api/auth.py(lines 77-159), returning aUserwith.sub. Useroutes/account.py/routes/players.py/routes/subscriptions.pyas the real reference pattern. Remove "System C pattern" framing.src/basketball_api/schemas/jersey_public.py— that directory does not exist and repo convention is inline Pydantic models per route (see checkout.py lines 38-63).routes/__init__.pymodification — file is empty and unused; main.py imports routers directly.014_add_password_reset_tokens.py). Repo is at 030. Next free slot is 031. Fix Lineage to reference the corrected number once T2 (#429) is refined.app.dependency_overrides[get_current_user]to inject a fakeUser" instead of the vaguer "mock JWT validation".[SCOPE] items:
arch-jersey-intakein pal-e-docs (referenced but search_notes empty).feedback_funnel_requires_authnote (cited as policy justification but not found).Full details in review note. Once [BODY] fixes land and [SCOPE] items are resolved or explicitly deferred, ready for next_up (after T2 also passes review).