Add SQLAlchemy models, Alembic migrations, and DB-backed health check #2
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?
Lineage
plan-mcd-tracker→ Phase 3: Data Model + PostgresRepo
forgejo_admin/mcd-tracker-apiUser Story
As a developer
I want the domain model and database layer in place
So that API endpoints (Phase 5) can persist and query coupon usage data.
Architecture
arch-domain-mcd-tracker— this issue implements the entity model exactly as designed. Two entities: Location and CouponUsage. No separate User table — identity from Keycloak JWTsubclaim stored askeycloak_substring.WHERE expires_at > NOW()counts active codes.expires_at = used_at + 30 days.Context
Phase 2 completed: FastAPI scaffold is live at
https://mcd-tracker.tail5b443a.ts.net/healthz. Postgres 16-alpine pod is already running in themcd-trackernamespace (deployed in Phase 1). The database password is in k8s secretmcd-tracker-secretskeypostgres-password. The env varMCD_TRACKER_DATABASE_URLis already wired in the kustomize deployment patch.Connection string pattern:
postgresql://mcd_tracker:$(POSTGRES_PASSWORD)@postgres:5432/mcd_trackerFollow the basketball-api pattern at
~/basketball-api/src/basketball_api/for database.py, models.py, and alembic setup.File Targets
Files to create:
src/mcd_tracker_api/database.py— SQLAlchemy engine fromsettings.database_url, SessionLocal factory, Base declarative base,get_db()FastAPI dependencysrc/mcd_tracker_api/models.py— two models:Location: id (PK), keycloak_sub (String, indexed), name (String), address (String), city (String), state (String, 2-char), latitude (Float, nullable), longitude (Float, nullable), created_at (DateTime, server_default=now)CouponUsage: id (PK), location_id (FK → location.id), keycloak_sub (String, indexed), code (String), used_at (DateTime, server_default=now), redeemed (Boolean, default=False), redeemed_at (DateTime, nullable), expires_at (DateTime — computed as used_at + 30 days, set in application logic or column default)alembic/— standard alembic init with env.py configured to usesettings.database_urland import models for autogeneratealembic.ini— standard config pointing toalembic/alembic/versions/001_initial_schema.py— autogenerated migration creating both tablestests/test_models.py— test that models create tables in a test DBFiles to modify:
src/mcd_tracker_api/main.py— addalembic.command.upgrade(config, "head")in lifespan (run migrations on startup)src/mcd_tracker_api/routes/health.py— add DB connectivity check (SELECT 1via get_db)pyproject.toml— add dependencies: sqlalchemy, psycopg2-binary, alembicsrc/mcd_tracker_api/config.py— ensuredatabase_urlfield exists (it should from Phase 2)Files NOT to touch:
.woodpecker.yaml— CI pipeline unchangedDockerfile— no changes neededAcceptance Criteria
LocationandCouponUsageSQLAlchemy models matcharch-domain-mcd-trackerentity diagramCouponUsage.expires_atcomputed asused_at + timedelta(days=30)keycloak_subindexed on both tables (query performance for per-user lookups)location_idFK on CouponUsage with cascade delete/healthzreturns DB connection status (e.g.,{"status": "ok", "db": "connected"})/healthzreturns degraded status if DB is unreachableTest Expectations
test_models.py— create Location, create CouponUsage linked to it, query back, verify fieldstest_health.py— health endpoint includes DB statustest_models.py— verify expires_at is 30 days after used_atpytest tests/ -vConstraints
postgresql.ENUMif needed (notsa.Enum) — avoids non-idempotentCREATE TYPEissues (lesson from basketball-api migration 008)expires_atshould be set in application logic (default in model or pre-insert hook), NOT a Postgres generated column — keeps the logic portable and testabledatabase.pypattern exactly for engine/session setupChecklist
Closes #2Related
project-mcd-tracker— project pagephase-mcd-tracker-3-data-model— phase notearch-domain-mcd-tracker— entity model being implemented