[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