diff --git a/cmd/status.go b/cmd/status.go index 146c48c..68ce8de 100644 --- a/cmd/status.go +++ b/cmd/status.go @@ -3,7 +3,6 @@ package cmd import ( - "context" "encoding/json" "fmt" @@ -13,12 +12,6 @@ import ( "github.com/spf13/cobra" ) -type statusLine struct { - Schema string - Version string - Status string -} - var statusCmd = &cobra.Command{ Use: "status", Short: "Show pgroll status", @@ -30,12 +23,12 @@ var statusCmd = &cobra.Command{ } defer state.Close() - statusLine, err := statusForSchema(ctx, state, flags.Schema()) + status, err := state.Status(ctx, flags.Schema()) if err != nil { return err } - statusJSON, err := json.MarshalIndent(statusLine, "", " ") + statusJSON, err := json.MarshalIndent(status, "", " ") if err != nil { return err } @@ -44,33 +37,3 @@ var statusCmd = &cobra.Command{ return nil }, } - -func statusForSchema(ctx context.Context, st *state.State, schema string) (*statusLine, error) { - latestVersion, err := st.LatestVersion(ctx, schema) - if err != nil { - return nil, err - } - if latestVersion == nil { - latestVersion = new(string) - } - - isActive, err := st.IsActiveMigrationPeriod(ctx, schema) - if err != nil { - return nil, err - } - - var status string - if *latestVersion == "" { - status = "No migrations" - } else if isActive { - status = "In Progress" - } else { - status = "Complete" - } - - return &statusLine{ - Schema: schema, - Version: *latestVersion, - Status: status, - }, nil -} diff --git a/pkg/roll/execute_test.go b/pkg/roll/execute_test.go index 93dcdd5..57bdbc2 100644 --- a/pkg/roll/execute_test.go +++ b/pkg/roll/execute_test.go @@ -389,6 +389,78 @@ func TestViewsAreCreatedWithSecurityInvokerTrue(t *testing.T) { }) } +func TestStatusMethodReturnsCorrectStatus(t *testing.T) { + t.Parallel() + + withMigratorAndConnectionToContainer(t, func(mig *roll.Roll, db *sql.DB) { + ctx := context.Background() + + // Get the initial migration status before any migrations are run + status, err := mig.Status(ctx, "public") + assert.NoError(t, err) + + // Ensure that the status shows "No migrations" + assert.Equal(t, &state.Status{ + Schema: "public", + Version: "", + Status: state.NoneMigrationStatus, + }, status) + + // Start a migration + err = mig.Start(ctx, &migrations.Migration{ + Name: "01_create_table", + Operations: []migrations.Operation{createTableOp("table1")}, + }) + assert.NoError(t, err) + + // Get the migration status + status, err = mig.Status(ctx, "public") + assert.NoError(t, err) + + // Ensure that the status shows "In progress" + assert.Equal(t, &state.Status{ + Schema: "public", + Version: "01_create_table", + Status: state.InProgressMigrationStatus, + }, status) + + // Rollback the migration + err = mig.Rollback(ctx) + assert.NoError(t, err) + + // Get the migration status + status, err = mig.Status(ctx, "public") + assert.NoError(t, err) + + // Ensure that the status shows "No migrations" + assert.Equal(t, &state.Status{ + Schema: "public", + Version: "", + Status: state.NoneMigrationStatus, + }, status) + + // Start and complete a migration + err = mig.Start(ctx, &migrations.Migration{ + Name: "01_create_table", + Operations: []migrations.Operation{createTableOp("table1")}, + }) + assert.NoError(t, err) + err = mig.Complete(ctx) + assert.NoError(t, err) + + // Get the migration status + status, err = mig.Status(ctx, "public") + assert.NoError(t, err) + + // Ensure that the status shows "Complete" + assert.Equal(t, &state.Status{ + Schema: "public", + Version: "01_create_table", + Status: state.CompleteMigrationStatus, + }, status) + }) +} + func createTableOp(tableName string) *migrations.OpCreateTable { return &migrations.OpCreateTable{ Name: tableName, diff --git a/pkg/roll/roll.go b/pkg/roll/roll.go index bd7ce61..30a200e 100644 --- a/pkg/roll/roll.go +++ b/pkg/roll/roll.go @@ -75,6 +75,10 @@ func (m *Roll) PGVersion() PGVersion { return m.pgVersion } +func (m *Roll) Status(ctx context.Context, schema string) (*state.Status, error) { + return m.state.Status(ctx, schema) +} + func (m *Roll) Close() error { err := m.state.Close() if err != nil { diff --git a/pkg/state/state.go b/pkg/state/state.go index 5736f91..bf50729 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -334,6 +334,37 @@ func (s *State) PreviousVersion(ctx context.Context, schema string) (*string, er return parent, nil } +// Status returns the current migration status of the specified schema +func (s *State) Status(ctx context.Context, schema string) (*Status, error) { + latestVersion, err := s.LatestVersion(ctx, schema) + if err != nil { + return nil, err + } + if latestVersion == nil { + latestVersion = new(string) + } + + isActive, err := s.IsActiveMigrationPeriod(ctx, schema) + if err != nil { + return nil, err + } + + var status MigrationStatus + if *latestVersion == "" { + status = NoneMigrationStatus + } else if isActive { + status = InProgressMigrationStatus + } else { + status = CompleteMigrationStatus + } + + return &Status{ + Schema: schema, + Version: *latestVersion, + Status: status, + }, nil +} + // ReadSchema reads & returns the current schema from postgres func ReadSchema(ctx context.Context, conn *sql.DB, stateSchema, schemaname string) (*schema.Schema, error) { var res schema.Schema diff --git a/pkg/state/status.go b/pkg/state/status.go new file mode 100644 index 0000000..cb77b59 --- /dev/null +++ b/pkg/state/status.go @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 + +package state + +type MigrationStatus string + +const ( + NoneMigrationStatus MigrationStatus = "No migrations" + InProgressMigrationStatus MigrationStatus = "In progress" + CompleteMigrationStatus MigrationStatus = "Complete" +) + +// Status describes the current migration status of a database schema. +type Status struct { + // The schema name. + Schema string `json:"schema"` + + // The name of the latest version schema. + Version string `json:"version"` + + // The status of the most recent migration. + Status MigrationStatus `json:"status"` +}