Skip to content

Commit

Permalink
🐛 Add missing Targets to ui.target.order after seeding (#536)
Browse files Browse the repository at this point in the history
This also adds some convenience methods to the `Setting` model to make
reading and updating their values somewhat less tedious.

Fixes #505

---------

Signed-off-by: Sam Lucidi <[email protected]>
  • Loading branch information
mansam authored Oct 26, 2023
1 parent 98f338c commit 8389678
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 5 deletions.
21 changes: 21 additions & 0 deletions migration/v10/model/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down
81 changes: 76 additions & 5 deletions seed/target.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package seed

import (
"container/list"
"encoding/json"
"errors"
"fmt"
Expand All @@ -10,6 +11,8 @@ import (
"gorm.io/gorm"
)

const UITargetOrder = "ui.target.order"

//
// Target applies Target seeds.
type Target struct {
Expand All @@ -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)
Expand Down Expand Up @@ -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)

Check failure on line 131 in seed/target.go

View workflow job for this annotation

GitHub Actions / test-api

s.As undefined (type "github.com/konveyor/tackle2-hub/migration/v11/model".Setting has no field or method As)

Check failure on line 131 in seed/target.go

View workflow job for this annotation

GitHub Actions / test-unit

s.As undefined (type "github.com/konveyor/tackle2-hub/migration/v11/model".Setting has no field or method As)
_ = s.With(merge(userOrder, seedIds, targetIds))

Check failure on line 132 in seed/target.go

View workflow job for this annotation

GitHub Actions / test-api

s.With undefined (type "github.com/konveyor/tackle2-hub/migration/v11/model".Setting has no field or method With)

Check failure on line 132 in seed/target.go

View workflow job for this annotation

GitHub Actions / test-unit

s.With undefined (type "github.com/konveyor/tackle2-hub/migration/v11/model".Setting has no field or method With)

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
}
Expand Down
24 changes: 24 additions & 0 deletions seed/target_test.go
Original file line number Diff line number Diff line change
@@ -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))
}

0 comments on commit 8389678

Please sign in to comment.