Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Config source consul support 1st array #536

Merged
merged 5 commits into from Jun 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
70 changes: 58 additions & 12 deletions 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)
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)
}
}
}

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)
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)
}
}
}
70 changes: 55 additions & 15 deletions config/source/consul/util.go
Expand Up @@ -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]
}
}
}

Expand Down