15014: Hide busy/idle nodes panel when crunch1 is not active.
[arvados.git] / lib / controller / router / request.go
index 67d4e0ffb6280b7ef5dde458dfe98770a4a55b4e..377f7243c009bef591fffb4b3b53acaf39c0d359 100644 (file)
@@ -6,6 +6,7 @@ package router
 
 import (
        "encoding/json"
+       "fmt"
        "io"
        "mime"
        "net/http"
@@ -26,9 +27,34 @@ func (rtr *router) loadRequestParams(req *http.Request, attrsKey string) (map[st
                return nil, httpError(http.StatusBadRequest, err)
        }
        params := map[string]interface{}{}
+
+       // Load parameters from req.Form, which (after
+       // req.ParseForm()) includes the query string and -- when
+       // 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, "["):
@@ -52,11 +78,6 @@ func (rtr *router) loadRequestParams(req *http.Request, attrsKey string) (map[st
                                        return nil, err
                                }
                                params[k] = j
-                       case k == "limit" || k == "offset":
-                               params[k], err = strconv.ParseInt(v, 10, 64)
-                               if err != nil {
-                                       return nil, err
-                               }
                        default:
                                params[k] = v
                        }
@@ -64,9 +85,15 @@ func (rtr *router) loadRequestParams(req *http.Request, attrsKey string) (map[st
                        // as foo=["bar","baz"]?
                }
        }
-       if ct, _, err := mime.ParseMediaType(req.Header.Get("Content-Type")); err != nil && ct == "application/json" {
+
+       // Decode body as JSON if Content-Type request header is
+       // missing or application/json.
+       mt := req.Header.Get("Content-Type")
+       if ct, _, err := mime.ParseMediaType(mt); err != nil && mt != "" {
+               return nil, fmt.Errorf("error parsing media type %q: %s", mt, err)
+       } else if (ct == "application/json" || mt == "") && req.ContentLength != 0 {
                jsonParams := map[string]interface{}{}
-               err := json.NewDecoder(req.Body).Decode(jsonParams)
+               err := json.NewDecoder(req.Body).Decode(&jsonParams)
                if err != nil {
                        return nil, httpError(http.StatusBadRequest, err)
                }
@@ -91,6 +118,19 @@ func (rtr *router) loadRequestParams(req *http.Request, attrsKey string) (map[st
                params["attrs"] = v
                delete(params, attrsKey)
        }
+
+       if order, ok := params["order"].(string); ok {
+               // We must accept strings ("foo, bar desc") and arrays
+               // (["foo", "bar desc"]) because RailsAPI does.
+               // Convert to an array here before trying to unmarshal
+               // into options structs.
+               if order == "" {
+                       delete(params, "order")
+               } else {
+                       params["order"] = strings.Split(order, ",")
+               }
+       }
+
        return params, nil
 }
 
@@ -110,3 +150,24 @@ func (rtr *router) transcode(src interface{}, dst interface{}) error {
        }
        return err
 }
+
+var intParams = map[string]bool{
+       "limit":  true,
+       "offset": true,
+}
+
+var boolParams = map[string]bool{
+       "distinct":             true,
+       "ensure_unique_name":   true,
+       "include_trash":        true,
+       "include_old_versions": true,
+}
+
+func stringToBool(s string) bool {
+       switch s {
+       case "", "false", "0":
+               return false
+       default:
+               return true
+       }
+}