diff --git a/seed/target.go b/seed/target.go index 1d77758b..e378d556 100644 --- a/seed/target.go +++ b/seed/target.go @@ -1,6 +1,7 @@ package seed import ( + "container/list" "encoding/json" "errors" "fmt" @@ -36,6 +37,7 @@ func (r *Target) With(seed libseed.Seed) (err error) { func (r *Target) Apply(db *gorm.DB) (err error) { log.Info("Applying Targets", "count", len(r.targets)) + var seedIds []uint for i := range r.targets { t := r.targets[i] target, found, fErr := r.find(db, "uuid = ?", t.UUID) @@ -93,9 +95,10 @@ func (r *Target) Apply(db *gorm.DB) (err error) { err = liberr.Wrap(result.Error) return } + seedIds = append(seedIds, target.ID) } - err = r.reorder(db) + err = r.reorder(db, seedIds) if err != nil { return } @@ -105,34 +108,28 @@ func (r *Target) Apply(db *gorm.DB) (err error) { // // reorder updates the value of the ui.target.order setting // to add any missing target ids. (namely, newly added targets.) -func (r *Target) reorder(db *gorm.DB) (err error) { +func (r *Target) reorder(db *gorm.DB, seedIds []uint) (err error) { targets := []model.Target{} result := db.Find(&targets) if result.Error != nil { err = liberr.Wrap(err) return } + var targetIds []uint + for _, t := range targets { + targetIds = append(targetIds, t.ID) + } + s := model.Setting{} result = db.First(&s, "key", UITargetOrder) if result.Error != nil { err = liberr.Wrap(err) return } - ordering := []uint{} - _ = s.As(&ordering) - known := make(map[uint]bool) - for _, id := range ordering { - known[id] = true - } - for _, t := range targets { - if !known[t.ID] { - ordering = append(ordering, t.ID) - } - } - err = s.With(ordering) - if err != nil { - return - } + userOrder := []uint{} + _ = s.As(&userOrder) + _ = s.With(merge(userOrder, seedIds, targetIds)) + result = db.Where("key", UITargetOrder).Updates(s) if result.Error != nil { err = liberr.Wrap(err) @@ -141,6 +138,45 @@ func (r *Target) reorder(db *gorm.DB) (err error) { return } +// +// merge new targets into the user's custom target order. +// params: +// userOrder: slice of target IDs in the user's desired order +// seedOrder: slice of target IDs in seedfile order +// ids: slice of ids of all the targets in the DB +func merge(userOrder []uint, seedOrder []uint, ids []uint) (mergedOrder []uint) { + ll := list.New() + known := make(map[uint]*list.Element) + for _, id := range userOrder { + known[id] = ll.PushBack(id) + } + for i, id := range seedOrder { + if _, found := known[id]; found { + continue + } + if i == 0 { + known[id] = ll.PushFront(id) + } else { + known[id] = ll.InsertAfter(id, known[seedOrder[i-1]]) + } + } + + for _, id := range ids { + if _, found := known[id]; found { + continue + } + ll.PushBack(id) + } + + for ll.Len() > 0 { + e := ll.Front() + mergedOrder = append(mergedOrder, e.Value.(uint)) + ll.Remove(e) + } + + return +} + // // Convenience method to find a Target. func (r *Target) find(db *gorm.DB, conditions ...interface{}) (t *model.Target, found bool, err error) { diff --git a/seed/target_test.go b/seed/target_test.go new file mode 100644 index 00000000..3a7681ae --- /dev/null +++ b/seed/target_test.go @@ -0,0 +1,24 @@ +package seed + +import ( + "github.com/onsi/gomega" + "testing" +) + +func TestMerge(t *testing.T) { + g := gomega.NewWithT(t) + + // the seed contains 10 targets in a given order, 3 of which are new + seedOrder := []uint{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + // the user has set up a custom order for the 7 targets that already exist in the db + userOrder := []uint{6, 9, 5, 4, 1, 3, 2} + // the DB in total has 13 targets including the 3 newly seeded ones and 3 that were pre-existing + // in the DB not not represented in the ordering due to a previous bug. + allIds := []uint{11, 12, 13, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + // we expect the newly added targets to be woven into the user's custom ordering, with any targets + // that had previously been dropped on the floor being added to the end of the ordering. + expectedOrder := []uint{6, 7, 8, 9, 10, 5, 4, 1, 3, 2, 11, 12, 13} + + mergedOrder := merge(userOrder, seedOrder, allIds) + g.Expect(mergedOrder).To(gomega.Equal(expectedOrder)) +}