fix: camel cased key of map item in config (#2715)

* fix: camel cased key of map item in config

* fix: mapping anonymous problem

* fix: mapping anonymous problem

* chore: refactor

* chore: add more tests

* chore: refactor
This commit is contained in:
Kevin Wan
2022-12-24 21:26:33 +08:00
committed by GitHub
parent f0d1722bbd
commit affbcb5698
5 changed files with 382 additions and 31 deletions

View File

@@ -376,19 +376,51 @@ func (u *Unmarshaler) processAnonymousField(field reflect.StructField, value ref
return err
}
if _, hasValue := getValue(m, key); hasValue {
return fmt.Errorf("fields of %s can't be wrapped inside, because it's anonymous", key)
}
if options.optional() {
return u.processAnonymousFieldOptional(field.Type, value, key, m, fullName)
return u.processAnonymousFieldOptional(field, value, key, m, fullName)
}
return u.processAnonymousFieldRequired(field.Type, value, m, fullName)
return u.processAnonymousFieldRequired(field, value, m, fullName)
}
func (u *Unmarshaler) processAnonymousFieldOptional(fieldType reflect.Type, value reflect.Value,
func (u *Unmarshaler) processAnonymousFieldOptional(field reflect.StructField, value reflect.Value,
key string, m valuerWithParent, fullName string) error {
derefedFieldType := Deref(field.Type)
switch derefedFieldType.Kind() {
case reflect.Struct:
return u.processAnonymousStructFieldOptional(field.Type, value, key, m, fullName)
default:
return u.processNamedField(field, value, m, fullName)
}
}
func (u *Unmarshaler) processAnonymousFieldRequired(field reflect.StructField, value reflect.Value,
m valuerWithParent, fullName string) error {
fieldType := field.Type
maybeNewValue(fieldType, value)
derefedFieldType := Deref(fieldType)
indirectValue := reflect.Indirect(value)
switch derefedFieldType.Kind() {
case reflect.Struct:
for i := 0; i < derefedFieldType.NumField(); i++ {
if err := u.processField(derefedFieldType.Field(i), indirectValue.Field(i),
m, fullName); err != nil {
return err
}
}
default:
if err := u.processNamedField(field, indirectValue, m, fullName); err != nil {
return err
}
}
return nil
}
func (u *Unmarshaler) processAnonymousStructFieldOptional(fieldType reflect.Type,
value reflect.Value, key string, m valuerWithParent, fullName string) error {
var filled bool
var required int
var requiredFilled int
@@ -428,21 +460,6 @@ func (u *Unmarshaler) processAnonymousFieldOptional(fieldType reflect.Type, valu
return nil
}
func (u *Unmarshaler) processAnonymousFieldRequired(fieldType reflect.Type, value reflect.Value,
m valuerWithParent, fullName string) error {
maybeNewValue(fieldType, value)
derefedFieldType := Deref(fieldType)
indirectValue := reflect.Indirect(value)
for i := 0; i < derefedFieldType.NumField(); i++ {
if err := u.processField(derefedFieldType.Field(i), indirectValue.Field(i), m, fullName); err != nil {
return err
}
}
return nil
}
func (u *Unmarshaler) processField(field reflect.StructField, value reflect.Value,
m valuerWithParent, fullName string) error {
if usingDifferentKeys(u.key, field) {

View File

@@ -212,6 +212,24 @@ func TestUnmarshalIntPtr(t *testing.T) {
assert.Equal(t, 1, *in.Int)
}
func TestUnmarshalIntSliceOfPtr(t *testing.T) {
type inner struct {
Ints []*int `key:"ints"`
}
m := map[string]interface{}{
"ints": []int{1, 2, 3},
}
var in inner
assert.NoError(t, UnmarshalKey(m, &in))
assert.NotEmpty(t, in.Ints)
var ints []int
for _, i := range in.Ints {
ints = append(ints, *i)
}
assert.EqualValues(t, []int{1, 2, 3}, ints)
}
func TestUnmarshalIntWithDefault(t *testing.T) {
type inner struct {
Int int `key:"int,default=5"`
@@ -3665,6 +3683,7 @@ func TestUnmarshalJsonBytesSliceOfMaps(t *testing.T) {
Name string `json:"name"`
ActualAmount int `json:"actual_amount"`
}
OrderApplyRefundReq struct {
OrderId string `json:"order_id"`
RefundReason RefundReasonData `json:"refund_reason,optional"`
@@ -3676,6 +3695,130 @@ func TestUnmarshalJsonBytesSliceOfMaps(t *testing.T) {
assert.NoError(t, UnmarshalJsonBytes(input, &req))
}
func TestUnmarshalJsonBytesWithAnonymousField(t *testing.T) {
type (
Int int
InnerConf struct {
Name string
}
Conf struct {
Int
InnerConf
}
)
var (
input = []byte(`{"Name": "hello", "Int": 3}`)
c Conf
)
assert.NoError(t, UnmarshalJsonBytes(input, &c))
assert.Equal(t, "hello", c.Name)
assert.Equal(t, Int(3), c.Int)
}
func TestUnmarshalJsonBytesWithAnonymousFieldOptional(t *testing.T) {
type (
Int int
InnerConf struct {
Name string
}
Conf struct {
Int `json:",optional"`
InnerConf
}
)
var (
input = []byte(`{"Name": "hello", "Int": 3}`)
c Conf
)
assert.NoError(t, UnmarshalJsonBytes(input, &c))
assert.Equal(t, "hello", c.Name)
assert.Equal(t, Int(3), c.Int)
}
func TestUnmarshalJsonBytesWithAnonymousFieldBadTag(t *testing.T) {
type (
Int int
InnerConf struct {
Name string
}
Conf struct {
Int `json:",optional=123"`
InnerConf
}
)
var (
input = []byte(`{"Name": "hello", "Int": 3}`)
c Conf
)
assert.Error(t, UnmarshalJsonBytes(input, &c))
}
func TestUnmarshalJsonBytesWithAnonymousFieldBadValue(t *testing.T) {
type (
Int int
InnerConf struct {
Name string
}
Conf struct {
Int
InnerConf
}
)
var (
input = []byte(`{"Name": "hello", "Int": "3"}`)
c Conf
)
assert.Error(t, UnmarshalJsonBytes(input, &c))
}
func TestUnmarshalJsonBytesWithAnonymousFieldBadTagInStruct(t *testing.T) {
type (
InnerConf struct {
Name string `json:",optional=123"`
}
Conf struct {
InnerConf `json:",optional"`
}
)
var (
input = []byte(`{"Name": "hello"}`)
c Conf
)
assert.Error(t, UnmarshalJsonBytes(input, &c))
}
func TestUnmarshalJsonBytesWithAnonymousFieldNotInOptions(t *testing.T) {
type (
InnerConf struct {
Name string `json:",options=[a,b]"`
}
Conf struct {
InnerConf `json:",optional"`
}
)
var (
input = []byte(`{"Name": "hello"}`)
c Conf
)
assert.Error(t, UnmarshalJsonBytes(input, &c))
}
func BenchmarkDefaultValue(b *testing.B) {
for i := 0; i < b.N; i++ {
var a struct {

View File

@@ -31,3 +31,27 @@ func TestMapValuerWithInherit_Value(t *testing.T) {
assert.Equal(t, "localhost", m["host"])
assert.Equal(t, 8080, m["port"])
}
func TestRecursiveValuer_Value(t *testing.T) {
input := map[string]interface{}{
"component": map[string]interface{}{
"name": "test",
"foo": map[string]interface{}{
"bar": "baz",
},
},
"foo": "value",
}
valuer := recursiveValuer{
current: mapValuer(input["component"].(map[string]interface{})),
parent: simpleValuer{
current: mapValuer(input),
},
}
val, ok := valuer.Value("foo")
assert.True(t, ok)
assert.EqualValues(t, map[string]interface{}{
"bar": "baz",
}, val)
}