Merge pull request #13514 from kargakis/queryparams-handle-pointers

queryparams: Handle pointer fields in structs
This commit is contained in:
Abhi Shah 2015-09-03 16:26:54 -07:00
commit 217e1d0d87
2 changed files with 71 additions and 9 deletions

View File

@ -50,6 +50,14 @@ func formatValue(value interface{}) string {
return fmt.Sprintf("%v", value) return fmt.Sprintf("%v", value)
} }
func isPointerKind(kind reflect.Kind) bool {
return kind == reflect.Ptr
}
func isStructKind(kind reflect.Kind) bool {
return kind == reflect.Struct
}
func isValueKind(kind reflect.Kind) bool { func isValueKind(kind reflect.Kind) bool {
switch kind { switch kind {
case reflect.String, reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, case reflect.String, reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16,
@ -70,7 +78,13 @@ func addParam(values url.Values, tag string, omitempty bool, value reflect.Value
if omitempty && zeroValue(value) { if omitempty && zeroValue(value) {
return return
} }
values.Add(tag, fmt.Sprintf("%v", value.Interface())) val := ""
iValue := fmt.Sprintf("%v", value.Interface())
if iValue != "<nil>" {
val = iValue
}
values.Add(tag, val)
} }
func addListOfParams(values url.Values, tag string, omitempty bool, list reflect.Value) { func addListOfParams(values url.Values, tag string, omitempty bool, list reflect.Value) {
@ -92,12 +106,20 @@ func Convert(obj runtime.Object) (url.Values, error) {
case reflect.Ptr, reflect.Interface: case reflect.Ptr, reflect.Interface:
sv = reflect.ValueOf(obj).Elem() sv = reflect.ValueOf(obj).Elem()
default: default:
return nil, fmt.Errorf("Expecting a pointer or interface") return nil, fmt.Errorf("expecting a pointer or interface")
} }
st := sv.Type() st := sv.Type()
if st.Kind() != reflect.Struct { if !isStructKind(st.Kind()) {
return nil, fmt.Errorf("Expecting a pointer to a struct") return nil, fmt.Errorf("expecting a pointer to a struct")
} }
// Check all object fields
convertStruct(result, st, sv)
return result, nil
}
func convertStruct(result url.Values, st reflect.Type, sv reflect.Value) {
for i := 0; i < st.NumField(); i++ { for i := 0; i < st.NumField(); i++ {
field := sv.Field(i) field := sv.Field(i)
tag, omitempty := jsonTag(st.Field(i)) tag, omitempty := jsonTag(st.Field(i))
@ -105,14 +127,24 @@ func Convert(obj runtime.Object) (url.Values, error) {
continue continue
} }
ft := field.Type() ft := field.Type()
kind := ft.Kind()
if isPointerKind(kind) {
kind = ft.Elem().Kind()
if !field.IsNil() {
field = reflect.Indirect(field)
}
}
switch { switch {
case isValueKind(ft.Kind()): case isValueKind(kind):
addParam(result, tag, omitempty, field) addParam(result, tag, omitempty, field)
case ft.Kind() == reflect.Array || ft.Kind() == reflect.Slice: case kind == reflect.Array || kind == reflect.Slice:
if isValueKind(ft.Elem().Kind()) { if isValueKind(ft.Elem().Kind()) {
addListOfParams(result, tag, omitempty, field) addListOfParams(result, tag, omitempty, field)
} }
case isStructKind(kind) && !(zeroValue(field) && omitempty):
convertStruct(result, ft, field)
} }
} }
return result, nil
} }

View File

@ -53,6 +53,13 @@ type foo struct {
func (*foo) IsAnAPIObject() {} func (*foo) IsAnAPIObject() {}
type baz struct {
Ptr *int `json:"ptr"`
Bptr *bool `json:"bptr,omitempty"`
}
func (*baz) IsAnAPIObject() {}
func validateResult(t *testing.T, input interface{}, actual, expected url.Values) { func validateResult(t *testing.T, input interface{}, actual, expected url.Values) {
local := url.Values{} local := url.Values{}
for k, v := range expected { for k, v := range expected {
@ -104,12 +111,12 @@ func TestConvert(t *testing.T) {
}, },
{ {
input: &foo{ input: &foo{
Str: "ignore embedded struct", Str: "don't ignore embedded struct",
Foobar: bar{ Foobar: bar{
Float1: 5.0, Float1: 5.0,
}, },
}, },
expected: url.Values{"str": {"ignore embedded struct"}}, expected: url.Values{"str": {"don't ignore embedded struct"}, "float1": {"5"}, "float2": {"0"}},
}, },
{ {
// Ignore untagged fields // Ignore untagged fields
@ -131,6 +138,25 @@ func TestConvert(t *testing.T) {
}, },
expected: url.Values{"str": {""}, "namedStr": {"named str"}}, expected: url.Values{"str": {""}, "namedStr": {"named str"}},
}, },
{
input: &baz{
Ptr: intp(5),
Bptr: boolp(true),
},
expected: url.Values{"ptr": {"5"}, "bptr": {"true"}},
},
{
input: &baz{
Bptr: boolp(true),
},
expected: url.Values{"ptr": {""}, "bptr": {"true"}},
},
{
input: &baz{
Ptr: intp(5),
},
expected: url.Values{"ptr": {"5"}},
},
} }
for _, test := range tests { for _, test := range tests {
@ -141,3 +167,7 @@ func TestConvert(t *testing.T) {
validateResult(t, test.input, result, test.expected) validateResult(t, test.input, result, test.expected)
} }
} }
func intp(n int) *int { return &n }
func boolp(b bool) *bool { return &b }