Skip to content

Commit

Permalink
Allow bind with a map[string]string
Browse files Browse the repository at this point in the history
  • Loading branch information
ItalyPaleAle committed Aug 29, 2020
1 parent b94d23d commit d723c76
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 0 deletions.
66 changes: 66 additions & 0 deletions binding/binding_test.go
Expand Up @@ -200,6 +200,12 @@ func TestBindingJSONDisallowUnknownFields(t *testing.T) {
`{"foo": "bar"}`, `{"foo": "bar", "what": "this"}`)
}

func TestBindingJSONStringMap(t *testing.T) {
testBodyBindingStringMap(t, JSON,
"/", "/",
`{"foo": "bar", "hello": "world"}`, `{"num": 2}`)
}

func TestBindingForm(t *testing.T) {
testFormBinding(t, "POST",
"/", "/",
Expand Down Expand Up @@ -336,6 +342,16 @@ func TestBindingFormForType(t *testing.T) {
"", "", "StructPointer")
}

func TestBindingFormStringMap(t *testing.T) {
testBodyBindingStringMap(t, Form,
"/", "",
`foo=bar&hello=world`, "")
// Should pick the last value
testBodyBindingStringMap(t, Form,
"/", "",
`foo=something&foo=bar&hello=world`, "")
}

func TestBindingQuery(t *testing.T) {
testQueryBinding(t, "POST",
"/?foo=bar&bar=foo", "/",
Expand Down Expand Up @@ -366,6 +382,28 @@ func TestBindingQueryBoolFail(t *testing.T) {
"bool_foo=unused", "")
}

func TestBindingQueryStringMap(t *testing.T) {
b := Query

obj := make(map[string]string)
req := requestWithBody("GET", "/?foo=bar&hello=world", "")
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.NotNil(t, obj)
assert.Len(t, obj, 2)
assert.Equal(t, "bar", obj["foo"])
assert.Equal(t, "world", obj["hello"])

obj = make(map[string]string)
req = requestWithBody("GET", "/?foo=bar&foo=2&hello=world", "") // should pick last
err = b.Bind(req, &obj)
assert.NoError(t, err)
assert.NotNil(t, obj)
assert.Len(t, obj, 2)
assert.Equal(t, "2", obj["foo"])
assert.Equal(t, "world", obj["hello"])
}

func TestBindingXML(t *testing.T) {
testBodyBinding(t,
XML, "xml",
Expand All @@ -387,6 +425,13 @@ func TestBindingYAML(t *testing.T) {
`foo: bar`, `bar: foo`)
}

func TestBindingYAMLStringMap(t *testing.T) {
// YAML is a superset of JSON, so the test below is JSON (to avoid newlines)
testBodyBindingStringMap(t, YAML,
"/", "/",
`{"foo": "bar", "hello": "world"}`, `{"nested": {"foo": "bar"}}`)
}

func TestBindingYAMLFail(t *testing.T) {
testBodyBindingFail(t,
YAML, "yaml",
Expand Down Expand Up @@ -1114,6 +1159,27 @@ func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody
assert.Error(t, err)
}

func testBodyBindingStringMap(t *testing.T, b Binding, path, badPath, body, badBody string) {
obj := make(map[string]string)
req := requestWithBody("POST", path, body)
if b.Name() == "form" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.NotNil(t, obj)
assert.Len(t, obj, 2)
assert.Equal(t, "bar", obj["foo"])
assert.Equal(t, "world", obj["hello"])

if badPath != "" && badBody != "" {
obj = make(map[string]string)
req = requestWithBody("POST", badPath, badBody)
err = b.Bind(req, &obj)
assert.Error(t, err)
}
}

func testBodyBindingUseNumber(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, name, b.Name())

Expand Down
38 changes: 38 additions & 0 deletions binding/form_mapping.go
Expand Up @@ -29,6 +29,21 @@ func mapForm(ptr interface{}, form map[string][]string) error {
var emptyField = reflect.StructField{}

func mapFormByTag(ptr interface{}, form map[string][]string, tag string) error {
// Check if ptr is a map
ptrVal := reflect.ValueOf(ptr)
var pointed interface{}
if ptrVal.Kind() == reflect.Ptr {
ptrVal = ptrVal.Elem()
pointed = ptrVal.Interface()
}
if ptrVal.Kind() == reflect.Map &&
ptrVal.Type().Key().Kind() == reflect.String {
if pointed != nil {
ptr = pointed
}
return setFormMap(ptr, form)
}

return mappingByPtr(ptr, formSource(form), tag)
}

Expand Down Expand Up @@ -349,3 +364,26 @@ func head(str, sep string) (head string, tail string) {
}
return str[:idx], str[idx+len(sep):]
}

func setFormMap(ptr interface{}, form map[string][]string) error {
el := reflect.TypeOf(ptr).Elem()
ptrSlice := (el.Kind() == reflect.Slice)
if ptrSlice {
fmt.Println(el, el.Elem())
}
for k, v := range form {
if ptrSlice {
ptr.(map[string][]string)[k] = v
if false {
return errors.New("cannot assign string slice value to map")
}
} else {
// Pick last
ptr.(map[string]string)[k] = v[len(v)-1]
if false {
return errors.New("cannot assign string value to map")
}
}
}
return nil
}
9 changes: 9 additions & 0 deletions binding/json_test.go
Expand Up @@ -19,3 +19,12 @@ func TestJSONBindingBindBody(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, "FOO", s.Foo)
}

func TestJSONBindingBindBodyMap(t *testing.T) {
s := make(map[string]string)
err := jsonBinding{}.BindBody([]byte(`{"foo": "FOO","hello":"world"}`), &s)
require.NoError(t, err)
assert.Len(t, s, 2)
assert.Equal(t, "FOO", s["foo"])
assert.Equal(t, "world", s["hello"])
}

0 comments on commit d723c76

Please sign in to comment.