diff --git a/params.go b/params.go index 4d0f1e6e..699ebede 100644 --- a/params.go +++ b/params.go @@ -1,6 +1,7 @@ package gophercloud import ( + "encoding/json" "fmt" "net/url" "reflect" @@ -155,6 +156,17 @@ func BuildQueryString(opts interface{}) (*url.URL, error) { params.Add(tags[0], v.Index(i).String()) } } + case reflect.Map: + vi := v.Interface() + mp, ok := vi.(map[string]string) + if !ok { + return nil, fmt.Errorf("Query opt [%v] is not type map[string]string, not supported.", v) + } + s, err := buildMapParam(mp) + if err != nil { + return nil, err + } + params.Add(tags[0], s) } } else { // Otherwise, the field is not set. @@ -172,6 +184,14 @@ func BuildQueryString(opts interface{}) (*url.URL, error) { return nil, fmt.Errorf("Options type is not a struct.") } +func buildMapParam(v map[string]string) (string, error) { + s, err := json.Marshal(v) + if err != nil { + return "", err + } + return string(s), nil +} + /* BuildHeaders is an internal function to be used by request methods in individual resource packages. diff --git a/params_test.go b/params_test.go index 2f40eec8..78f1a45a 100644 --- a/params_test.go +++ b/params_test.go @@ -36,12 +36,13 @@ func TestMaybeInt(t *testing.T) { func TestBuildQueryString(t *testing.T) { type testVar string opts := struct { - J int `q:"j"` - R string `q:"r,required"` - C bool `q:"c"` - S []string `q:"s"` - TS []testVar `q:"ts"` - TI []int `q:"ti"` + J int `q:"j"` + R string `q:"r,required"` + C bool `q:"c"` + S []string `q:"s"` + TS []testVar `q:"ts"` + TI []int `q:"ti"` + MP map[string]string `q:"mp"` }{ J: 2, R: "red", @@ -49,8 +50,9 @@ func TestBuildQueryString(t *testing.T) { S: []string{"one", "two", "three"}, TS: []testVar{"a", "b"}, TI: []int{1, 2}, + MP: map[string]string{"k1": "v1", "k2": "v2"}, } - expected := &url.URL{RawQuery: "c=true&j=2&r=red&s=one&s=two&s=three&ti=1&ti=2&ts=a&ts=b"} + expected := &url.URL{RawQuery: "c=true&j=2&mp=%7B%22k1%22%3A%22v1%22%2C%22k2%22%3A%22v2%22%7D&r=red&s=one&s=two&s=three&ti=1&ti=2&ts=a&ts=b"} actual, err := BuildQueryString(&opts) if err != nil { t.Errorf("Error building query string: %v", err) @@ -58,12 +60,13 @@ func TestBuildQueryString(t *testing.T) { th.CheckDeepEquals(t, expected, actual) opts = struct { - J int `q:"j"` - R string `q:"r,required"` - C bool `q:"c"` - S []string `q:"s"` - TS []testVar `q:"ts"` - TI []int `q:"ti"` + J int `q:"j"` + R string `q:"r,required"` + C bool `q:"c"` + S []string `q:"s"` + TS []testVar `q:"ts"` + TI []int `q:"ti"` + MP map[string]string `q:"mp"` }{ J: 2, C: true,