From e09dfaa34255d64b92acd620bfa266ab07faf660 Mon Sep 17 00:00:00 2001 From: "Johann Kellerman kellerza@gmail.com" Date: Wed, 14 Jul 2021 10:19:04 +0200 Subject: [PATCH 1/2] merge --- utils/env.go | 55 +++++++++++++++++++--------- utils/env_test.go | 91 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 16 deletions(-) create mode 100644 utils/env_test.go diff --git a/utils/env.go b/utils/env.go index 5cadbb7c3..ce4912c1d 100644 --- a/utils/env.go +++ b/utils/env.go @@ -13,27 +13,50 @@ func ConvertEnvs(m map[string]string) []string { return s } -// mergeStringMaps merges map m1 into m2 and return a resulting map as a new map -// maps that are passed for merging will not be changed -func MergeStringMaps(m1, m2 map[string]string) map[string]string { - if m1 == nil { - return m2 - } - if m2 == nil { - return m1 - } - // make a copy of a map - m := make(map[string]string) - for k, v := range m1 { - m[k] = v +// merge all dictionaries and return a new dictionary +// recursively if matching keys are both dictionaries +func MergeDicts(dicts ...map[string]interface{}) map[string]interface{} { + res := make(map[string]interface{}) + for _, m := range dicts { + if m == nil { + continue + } + for k, v := range m { + + if v0, ok := res[k]; ok { + // Recursive merging if res[k] exists (and both are dicts) + t0, ok0 := v0.(map[string]interface{}) + t1, ok1 := v.(map[string]interface{}) + if ok0 && ok1 { + res[k] = MergeDicts(t0, t1) + continue + } + } + res[k] = v + } } + return res +} - for k, v := range m2 { - m[k] = v +// merge all string maps and return a new map +// maps that are passed for merging will not be changed +func MergeStringMaps(maps ...map[string]string) map[string]string { + res := make(map[string]string) + for _, m := range maps { + if m == nil { + continue + } + for k, v := range m { + res[k] = v + } + } + if len(res) == 0 { + return nil } - return m + return res } +// does a slice contain a string func StringInSlice(slice []string, val string) (int, bool) { for i, item := range slice { if item == val { diff --git a/utils/env_test.go b/utils/env_test.go new file mode 100644 index 000000000..241a7fe24 --- /dev/null +++ b/utils/env_test.go @@ -0,0 +1,91 @@ +package utils + +import ( + "fmt" + "testing" +) + +func TestMergeDicts(t *testing.T) { + MergeDicts(nil, nil) + d1 := map[string]interface{}{ + "t": "1", + } + d2 := map[string]interface{}{ + "t": "2", + "t2": "1", + } + expect := func(m1, m2 map[string]interface{}, v ...string) { + d := MergeDicts(m1, m2) + for i := 0; i < len(v)/2; i++ { + if fmt.Sprintf("%v", d[v[i*2]]) != v[i*2+1] { + t.Errorf("err %v, expected %s", d, v) + } + } + } + expect(nil, d1, "t", "1") + expect(d1, d1, "t", "1") + expect(d1, nil, "t", "1") + expect(d2, d1, "t", "1", "t2", "1") + expect(d1, d2, "t", "2", "t2", "1") + expect(nil, d2, "t", "2", "t2", "1") +} + +func TestMergeDictsRecursive(t *testing.T) { + d0 := map[string]interface{}{ + "a": "1", + } + d1 := map[string]interface{}{ + "a": "11", + "b": "2", + } + r0 := map[string]interface{}{ + "r": d0, + "r1": "1", + } + r1 := map[string]interface{}{ + "r": d1, + "r2": "2", + } + r3 := map[string]interface{}{ + "r": "00", + "r2": "0", + } + + expect := func(m1, m2 map[string]interface{}, v string) { + d := MergeDicts(m1, m2) + if fmt.Sprintf("%v", d) != v { + t.Errorf("err %v, expected %s", d, v) + } + } + // all simple vars... second overwrites + expect(d1, d0, "map[a:1 b:2]") + expect(d0, d1, "map[a:11 b:2]") + + // r are both dicts... recursive on r... same inner result as the previous + expect(r1, r0, "map[r:map[a:1 b:2] r1:1 r2:2]") + expect(r0, r1, "map[r:map[a:11 b:2] r1:1 r2:2]") + + // one is NOT a dict... second overwrites + expect(r1, r3, "map[r:00 r2:0]") + expect(r3, r1, "map[r:map[a:11 b:2] r2:2]") +} + +func TestMergeStringMaps(t *testing.T) { + d0 := map[string]string{ + "a": "1", + } + d1 := map[string]string{ + "a": "11", + "b": "2", + } + + expect := func(m1, m2 map[string]string, v string) { + d := MergeStringMaps(m1, m2) + if fmt.Sprintf("%v", d) != v { + t.Errorf("err %v, expected %s", d, v) + } + } + // all simple vars... second overwrites + expect(d1, d0, "map[a:1 b:2]") + expect(d0, d1, "map[a:11 b:2]") +} From 19e21de7a0d8a504bf8054ae3b57e27149029254 Mon Sep 17 00:00:00 2001 From: "Johann Kellerman kellerza@gmail.com" Date: Wed, 14 Jul 2021 13:18:30 +0200 Subject: [PATCH 2/2] feedback --- utils/env.go | 22 ++++++++++++++---- utils/env_test.go | 59 +++++++++++++++++++++++++---------------------- 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/utils/env.go b/utils/env.go index ce4912c1d..1e7fa8dc1 100644 --- a/utils/env.go +++ b/utils/env.go @@ -4,6 +4,8 @@ package utils +import "reflect" + // convertEnvs convert env variables passed as a map to a list of them func ConvertEnvs(m map[string]string) []string { s := make([]string, 0, len(m)) @@ -13,9 +15,21 @@ func ConvertEnvs(m map[string]string) []string { return s } +func mapify(i interface{}) (map[string]interface{}, bool) { + value := reflect.ValueOf(i) + if value.Kind() == reflect.Map { + m := map[string]interface{}{} + for _, k := range value.MapKeys() { + m[k.String()] = value.MapIndex(k).Interface() + } + return m, true + } + return map[string]interface{}{}, false +} + // merge all dictionaries and return a new dictionary // recursively if matching keys are both dictionaries -func MergeDicts(dicts ...map[string]interface{}) map[string]interface{} { +func MergeMaps(dicts ...map[string]interface{}) map[string]interface{} { res := make(map[string]interface{}) for _, m := range dicts { if m == nil { @@ -25,10 +39,10 @@ func MergeDicts(dicts ...map[string]interface{}) map[string]interface{} { if v0, ok := res[k]; ok { // Recursive merging if res[k] exists (and both are dicts) - t0, ok0 := v0.(map[string]interface{}) - t1, ok1 := v.(map[string]interface{}) + t0, ok0 := mapify(v0) + t1, ok1 := mapify(v) if ok0 && ok1 { - res[k] = MergeDicts(t0, t1) + res[k] = MergeMaps(t0, t1) continue } } diff --git a/utils/env_test.go b/utils/env_test.go index 241a7fe24..5810d5be9 100644 --- a/utils/env_test.go +++ b/utils/env_test.go @@ -3,10 +3,19 @@ package utils import ( "fmt" "testing" + + "github.com/google/go-cmp/cmp" ) -func TestMergeDicts(t *testing.T) { - MergeDicts(nil, nil) +func expectMaps(t *testing.T, v map[string]interface{}, m ...map[string]interface{}) { + d := MergeMaps(m...) + if !cmp.Equal(d, v) { + t.Errorf("err %v, expected %v", d, v) + } +} + +func TestMergeMaps(t *testing.T) { + MergeMaps(nil, nil) d1 := map[string]interface{}{ "t": "1", } @@ -14,23 +23,17 @@ func TestMergeDicts(t *testing.T) { "t": "2", "t2": "1", } - expect := func(m1, m2 map[string]interface{}, v ...string) { - d := MergeDicts(m1, m2) - for i := 0; i < len(v)/2; i++ { - if fmt.Sprintf("%v", d[v[i*2]]) != v[i*2+1] { - t.Errorf("err %v, expected %s", d, v) - } - } - } - expect(nil, d1, "t", "1") - expect(d1, d1, "t", "1") - expect(d1, nil, "t", "1") - expect(d2, d1, "t", "1", "t2", "1") - expect(d1, d2, "t", "2", "t2", "1") - expect(nil, d2, "t", "2", "t2", "1") + expectMaps(t, d1, nil, d1) + expectMaps(t, d1, d1, d1) + expectMaps(t, d1, d1, nil) + expectMaps(t, d2, d1, d2) + expectMaps(t, map[string]interface{}{ + "t": "1", + "t2": "1", + }, d2, d1) } -func TestMergeDictsRecursive(t *testing.T) { +func TestMergeMapsRecursive(t *testing.T) { d0 := map[string]interface{}{ "a": "1", } @@ -51,23 +54,23 @@ func TestMergeDictsRecursive(t *testing.T) { "r2": "0", } - expect := func(m1, m2 map[string]interface{}, v string) { - d := MergeDicts(m1, m2) - if fmt.Sprintf("%v", d) != v { - t.Errorf("err %v, expected %s", d, v) - } + exp0 := map[string]interface{}{ + "a": "1", + "b": "2", } + exp1 := d1 + // all simple vars... second overwrites - expect(d1, d0, "map[a:1 b:2]") - expect(d0, d1, "map[a:11 b:2]") + expectMaps(t, exp0, d1, d0) + expectMaps(t, exp1, d0, d1) // r are both dicts... recursive on r... same inner result as the previous - expect(r1, r0, "map[r:map[a:1 b:2] r1:1 r2:2]") - expect(r0, r1, "map[r:map[a:11 b:2] r1:1 r2:2]") + expectMaps(t, map[string]interface{}{"r": exp0, "r1": "1", "r2": "2"}, r1, r0) + expectMaps(t, map[string]interface{}{"r": exp1, "r1": "1", "r2": "2"}, r0, r1) // one is NOT a dict... second overwrites - expect(r1, r3, "map[r:00 r2:0]") - expect(r3, r1, "map[r:map[a:11 b:2] r2:2]") + expectMaps(t, map[string]interface{}{"r": "00", "r2": "0"}, r1, r3) + expectMaps(t, map[string]interface{}{"r": exp1, "r2": "2"}, r3, r1) } func TestMergeStringMaps(t *testing.T) {