[ARVADOS] created: 1.3.0-1926-gb69a0b0f7

Git user git at public.curoverse.com
Tue Nov 26 20:32:44 UTC 2019


        at  b69a0b0f7b4b9a773fb060a592057f9bd146e480 (commit)


commit b69a0b0f7b4b9a773fb060a592057f9bd146e480
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date:   Tue Nov 26 15:32:30 2019 -0500

    15877: Accept JSON-encoded param values in JSON request body.
    
    Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>

diff --git a/lib/controller/router/request.go b/lib/controller/router/request.go
index 4d18395b6..cc6379486 100644
--- a/lib/controller/router/request.go
+++ b/lib/controller/router/request.go
@@ -16,6 +16,45 @@ import (
 	"github.com/gorilla/mux"
 )
 
+func guessAndParse(k, v string) (interface{}, error) {
+	// All of these form values arrive as strings, so we need some
+	// type-guessing to accept non-string inputs:
+	//
+	// Values for parameters that take ints (limit=1) or bools
+	// (include_trash=1) are parsed accordingly.
+	//
+	// "null" and "" are nil.
+	//
+	// Values that look like JSON objects, arrays, or strings are
+	// parsed as JSON.
+	//
+	// The rest are left as strings.
+	switch {
+	case intParams[k]:
+		return strconv.ParseInt(v, 10, 64)
+	case boolParams[k]:
+		return stringToBool(v), nil
+	case v == "null" || v == "":
+		return nil, nil
+	case strings.HasPrefix(v, "["):
+		var j []interface{}
+		err := json.Unmarshal([]byte(v), &j)
+		return j, err
+	case strings.HasPrefix(v, "{"):
+		var j map[string]interface{}
+		err := json.Unmarshal([]byte(v), &j)
+		return j, err
+	case strings.HasPrefix(v, "\""):
+		var j string
+		err := json.Unmarshal([]byte(v), &j)
+		return j, err
+	default:
+		return v, nil
+	}
+	// TODO: Need to accept "?foo[]=bar&foo[]=baz" as
+	// foo=["bar","baz"]?
+}
+
 // Parse req as an Arvados V1 API request and return the request
 // parameters.
 //
@@ -33,56 +72,11 @@ func (rtr *router) loadRequestParams(req *http.Request, attrsKey string) (map[st
 	// Content-Type is application/x-www-form-urlencoded -- the
 	// request body.
 	for k, values := range req.Form {
-		// All of these form values arrive as strings, so we
-		// need some type-guessing to accept non-string
-		// inputs:
-		//
-		// Values for parameters that take ints (limit=1) or
-		// bools (include_trash=1) are parsed accordingly.
-		//
-		// "null" and "" are nil.
-		//
-		// Values that look like JSON objects, arrays, or
-		// strings are parsed as JSON.
-		//
-		// The rest are left as strings.
 		for _, v := range values {
-			switch {
-			case intParams[k]:
-				params[k], err = strconv.ParseInt(v, 10, 64)
-				if err != nil {
-					return nil, err
-				}
-			case boolParams[k]:
-				params[k] = stringToBool(v)
-			case v == "null" || v == "":
-				params[k] = nil
-			case strings.HasPrefix(v, "["):
-				var j []interface{}
-				err := json.Unmarshal([]byte(v), &j)
-				if err != nil {
-					return nil, err
-				}
-				params[k] = j
-			case strings.HasPrefix(v, "{"):
-				var j map[string]interface{}
-				err := json.Unmarshal([]byte(v), &j)
-				if err != nil {
-					return nil, err
-				}
-				params[k] = j
-			case strings.HasPrefix(v, "\""):
-				var j string
-				err := json.Unmarshal([]byte(v), &j)
-				if err != nil {
-					return nil, err
-				}
-				params[k] = j
-			default:
-				params[k] = v
+			params[k], err = guessAndParse(k, v)
+			if err != nil {
+				return nil, err
 			}
-			// TODO: Need to accept "?foo[]=bar&foo[]=baz"
-			// as foo=["bar","baz"]?
 		}
 	}
 
@@ -98,7 +92,20 @@ func (rtr *router) loadRequestParams(req *http.Request, attrsKey string) (map[st
 			return nil, httpError(http.StatusBadRequest, err)
 		}
 		for k, v := range jsonParams {
-			params[k] = v
+			switch v := v.(type) {
+			case string:
+				// The Ruby "arv" cli tool sends a
+				// JSON-encode params map with
+				// JSON-encoded values.
+				dec, err := guessAndParse(k, v)
+				if err != nil {
+					return nil, err
+				}
+				jsonParams[k] = dec
+				params[k] = dec
+			default:
+				params[k] = v
+			}
 		}
 		if attrsKey != "" && params[attrsKey] == nil {
 			// Copy top-level parameters from JSON request
diff --git a/lib/controller/router/request_test.go b/lib/controller/router/request_test.go
index 89238f656..118415cb4 100644
--- a/lib/controller/router/request_test.go
+++ b/lib/controller/router/request_test.go
@@ -49,10 +49,26 @@ func (tr *testReq) Request() *http.Request {
 	} else if tr.json {
 		if tr.jsonAttrsTop {
 			for k, v := range tr.attrs {
-				param[k] = v
+				if tr.jsonStringParam {
+					j, err := json.Marshal(v)
+					if err != nil {
+						panic(err)
+					}
+					param[k] = string(j)
+				} else {
+					param[k] = v
+				}
 			}
 		} else if tr.attrs != nil {
-			param[tr.attrsKey] = tr.attrs
+			if tr.jsonStringParam {
+				j, err := json.Marshal(tr.attrs)
+				if err != nil {
+					panic(err)
+				}
+				param[tr.attrsKey] = string(j)
+			} else {
+				param[tr.attrsKey] = tr.attrs
+			}
 		}
 		tr.body = bytes.NewBuffer(nil)
 		err := json.NewEncoder(tr.body).Encode(param)
@@ -118,6 +134,8 @@ func (s *RouterSuite) TestAttrsInBody(c *check.C) {
 	for _, tr := range []testReq{
 		{attrsKey: "model_name", json: true, attrs: attrs},
 		{attrsKey: "model_name", json: true, attrs: attrs, jsonAttrsTop: true},
+		{attrsKey: "model_name", json: true, attrs: attrs, jsonAttrsTop: true, jsonStringParam: true},
+		{attrsKey: "model_name", json: true, attrs: attrs, jsonAttrsTop: false, jsonStringParam: true},
 	} {
 		c.Logf("tr: %#v", tr)
 		req := tr.Request()

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list