[#586] fixed nested multiple expands with shared path

This commit is contained in:
Gani Georgiev
2022-12-20 11:07:16 +02:00
parent ca528cef03
commit fb57c8091d
34 changed files with 216 additions and 41 deletions
+66
View File
@@ -132,6 +132,72 @@ func (m *Record) SetExpand(expand map[string]any) {
m.expand = shallowCopy(expand)
}
// MergeExpand merges recursively the provided expand data into
// the current model's expand (if any).
//
// Note that if an expanded prop with the same key is a slice (old or new expand)
// then both old and new records will be merged into a new slice (aka. a :merge: [b,c] => [a,b,c]).
// Otherwise the "old" expanded record will be replace with the "new" one (aka. a :merge: aNew => aNew).
func (m *Record) MergeExpand(expand map[string]any) {
for key, new := range expand {
old, ok := m.expand[key]
if !ok {
m.expand[key] = new
continue
}
var wasOldSlice bool
var oldSlice []*Record
switch v := old.(type) {
case *Record:
oldSlice = []*Record{v}
case []*Record:
wasOldSlice = true
oldSlice = v
default:
// invalid old expand data -> assign directly the new
// (no matter whether new is valid or not)
m.expand[key] = new
continue
}
var wasNewSlice bool
var newSlice []*Record
switch v := new.(type) {
case *Record:
newSlice = []*Record{v}
case []*Record:
wasNewSlice = true
newSlice = v
default:
// invalid new expand data -> skip
continue
}
oldIndexed := map[string]*Record{}
for _, oldRecord := range oldSlice {
oldIndexed[oldRecord.Id] = oldRecord
}
for _, newRecord := range newSlice {
oldRecord := oldIndexed[newRecord.Id]
if oldRecord != nil {
// note: there is no need to update oldSlice since oldRecord is a reference
oldRecord.MergeExpand(newRecord.Expand())
} else {
// missing new entry
oldSlice = append(oldSlice, newRecord)
}
}
if wasOldSlice || wasNewSlice {
m.expand[key] = oldSlice
} else {
m.expand[key] = oldSlice[0]
}
}
}
// SchemaData returns a shallow copy ONLY of the defined record schema fields data.
func (m *Record) SchemaData() map[string]any {
result := map[string]any{}
+93 -1
View File
@@ -358,7 +358,7 @@ func TestRecordOriginalCopy(t *testing.T) {
}
}
func TestRecordExpand(t *testing.T) {
func TestRecordSetAndGetExpand(t *testing.T) {
collection := &models.Collection{}
m := models.NewRecord(collection)
@@ -375,6 +375,98 @@ func TestRecordExpand(t *testing.T) {
}
}
func TestRecordMergeExpandDirect(t *testing.T) {
collection := &models.Collection{}
m := models.NewRecord(collection)
m.Id = "m"
// a
a := models.NewRecord(collection)
a.Id = "a"
a1 := models.NewRecord(collection)
a1.Id = "a1"
a2 := models.NewRecord(collection)
a2.Id = "a2"
a3 := models.NewRecord(collection)
a3.Id = "a3"
a31 := models.NewRecord(collection)
a31.Id = "a31"
a32 := models.NewRecord(collection)
a32.Id = "a32"
a.SetExpand(map[string]any{
"a1": a1,
"a23": []*models.Record{a2, a3},
})
a3.SetExpand(map[string]any{
"a31": a31,
"a32": []*models.Record{a32},
})
// b
b := models.NewRecord(collection)
b.Id = "b"
b1 := models.NewRecord(collection)
b1.Id = "b1"
b.SetExpand(map[string]any{
"b1": b1,
})
// c
c := models.NewRecord(collection)
c.Id = "c"
// load initial expand
m.SetExpand(map[string]any{
"a": a,
"b": b,
"c": []*models.Record{c},
})
// a (new)
aNew := models.NewRecord(collection)
aNew.Id = a.Id
a3New := models.NewRecord(collection)
a3New.Id = a3.Id
a32New := models.NewRecord(collection)
a32New.Id = "a32New"
a33New := models.NewRecord(collection)
a33New.Id = "a33New"
a3New.SetExpand(map[string]any{
"a32": []*models.Record{a32New},
"a33New": a33New,
})
aNew.SetExpand(map[string]any{
"a23": []*models.Record{a2, a3New},
})
// b (new)
bNew := models.NewRecord(collection)
bNew.Id = "bNew"
dNew := models.NewRecord(collection)
dNew.Id = "dNew"
// merge expands
m.MergeExpand(map[string]any{
"a": aNew,
"b": []*models.Record{bNew},
"dNew": dNew,
})
result := m.Expand()
raw, err := json.Marshal(result)
if err != nil {
t.Fatal(err)
}
rawStr := string(raw)
expected := `{"a":{"collectionId":"","collectionName":"","created":"","expand":{"a1":{"collectionId":"","collectionName":"","created":"","id":"a1","updated":""},"a23":[{"collectionId":"","collectionName":"","created":"","id":"a2","updated":""},{"collectionId":"","collectionName":"","created":"","expand":{"a31":{"collectionId":"","collectionName":"","created":"","id":"a31","updated":""},"a32":[{"collectionId":"","collectionName":"","created":"","id":"a32","updated":""},{"collectionId":"","collectionName":"","created":"","id":"a32New","updated":""}],"a33New":{"collectionId":"","collectionName":"","created":"","id":"a33New","updated":""}},"id":"a3","updated":""}]},"id":"a","updated":""},"b":[{"collectionId":"","collectionName":"","created":"","expand":{"b1":{"collectionId":"","collectionName":"","created":"","id":"b1","updated":""}},"id":"b","updated":""},{"collectionId":"","collectionName":"","created":"","id":"bNew","updated":""}],"c":[{"collectionId":"","collectionName":"","created":"","id":"c","updated":""}],"dNew":{"collectionId":"","collectionName":"","created":"","id":"dNew","updated":""}}`
if expected != rawStr {
t.Fatalf("Expected \n%v, \ngot \n%v", expected, rawStr)
}
}
func TestRecordSchemaData(t *testing.T) {
collection := &models.Collection{
Type: models.CollectionTypeAuth,