infra: migration 048 — GRANT tournament tables to westside_streamlit_ro #510

Open
opened 2026-04-23 12:59:04 +00:00 by forgejo_admin · 1 comment

Type

Feature

Lineage

Standalone — discovered 2026-04-23. Extends migration 044's GRANT allowlist for the forthcoming Streamlit tournament section (forgejo_admin/westside-streamlit#15). Scope corrected after C3 review (2026-04-24): tournament payments flow through orders (already granted by 044), so only tournaments and tournament_products need to be added. registrations is NOT involved.

Repo

forgejo_admin/basketball-api

User Story

As the westside-streamlit app
I want SELECT permission on tournaments and tournament_products for the westside_streamlit_ro role
So that the dashboard can query "for tournament X, which tournament_products are listed and which orders paid for them" — joining through tables that are now all in the role's allowlist

Context

Migration 044_add_westside_streamlit_ro_role created the westside_streamlit_ro role and GRANTed SELECT on 6 tables: orders, parents, player_teams, players, products, teams. Migration 045_add_tournament_tables later added tournaments and tournament_products. Tournament payments are stored in the existing orders table (already granted) — orders.product_id joins to tournament_products.product_id, which joins to tournaments.id. Verified via models.py Order and TournamentProduct definitions and via PR review notes (review-994/998/1013) that confirm tournament checkout creates orders, not registrations. The registrations table is for tryouts/practices and is NOT needed for tournament visibility. Defense-in-depth principle holds: new tables not auto-exposed; this extends the allowlist deliberately and minimally. Alembic head is 047, next slot 048.

File Targets

Files to create:

  • alembic/versions/048_grant_tournament_tables_to_ro_role.py — new migration, revision 048, down_revision 047

Files NOT to touch:

  • Any schema-changing migrations (no tables, no columns)
  • Any model in src/basketball_api/models.py — this is ACL only
  • Migration 044 — don't amend; add a new forward migration

Acceptance Criteria

  • Revision is exactly "048", down_revision is exactly "047" (per feedback_migration_slot_coordination — slot collision = incident)
  • Role name is exactly westside_streamlit_ro (verify by reading migration 044 before writing 048)
  • upgrade(): op.execute("GRANT SELECT ON tournaments, tournament_products TO westside_streamlit_ro")two tables only, NOT registrations
  • downgrade(): op.execute("REVOKE SELECT ON tournaments, tournament_products FROM westside_streamlit_ro")
  • On a fresh local db: alembic upgrade head runs without error
  • alembic downgrade 047 cleanly reverts
  • Migration file style/header matches existing migrations (see migration 044 as the canonical reference for ACL-only migrations)

Test Expectations

  • Existing test suite continues to pass: pytest tests/ -x
  • Migration file passes project lint: ruff check alembic/versions/048_grant_tournament_tables_to_ro_role.py
  • Run commands:
    • alembic upgrade head
    • alembic downgrade 047 && alembic upgrade head
    • ruff check alembic/versions/048_grant_tournament_tables_to_ro_role.py
    • pytest tests/ -x

Constraints

  • Revision slot 048 — if another PR claims 048 first, rebase and renumber, don't collision.
  • Use op.execute(...) for the GRANT/REVOKE (match migration 044's SQL style).
  • Do NOT modify src/basketball_api/models.py.
  • Do NOT add schema changes; ACL only.
  • Grant exactly two tables: tournaments and tournament_products. Do NOT include registrations (not needed; tournament payments are in orders which is already granted).

Checklist

  • PR opened
  • Alembic upgrade/downgrade round-trip tested locally
  • ruff check clean
  • No unrelated changes
  • project-westside-streamlit — consumer project
  • story-westside-streamlit-tournament — the user story this serves
  • feedback_migration_slot_coordination — slot-collision rule
  • feedback_basketball_hands_off — why allowlist is narrow
  • Migration 044_add_westside_streamlit_ro_role — defines the role being extended
  • Migration 045_add_tournament_tables — source of the tables being GRANTed
  • Consumer ticket: forgejo_admin/westside-streamlit#15 (C3 — Tournament section)
### Type Feature ### Lineage Standalone — discovered 2026-04-23. Extends migration 044's GRANT allowlist for the forthcoming Streamlit tournament section (`forgejo_admin/westside-streamlit#15`). **Scope corrected after C3 review** (2026-04-24): tournament payments flow through `orders` (already granted by 044), so only `tournaments` and `tournament_products` need to be added. **`registrations` is NOT involved.** ### Repo `forgejo_admin/basketball-api` ### User Story As **the westside-streamlit app** I want **SELECT permission on `tournaments` and `tournament_products` for the `westside_streamlit_ro` role** So that **the dashboard can query "for tournament X, which `tournament_products` are listed and which `orders` paid for them" — joining through tables that are now all in the role's allowlist** ### Context Migration `044_add_westside_streamlit_ro_role` created the `westside_streamlit_ro` role and GRANTed SELECT on 6 tables: `orders, parents, player_teams, players, products, teams`. Migration `045_add_tournament_tables` later added `tournaments` and `tournament_products`. **Tournament payments are stored in the existing `orders` table (already granted) — `orders.product_id` joins to `tournament_products.product_id`, which joins to `tournaments.id`.** Verified via `models.py` Order and TournamentProduct definitions and via PR review notes (review-994/998/1013) that confirm tournament checkout creates `orders`, not registrations. The `registrations` table is for tryouts/practices and is NOT needed for tournament visibility. Defense-in-depth principle holds: new tables not auto-exposed; this extends the allowlist deliberately and minimally. Alembic head is `047`, next slot `048`. ### File Targets Files to create: - `alembic/versions/048_grant_tournament_tables_to_ro_role.py` — new migration, revision `048`, down_revision `047` Files NOT to touch: - Any schema-changing migrations (no tables, no columns) - Any model in `src/basketball_api/models.py` — this is ACL only - Migration `044` — don't amend; add a new forward migration ### Acceptance Criteria - [ ] Revision is exactly `"048"`, down_revision is exactly `"047"` (per `feedback_migration_slot_coordination` — slot collision = incident) - [ ] Role name is exactly `westside_streamlit_ro` (verify by reading migration 044 before writing 048) - [ ] `upgrade()`: `op.execute("GRANT SELECT ON tournaments, tournament_products TO westside_streamlit_ro")` — **two tables only, NOT registrations** - [ ] `downgrade()`: `op.execute("REVOKE SELECT ON tournaments, tournament_products FROM westside_streamlit_ro")` - [ ] On a fresh local db: `alembic upgrade head` runs without error - [ ] `alembic downgrade 047` cleanly reverts - [ ] Migration file style/header matches existing migrations (see migration 044 as the canonical reference for ACL-only migrations) ### Test Expectations - [ ] Existing test suite continues to pass: `pytest tests/ -x` - [ ] Migration file passes project lint: `ruff check alembic/versions/048_grant_tournament_tables_to_ro_role.py` - Run commands: - `alembic upgrade head` - `alembic downgrade 047 && alembic upgrade head` - `ruff check alembic/versions/048_grant_tournament_tables_to_ro_role.py` - `pytest tests/ -x` ### Constraints - Revision slot `048` — if another PR claims `048` first, **rebase and renumber**, don't collision. - Use `op.execute(...)` for the GRANT/REVOKE (match migration 044's SQL style). - Do NOT modify `src/basketball_api/models.py`. - Do NOT add schema changes; ACL only. - **Grant exactly two tables: `tournaments` and `tournament_products`. Do NOT include `registrations` (not needed; tournament payments are in `orders` which is already granted).** ### Checklist - [ ] PR opened - [ ] Alembic upgrade/downgrade round-trip tested locally - [ ] `ruff check` clean - [ ] No unrelated changes ### Related - `project-westside-streamlit` — consumer project - `story-westside-streamlit-tournament` — the user story this serves - `feedback_migration_slot_coordination` — slot-collision rule - `feedback_basketball_hands_off` — why allowlist is narrow - Migration `044_add_westside_streamlit_ro_role` — defines the role being extended - Migration `045_add_tournament_tables` — source of the tables being GRANTed - Consumer ticket: `forgejo_admin/westside-streamlit#15` (C3 — Tournament section)
forgejo_admin changed title from infra: migration 048 — GRANT tournament + registrations tables to westside_streamlit_ro to infra: migration 048 — GRANT tournament tables to westside_streamlit_ro 2026-04-25 02:15:21 +00:00
Author
Owner

Scope Review: APPROVED

Review note: review-1072-2026-04-22

Third review round; scope correction (drop registrations) verified against models.py and migration 044/045. File targets, traceability, and dependencies all check out. Ready to move backlog → todo.

Verified:

  • Migration slot 048 is free at HEAD (047_add_stripe_checkout_url_to_orders.py).
  • Tables tournaments + tournament_products defined in src/basketball_api/models.py (lines 596, 614).
  • registrations correctly excluded — bound to tryouts/practices, not tournament payments. Tournament payments flow through orders (already in 044's GRANT allowlist).
  • Story note story-westside-streamlit-tournament (id 1594) exists and documents the orders-based join chain.
  • Downstream westside-streamlit#15 (C3) explicitly blocked by this ticket — consistent.

Style heads-up (non-blocking): AC #3 specifies GRANT SELECT ON tournaments, tournament_products TO ... (comma-separated). Migration 044's actual style is a per-table loop with GRANT SELECT ON TABLE public.{table} TO {role};. Both forms are valid PostgreSQL — flagging so QA doesn't bounce on style during code review.

Non-blocking follow-ups for Ava:

  • [SCOPE] Add tournament row to the User Stories table on project-westside-streamlit (story note exists but isn't linked from the project page).
  • [SCOPE] Consider creating a standalone arch-postgres-role note to back the arch:postgres-role label.

Neither follow-up blocks ticket advancement.

## Scope Review: APPROVED Review note: `review-1072-2026-04-22` Third review round; scope correction (drop `registrations`) verified against `models.py` and migration 044/045. File targets, traceability, and dependencies all check out. Ready to move backlog → todo. **Verified:** - Migration slot 048 is free at HEAD (`047_add_stripe_checkout_url_to_orders.py`). - Tables `tournaments` + `tournament_products` defined in `src/basketball_api/models.py` (lines 596, 614). - `registrations` correctly excluded — bound to tryouts/practices, not tournament payments. Tournament payments flow through `orders` (already in 044's GRANT allowlist). - Story note `story-westside-streamlit-tournament` (id 1594) exists and documents the orders-based join chain. - Downstream `westside-streamlit#15` (C3) explicitly blocked by this ticket — consistent. **Style heads-up (non-blocking):** AC #3 specifies `GRANT SELECT ON tournaments, tournament_products TO ...` (comma-separated). Migration 044's actual style is a per-table loop with `GRANT SELECT ON TABLE public.{table} TO {role};`. Both forms are valid PostgreSQL — flagging so QA doesn't bounce on style during code review. **Non-blocking follow-ups for Ava:** - `[SCOPE]` Add `tournament` row to the User Stories table on `project-westside-streamlit` (story note exists but isn't linked from the project page). - `[SCOPE]` Consider creating a standalone `arch-postgres-role` note to back the `arch:postgres-role` label. Neither follow-up blocks ticket advancement.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
forgejo_admin/basketball-api#510
No description provided.