Merge pull request #536 from magodo/config_source_consul_support_array
Config source consul support 1st array
This commit is contained in:
		| @@ -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) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -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] | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user