filter enhancements

This commit is contained in:
Gani Georgiev
2023-01-07 22:25:56 +02:00
parent d5775ff657
commit 9b880f5ab4
102 changed files with 3693 additions and 986 deletions
+59 -25
View File
@@ -2,6 +2,7 @@ package migrate
import (
"fmt"
"strings"
"time"
"github.com/AlecAivazis/survey/v2"
@@ -72,9 +73,19 @@ func (r *Runner) Run(args ...string) error {
}
}
names, err := r.lastAppliedMigrations(toRevertCount)
if err != nil {
color.Red(err.Error())
return err
}
confirm := false
prompt := &survey.Confirm{
Message: fmt.Sprintf("Do you really want to revert the last %d applied migration(s)?", toRevertCount),
Message: fmt.Sprintf(
"\n%v\nDo you really want to revert the last %d applied migration(s)?",
strings.Join(names, "\n"),
toRevertCount,
),
}
survey.AskOne(prompt, &confirm)
if !confirm {
@@ -138,38 +149,43 @@ func (r *Runner) Up() ([]string, error) {
return applied, nil
}
// Down reverts the last `toRevertCount` applied migrations.
// Down reverts the last `toRevertCount` applied migrations
// (in the order they were applied).
//
// On success returns list with the reverted migrations file names.
func (r *Runner) Down(toRevertCount int) ([]string, error) {
reverted := make([]string, 0, toRevertCount)
names, appliedErr := r.lastAppliedMigrations(toRevertCount)
if appliedErr != nil {
return nil, appliedErr
}
err := r.db.Transactional(func(tx *dbx.Tx) error {
for i := len(r.migrationsList.Items()) - 1; i >= 0; i-- {
m := r.migrationsList.Item(i)
// skip unapplied
if !r.isMigrationApplied(tx, m.File) {
continue
}
// revert limit reached
if toRevertCount-len(reverted) <= 0 {
break
}
// ignore empty Down action
if m.Down != nil {
if err := m.Down(tx); err != nil {
return fmt.Errorf("Failed to revert migration %s: %w", m.File, err)
for _, name := range names {
for _, m := range r.migrationsList.Items() {
if m.File != name {
continue
}
}
if err := r.saveRevertedMigration(tx, m.File); err != nil {
return fmt.Errorf("Failed to save reverted migration info for %s: %w", m.File, err)
}
// revert limit reached
if toRevertCount-len(reverted) <= 0 {
return nil
}
reverted = append(reverted, m.File)
// ignore empty Down action
if m.Down != nil {
if err := m.Down(tx); err != nil {
return fmt.Errorf("Failed to revert migration %s: %w", m.File, err)
}
}
if err := r.saveRevertedMigration(tx, m.File); err != nil {
return fmt.Errorf("Failed to save reverted migration info for %s: %w", m.File, err)
}
reverted = append(reverted, m.File)
}
}
return nil
@@ -178,6 +194,7 @@ func (r *Runner) Down(toRevertCount int) ([]string, error) {
if err != nil {
return nil, err
}
return reverted, nil
}
@@ -207,7 +224,7 @@ func (r *Runner) isMigrationApplied(tx dbx.Builder, file string) bool {
func (r *Runner) saveAppliedMigration(tx dbx.Builder, file string) error {
_, err := tx.Insert(r.tableName, dbx.Params{
"file": file,
"applied": time.Now().Unix(),
"applied": time.Now().UnixMicro(),
}).Execute()
return err
@@ -218,3 +235,20 @@ func (r *Runner) saveRevertedMigration(tx dbx.Builder, file string) error {
return err
}
func (r *Runner) lastAppliedMigrations(limit int) ([]string, error) {
var files = make([]string, 0, limit)
err := r.db.Select("file").
From(r.tableName).
Where(dbx.Not(dbx.HashExp{"applied": nil})).
OrderBy("applied DESC", "file DESC").
Limit(int64(limit)).
Column(&files)
if err != nil {
return nil, err
}
return files, nil
}
+56 -40
View File
@@ -3,6 +3,7 @@ package migrate
import (
"context"
"database/sql"
"encoding/json"
"testing"
"time"
@@ -52,73 +53,88 @@ func TestRunnerUpAndDown(t *testing.T) {
}
defer testDB.Close()
var test1UpCalled bool
var test1DownCalled bool
var test2UpCalled bool
var test2DownCalled bool
callsOrder := []string{}
l := MigrationsList{}
l.Register(func(db dbx.Builder) error {
test1UpCalled = true
callsOrder = append(callsOrder, "up2")
return nil
}, func(db dbx.Builder) error {
test1DownCalled = true
return nil
}, "1_test")
l.Register(func(db dbx.Builder) error {
test2UpCalled = true
return nil
}, func(db dbx.Builder) error {
test2DownCalled = true
callsOrder = append(callsOrder, "down2")
return nil
}, "2_test")
l.Register(func(db dbx.Builder) error {
callsOrder = append(callsOrder, "up3")
return nil
}, func(db dbx.Builder) error {
callsOrder = append(callsOrder, "down3")
return nil
}, "3_test")
l.Register(func(db dbx.Builder) error {
callsOrder = append(callsOrder, "up1")
return nil
}, func(db dbx.Builder) error {
callsOrder = append(callsOrder, "down1")
return nil
}, "1_test")
r, err := NewRunner(testDB.DB, l)
if err != nil {
t.Fatal(err)
}
// simulate partially run migration
r.saveAppliedMigration(testDB, r.migrationsList.Item(0).File)
// simulate partially out-of-order run migration
r.saveAppliedMigration(testDB, "2_test")
// ---------------------------------------------------------------
// Up()
// ---
// ---------------------------------------------------------------
if _, err := r.Up(); err != nil {
t.Fatal(err)
}
if test1UpCalled {
t.Fatalf("Didn't expect 1_test to be called")
}
expectedUpCallsOrder := `["up1","up3"]` // skip up2 since it was applied previously
if !test2UpCalled {
t.Fatalf("Expected 2_test to be called")
}
// simulate unrun migration
var test3DownCalled bool
r.migrationsList.Register(nil, func(db dbx.Builder) error {
test3DownCalled = true
return nil
}, "3_test")
// Down()
// ---
// revert one migration
if _, err := r.Down(1); err != nil {
upCallsOrder, err := json.Marshal(callsOrder)
if err != nil {
t.Fatal(err)
}
if test3DownCalled {
t.Fatal("Didn't expect 3_test to be reverted.")
if v := string(upCallsOrder); v != expectedUpCallsOrder {
t.Fatalf("Expected Up() calls order %s, got %s", expectedUpCallsOrder, upCallsOrder)
}
if !test2DownCalled {
t.Fatal("Expected 2_test to be reverted.")
// ---------------------------------------------------------------
// reset callsOrder
callsOrder = []string{}
// simulate unrun migration
r.migrationsList.Register(nil, func(db dbx.Builder) error {
callsOrder = append(callsOrder, "down4")
return nil
}, "4_test")
// ---------------------------------------------------------------
// ---------------------------------------------------------------
// Down()
// ---------------------------------------------------------------
if _, err := r.Down(2); err != nil {
t.Fatal(err)
}
if test1DownCalled {
t.Fatal("Didn't expect 1_test to be reverted.")
expectedDownCallsOrder := `["down3","down1"]` // revert in the applied order
downCallsOrder, err := json.Marshal(callsOrder)
if err != nil {
t.Fatal(err)
}
if v := string(downCallsOrder); v != expectedDownCallsOrder {
t.Fatalf("Expected Down() calls order %s, got %s", expectedDownCallsOrder, downCallsOrder)
}
}