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 db6f708d..1edc5b48 100644 --- a/config/source/consul/util.go +++ b/config/source/consul/util.go @@ -8,40 +8,78 @@ import ( "github.com/micro/go-micro/config/encoder" ) +type jsonValue interface { + Value() interface{} + Decode(encoder.Encoder, []byte) (jsonValue, error) +} +type jsonArrayValue []interface{} +type jsonMapValue map[string]interface{} + +func (a jsonArrayValue) Value() interface{} { return a } +func (a jsonArrayValue) Decode(e encoder.Encoder, b []byte) (jsonValue, error) { + v := jsonArrayValue{} + err := e.Decode(b, &v) + return v, err +} +func (m jsonMapValue) Value() interface{} { return m } +func (m jsonMapValue) Decode(e encoder.Encoder, b []byte) (jsonValue, error) { + v := jsonMapValue{} + err := e.Decode(b, &v) + return v, err +} + 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, stripPrefix), "/") - var val map[string]interface{} + if pathString == "" { + continue + } + var val jsonValue + 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 { - 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{}) + // check whether this is an array + if v.Value[0] == 91 && v.Value[len(v.Value)-1] == 93 { + val = jsonArrayValue{} + if val, err = val.Decode(e, v.Value); err != nil { + return nil, fmt.Errorf("faild decode value. path: %s, error: %s", pathString, err) + } + } else { + val = jsonMapValue{} + if val, err = val.Decode(e, v.Value); err != nil { + return nil, fmt.Errorf("faild decode value. path: %s, error: %s", pathString, err) } - target = target[dir].(map[string]interface{}) } - } + target := data + 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 jsonArrayValue: + target[leafDir] = val.Value() + case jsonMapValue: + target[leafDir] = make(map[string]interface{}) + target = target[leafDir].(map[string]interface{}) + mapv := val.Value().(jsonMapValue) + for k := range mapv { + target[k] = mapv[k] + } } }