diff --git a/config/reader/json/values_test.go b/config/reader/json/values_test.go index 516199a6..97aec68a 100644 --- a/config/reader/json/values_test.go +++ b/config/reader/json/values_test.go @@ -1,39 +1,85 @@ package json import ( + "reflect" "testing" "github.com/micro/go-micro/config/source" ) func TestValues(t *testing.T) { - data := []byte(`{"foo": "bar", "baz": {"bar": "cat"}}`) - + emptyStr := "" testData := []struct { - path []string - value string + csdata []byte + path []string + accepter interface{} + value interface{} }{ { + []byte(`{"foo": "bar", "baz": {"bar": "cat"}}`), []string{"foo"}, + emptyStr, "bar", }, { + []byte(`{"foo": "bar", "baz": {"bar": "cat"}}`), []string{"baz", "bar"}, + emptyStr, "cat", }, } - values, err := newValues(&source.ChangeSet{ - Data: data, - }) + for idx, test := range testData { + values, err := newValues(&source.ChangeSet{ + Data: test.csdata, + }) + if err != nil { + t.Fatal(err) + } - if err != nil { - t.Fatal(err) - } - - for _, test := range testData { - if v := values.Get(test.path...).String(""); v != test.value { - t.Fatalf("Expected %s got %s for path %v", test.value, v, test.path) + err = values.Get(test.path...).Scan(&test.accepter) + if err != nil { + t.Fatal(err) + } + if test.accepter != test.value { + t.Fatalf("No.%d Expected %v got %v for path %v", idx, test.value, test.accepter, test.path) + } + } +} + +func TestStructArray(t *testing.T) { + type T struct { + Foo string + } + + emptyTSlice := []T{} + + testData := []struct { + csdata []byte + accepter []T + value []T + }{ + { + []byte(`[{"foo": "bar"}]`), + emptyTSlice, + []T{T{Foo: "bar"}}, + }, + } + + for idx, test := range testData { + values, err := newValues(&source.ChangeSet{ + Data: test.csdata, + }) + if err != nil { + t.Fatal(err) + } + + err = values.Get().Scan(&test.accepter) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(test.accepter, test.value) { + t.Fatalf("No.%d Expected %v got %v", idx, test.value, test.accepter) } } } diff --git a/config/source/consul/util.go b/config/source/consul/util.go index 80de3d3f..1deacbb4 100644 --- a/config/source/consul/util.go +++ b/config/source/consul/util.go @@ -8,40 +8,80 @@ import ( "github.com/micro/go-micro/config/encoder" ) +type configValue interface { + Value() interface{} + Decode(encoder.Encoder, []byte) error +} +type configArrayValue struct { + v []interface{} +} + +func (a *configArrayValue) Value() interface{} { return a.v } +func (a *configArrayValue) Decode(e encoder.Encoder, b []byte) error { + return e.Decode(b, &a.v) +} + +type configMapValue struct { + v map[string]interface{} +} + +func (m *configMapValue) Value() interface{} { return m.v } +func (m *configMapValue) Decode(e encoder.Encoder, b []byte) error { + return e.Decode(b, &m.v) +} + func makeMap(e encoder.Encoder, kv api.KVPairs, stripPrefix string) (map[string]interface{}, error) { + data := make(map[string]interface{}) // consul guarantees lexicographic order, so no need to sort for _, v := range kv { pathString := strings.TrimPrefix(strings.TrimPrefix(v.Key, strings.TrimPrefix(stripPrefix, "/")), "/") - var val map[string]interface{} + if pathString == "" { + continue + } + var val configValue + var err error // ensure a valid value is stored at this location if len(v.Value) > 0 { - if err := e.Decode(v.Value, &val); err != nil { + // try to decode into map value or array value + arrayV := &configArrayValue{v: []interface{}{}} + mapV := &configMapValue{v: map[string]interface{}{}} + switch { + case arrayV.Decode(e, v.Value) == nil: + val = arrayV + case mapV.Decode(e, v.Value) == nil: + val = mapV + default: return nil, fmt.Errorf("faild decode value. path: %s, error: %s", pathString, err) } } // set target at the root target := data - - // then descend to the target location, creating as we go, if need be - if pathString != "" { - path := strings.Split(pathString, "/") - // find (or create) the location we want to put this value at - for _, dir := range path { - if _, ok := target[dir]; !ok { - target[dir] = make(map[string]interface{}) - } - target = target[dir].(map[string]interface{}) + path := strings.Split(pathString, "/") + // find (or create) the leaf node we want to put this value at + for _, dir := range path[:len(path)-1] { + if _, ok := target[dir]; !ok { + target[dir] = make(map[string]interface{}) } - + target = target[dir].(map[string]interface{}) } + leafDir := path[len(path)-1] + // copy over the keys from the value - for k := range val { - target[k] = val[k] + switch val.(type) { + case *configArrayValue: + target[leafDir] = val.Value() + case *configMapValue: + target[leafDir] = make(map[string]interface{}) + target = target[leafDir].(map[string]interface{}) + mapv := val.Value().(map[string]interface{}) + for k := range mapv { + target[k] = mapv[k] + } } }