[ARVADOS] updated: 7b6d593fcf9e76a1908c8a32e362fcd4f9cd2c4d
Git user
git at public.curoverse.com
Wed Jun 15 12:29:36 EDT 2016
Summary of changes:
sdk/go/arvados/client.go | 87 +++++++++++++++++++++++++++----------------
sdk/go/arvados/client_test.go | 70 ++++++++++++++++++++++++++++++++++
2 files changed, 125 insertions(+), 32 deletions(-)
via 7b6d593fcf9e76a1908c8a32e362fcd4f9cd2c4d (commit)
from b9b1b04f50fbc0b8539c641662bed152c94165ea (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
commit 7b6d593fcf9e76a1908c8a32e362fcd4f9cd2c4d
Author: Tom Clegg <tom at curoverse.com>
Date: Wed Jun 15 12:29:21 2016 -0400
9395: Fix encoding of integers in query params.
diff --git a/sdk/go/arvados/client.go b/sdk/go/arvados/client.go
index ee830c8..00edc85 100644
--- a/sdk/go/arvados/client.go
+++ b/sdk/go/arvados/client.go
@@ -6,6 +6,7 @@ import (
"fmt"
"io"
"io/ioutil"
+ "math"
"net/http"
"net/url"
"os"
@@ -81,6 +82,57 @@ func (c *Client) DoAndDecode(dst interface{}, req *http.Request) error {
return json.Unmarshal(buf, dst)
}
+// Convert an arbitrary struct to url.Values. For example,
+//
+// Foo{Bar: []int{1,2,3}, Baz: "waz"}
+//
+// becomes
+//
+// url.Values{`bar`:`{"a":[1,2,3]}`,`Baz`:`waz`}
+//
+// params itself is returned if it is already an url.Values.
+func anythingToValues(params interface{}) (url.Values, error) {
+ if v, ok := params.(url.Values); ok {
+ return v, nil
+ }
+ // TODO: Do this more efficiently, possibly using
+ // json.Decode/Encode, so the whole thing doesn't have to get
+ // encoded, decoded, and re-encoded.
+ j, err := json.Marshal(params)
+ if err != nil {
+ return nil, err
+ }
+ var generic map[string]interface{}
+ err = json.Unmarshal(j, &generic)
+ if err != nil {
+ return nil, err
+ }
+ urlValues := url.Values{}
+ for k, v := range generic {
+ if v, ok := v.(string); ok {
+ urlValues.Set(k, v)
+ continue
+ }
+ if v, ok := v.(float64); ok {
+ // Unmarshal decodes all numbers as float64,
+ // which can be written as 1.2345e4 in JSON,
+ // but this form is not accepted for ints in
+ // url params. If a number fits in an int64,
+ // encode it as int64 rather than float64.
+ if v, frac := math.Modf(v); frac == 0 && v <= math.MaxInt64 && v >= math.MinInt64 {
+ urlValues.Set(k, fmt.Sprintf("%d", int64(v)))
+ continue
+ }
+ }
+ j, err := json.Marshal(v)
+ if err != nil {
+ return nil, err
+ }
+ urlValues.Set(k, string(j))
+ }
+ return urlValues, nil
+}
+
// RequestAndDecode performs an API request and unmarshals the
// response (which must be JSON) into dst. Method and body arguments
// are the same as for http.NewRequest(). The given path is added to
@@ -90,38 +142,9 @@ func (c *Client) DoAndDecode(dst interface{}, req *http.Request) error {
// path must not contain a query string.
func (c *Client) RequestAndDecode(dst interface{}, method, path string, body io.Reader, params interface{}) error {
urlString := c.apiURL(path)
- var urlValues url.Values
- if v, ok := params.(url.Values); ok {
- urlValues = v
- } else if params != nil {
- // Convert an arbitrary struct to url.Values. For
- // example, Foo{Bar: []int{1,2,3}, Baz: "waz"} becomes
- // url.Values{`bar`:`{"a":[1,2,3]}`,`Baz`:`waz`}
- //
- // TODO: Do this more efficiently, possibly using
- // json.Decode/Encode, so the whole thing doesn't have
- // to get encoded, decoded, and re-encoded.
- j, err := json.Marshal(params)
- if err != nil {
- return err
- }
- var generic map[string]interface{}
- err = json.Unmarshal(j, &generic)
- if err != nil {
- return err
- }
- urlValues = url.Values{}
- for k, v := range generic {
- if v, ok := v.(string); ok {
- urlValues.Set(k, v)
- continue
- }
- j, err := json.Marshal(v)
- if err != nil {
- return err
- }
- urlValues.Set(k, string(j))
- }
+ urlValues, err := anythingToValues(params)
+ if err != nil {
+ return err
}
if (method == "GET" || body != nil) && urlValues != nil {
// FIXME: what if params don't fit in URL
diff --git a/sdk/go/arvados/client_test.go b/sdk/go/arvados/client_test.go
index 2db50bf..422ad90 100644
--- a/sdk/go/arvados/client_test.go
+++ b/sdk/go/arvados/client_test.go
@@ -5,6 +5,7 @@ import (
"fmt"
"io/ioutil"
"net/http"
+ "net/url"
"sync"
"testing"
)
@@ -81,3 +82,72 @@ func TestCurrentUser(t *testing.T) {
t.Errorf("got nil error, expected something awful")
}
}
+
+func TestAnythingToValues(t *testing.T) {
+ type testCase struct {
+ in interface{}
+ // ok==nil means anythingToValues should return an
+ // error, otherwise it's a func that returns true if
+ // out is correct
+ ok func(out url.Values) bool
+ }
+ for _, tc := range []testCase{
+ {
+ in: map[string]interface{}{"foo": "bar"},
+ ok: func(out url.Values) bool {
+ return out.Get("foo") == "bar"
+ },
+ },
+ {
+ in: map[string]interface{}{"foo": 2147483647},
+ ok: func(out url.Values) bool {
+ return out.Get("foo") == "2147483647"
+ },
+ },
+ {
+ in: map[string]interface{}{"foo": 1.234},
+ ok: func(out url.Values) bool {
+ return out.Get("foo") == "1.234"
+ },
+ },
+ {
+ in: map[string]interface{}{"foo": "1.234"},
+ ok: func(out url.Values) bool {
+ return out.Get("foo") == "1.234"
+ },
+ },
+ {
+ in: map[string]interface{}{"foo": map[string]interface{}{"bar":1.234}},
+ ok: func(out url.Values) bool {
+ return out.Get("foo") == `{"bar":1.234}`
+ },
+ },
+ {
+ in: url.Values{"foo": {"bar"}},
+ ok: func(out url.Values) bool {
+ return out.Get("foo") == "bar"
+ },
+ },
+ {
+ in: 1234,
+ ok: nil,
+ },
+ {
+ in: []string{"foo"},
+ ok: nil,
+ },
+ } {
+ t.Logf("%#v", tc.in)
+ out, err := anythingToValues(tc.in)
+ switch {
+ case tc.ok == nil:
+ if err == nil {
+ t.Errorf("got %#v, expected error", out)
+ }
+ case err != nil:
+ t.Errorf("got err %#v, expected nil", err)
+ case !tc.ok(out):
+ t.Errorf("got %#v but tc.ok() says that is wrong", out)
+ }
+ }
+}
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list