feat: flyway style migration - #3500
Open
ppatel9703 wants to merge 6 commits into
Open
Conversation
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
ppatel9703
marked this pull request as ready for review
August 3, 2026 12:46
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds a Flyway-style Migrations tab to the engine UI so admins can view migration history/status for a database engine and author + run new versioned SQL migrations without leaving SEMOSS. This is the UI counterpart to the migration engine added in SEMOSS/Semoss#2822 — see How It Ties Together below for the full request lifecycle across both PRs.
Changes Made
Tab gating (
engine-layout.tsx) — Adds a secondusePixel<boolean>call alongside the existinggetDatabaseCategoryone, scoped toDATABASEengines only:The empty-string query when not a DATABASE engine short-circuits the pixel call and defaults to
false. The existing tab-filter logic (already scoped toroute.type === "DATABASE") gains a third branch alongside the Query/SPARQL checks:if (t.path === "migrations") return migrationsEnabled === true;. The page's loading gate was extended to block on both pixel calls (getDatabaseCategory.status !== "SUCCESS" || getMigrationsEnabled.status !== "SUCCESS") before rendering, so the tab list doesn't flicker in before the flag resolves.engine.constants.ts— NewENGINE_ROUTESentry forMigrations(path: "migrations"), inserted aftersparql-query, withrestrict: ["READ_ONLY", "EDIT", "OWNER"]. Read-only users can see the tab (to view history); write actions are gated separately in the page component.Migrations page (
engine-migrations.page.tsx, refactored from an initialengine-migrations-page.tsx) — CallsListEngineMigrations(engine=["${active.id}"]);viausePixel. Handles loading (spinner), error (surfaces the pixel error message), and empty states ("No migrations found for this engine"). Table columns: Version, Description, File, State (badge), Applied By, Applied On, Execution Time (formatted asXmsunder 1s, elseX.XXs), and Notes (truncated error message). No optimistic updates — a manual Refresh button and the "New Migration" dialog's close callback both trigger a fulllistMigrations.refresh(). Adds acanEdit = active.role === "EDIT" || active.role === "OWNER"check (mirroring the backend'suserCanEditEngine) that gates both the "New Migration" button and whether the dialog is even mounted, so the UI never offers an action that would fail server-side.engine-migration-state-badge.tsx— Maps each of the five states to a badge:SUCCESSandFAILEDuse semanticBadgevariants (outline/destructive);OUTDATEDandMISSINGfall back to raw Tailwind color classes (amber/orange) since no semantic "warning" token exists yet in the design system — called out in-code as a known gap; default/PENDINGrenders an outline "Pending" badge.engine-migrations-summary.tsx— Computes and renders a 6-stat summary row above the table: Current Version (highest version amongSUCCESS/OUTDATEDrows, using acompareVersionshelper that numerically compares dotted segments — mirrors the Java-side version comparison exactly) plus counts of Applied, Pending, Failed, Outdated, and Missing rows. Only rendered when there's at least one migration.engine-new-migration-dialog.tsx— Form with Description and SQL (monospace textarea) fields. Client-side validation requires both non-empty before any network call, with an inline error toast otherwise. On submit, calls:and checks the pixel
errorsarray first, thenresult.success === false, throwing the returnederrorMessagein either failure case. On success: toast confirmingMigration V${version} applied successfully, form reset, andonClose(true)(parent refreshes the table). On failure: error toast, form state preserved so the user can fix and retry without retyping the SQL. Asubmittingflag disables both buttons and shows an inline spinner. Note: there is no confirmation step before running — "Save" writes the file and executes the SQL immediately, matching the backend's own immediate-execution behavior.index.ts— Barrel-exports the three new components (engine-migration-state-badge,engine-migrations-summary,engine-new-migration-dialog).How It Ties Together
GetEngineMigrationsEnabledis called → the Migrations tab only renders if the engine's smss hasENABLE_MIGRATIONS=true(set server-side, see the paired backend PR).ListEngineMigrationsis called → the backend'sMigrationStatusUtils.getStatus()reconciles the engine'sassets/.migrationsfolder against itsSEMOSS_SCHEMA_HISTORYtable and returns one row per migration version with a computed state.SaveEngineMigrationwrites the versioned file server-side, runs it immediately, and returns success/failure → this dialog surfaces that result via toast and the parent page re-fetches the list.MigrationStatetype (PENDING | SUCCESS | FAILED | MISSING | OUTDATED) is a literal mirror of the backend'sMigrationStatusstates — both sides comment the contract explicitly so the enums can't silently drift apart.open()); this UI is a read/author surface on top of that, not the only way migrations run.How to Test
ENABLE_MIGRATIONS=trueon a database engine's smss (requires the backend PR).EDIT/OWNERroles can author).SUCCESS.FAILEDstate/error toast, and that the form retains your input so you can correct and resubmit.OUTDATEDorMISSINGstate on the backend and confirm the badge/summary counts reflect it correctly.Notes
GetEngineMigrationsEnabled,ListEngineMigrations, andSaveEngineMigrationreactors this UI calls.OUTDATED/MISSINGstates, so those fall back to raw Tailwind colors instead of design-system tokens.