From 838967893feb3215665ddb8932d0def3de1e2a63 Mon Sep 17 00:00:00 2001 From: Samuel Lucidi Date: Thu, 26 Oct 2023 10:02:15 -0400 Subject: [PATCH] :bug: Add missing Targets to ui.target.order after seeding (#536) This also adds some convenience methods to the `Setting` model to make reading and updating their values somewhat less tedious. Fixes https://github.com/konveyor/tackle2-hub/issues/505 --------- Signed-off-by: Sam Lucidi --- migration/v10/model/core.go | 21 ++++++++++ seed/target.go | 81 ++++++++++++++++++++++++++++++++++--- seed/target_test.go | 24 +++++++++++ 3 files changed, 121 insertions(+), 5 deletions(-) create mode 100644 seed/target_test.go diff --git a/migration/v10/model/core.go b/migration/v10/model/core.go index c18e5663e..fca0b85ab 100644 --- a/migration/v10/model/core.go +++ b/migration/v10/model/core.go @@ -27,6 +27,27 @@ type Setting struct { Value JSON `gorm:"type:json"` } +// +// With updates the value of the Setting with the json representation +// of the `value` parameter. +func (r *Setting) With(value interface{}) (err error) { + r.Value, err = json.Marshal(value) + if err != nil { + err = liberr.Wrap(err) + } + return +} + +// +// As unmarshalls the value of the Setting into the `ptr` parameter. +func (r *Setting) As(ptr interface{}) (err error) { + err = json.Unmarshal(r.Value, ptr) + if err != nil { + err = liberr.Wrap(err) + } + return +} + type Bucket struct { Model Path string `gorm:"<-:create;uniqueIndex"` diff --git a/seed/target.go b/seed/target.go index 6cb4e283f..d3deb2266 100644 --- a/seed/target.go +++ b/seed/target.go @@ -1,6 +1,7 @@ package seed import ( + "container/list" "encoding/json" "errors" "fmt" @@ -10,6 +11,8 @@ import ( "gorm.io/gorm" ) +const UITargetOrder = "ui.target.order" + // // Target applies Target seeds. type Target struct { @@ -34,7 +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)) - ids := []uint{} + var seedIds []uint for i := range r.targets { t := r.targets[i] target, found, fErr := r.find(db, "uuid = ?", t.UUID) @@ -93,16 +96,84 @@ func (r *Target) Apply(db *gorm.DB) (err error) { err = liberr.Wrap(result.Error) return } - ids = append(ids, target.ID) + seedIds = append(seedIds, target.ID) + } + + err = r.reorder(db, seedIds) + if err != nil { + return + } + return +} + +// +// 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, 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 } + userOrder := []uint{} + _ = s.As(&userOrder) + _ = s.With(merge(userOrder, seedIds, targetIds)) - value, _ := json.Marshal(ids) - uiOrder := model.Setting{Key: "ui.target.order", Value: value} - result := db.Where("key", "ui.target.order").Updates(uiOrder) + result = db.Where("key", UITargetOrder).Updates(s) if result.Error != nil { err = liberr.Wrap(err) return } + 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 } diff --git a/seed/target_test.go b/seed/target_test.go new file mode 100644 index 000000000..3a7681ae5 --- /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)) +}