feat(api): GET /graphs/{slug}/migrations endpoint (Phase 7 #59)#257
feat(api): GET /graphs/{slug}/migrations endpoint (Phase 7 #59)#257charlie83Gs merged 2 commits intomainfrom
Conversation
Ships the read-only half of the migration-audit API. Reads the graph_migration_runs table for this graph, returns rows newest-first as GraphMigrationRunResponse. Backs the frontend history table (#61) and gives operators a debug path without needing Hatchet UI access. Split from the re-migrate endpoint (also #59) because the re-migrate needs graph_migration_wf to exist (Phase 7 #57) to dispatch against; the list endpoint only needs the audit table (#6, already shipped). - Requires GRAPH_READ permission — history can leak when data transformations touched the graph. Read banner on UI relies on Graph.read_only_reason='migrating' separately; this endpoint is for history + debugging only. - Status is str, not Literal — new states added later (e.g. 'cancelled') should not require a coordinated API + frontend ship. DB-layer CHECK constraint stays the source of truth. - Empty list when graph has never migrated (brand-new graphs at v1, any graph on a plugin still at current_version=1). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… tests - order_by adds GraphMigrationRun.id.desc() as secondary sort so rows inserted in the same clock tick (batch migration enqueueing multiple pending rows) sort deterministically. Load-bearing for future cursor pagination. - ?limit query param clamps response size to keep long-lived graphs on many-version plugins from returning unbounded audit history. Default 100, server-side max 500 — beyond max returns 422. - 8 new HTTP-level integration tests in test_migrations_list_endpoint.py: 404 unknown slug, empty list for never-migrated graph, newest-first ordering, id-desc tie-break, cross-graph scoping (rows don't leak), limit clamps, limit=0 → 422, limit>max → 422. Mirror the fixture pattern from test_graph_read_only_endpoint.py. - Status comment dropped; docstring now points at kt_db.models.GraphMigrationRun for the authoritative status set + DB CHECK constraint so the enum doesn't drift between sites. - Schema tests: datetime import moved to module level (no more inline from datetime imports + __import__ hack). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Review fixes applied
Kept `graph_id` in the response: dropping it is technically payload-smaller but some frontend list consumers key by it when rendering multiple graphs' history side-by-side. Low-value drop; parking until a consumer actually needs the savings. Error-field scrub (re: internal paths/traceback leaks) remains a worker (#57) concern per your note. |
|
I have read the CLA Document and I hereby sign the CLA You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot. |
Summary
Ships the read-only half of the migration-audit API. Returns rows from `graph_migration_runs` for the graph, newest-first, as `GraphMigrationRunResponse`. Backs the Phase 7 frontend history table (#61) and gives operators a debug path without needing Hatchet UI access.
Split from the companion `POST /graphs/{slug}/re-migrate` (also part of #59) because re-migrate needs `graph_migration_wf` to exist (#57) to dispatch against; this list endpoint only depends on the audit table (#6, already shipped).
Contract
Schema design
`status` is `str`, not `Literal`. The DB column is `VARCHAR(16)` with a CHECK constraint at the model layer; validation lives there. New states added later (e.g. `'cancelled'`) won't require a coordinated API + frontend ship.
Test plan
🤖 Generated with Claude Code