diff --git a/CHANGELOG.md b/CHANGELOG.md index a724804d..7ce94aa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,11 @@ We use [Break Versioning][breakver]. The version numbers follow a `. foo=bar&foo=baz). + +### `reitit-frontend` + +* Decode multi-valued query params correctly into seqs (e.g. foo=bar&foo=baz => `{:foo ["bar", "baz"]}`). ## 0.3.10 (2019-10-8) diff --git a/modules/reitit-core/src/reitit/impl.cljc b/modules/reitit-core/src/reitit/impl.cljc index 5f41802c..c99d593b 100644 --- a/modules/reitit-core/src/reitit/impl.cljc +++ b/modules/reitit-core/src/reitit/impl.cljc @@ -73,7 +73,7 @@ (defn resolve-routes [raw-routes {:keys [coerce] :as opts}] (cond->> (->> (walk raw-routes opts) (map-data merge-data)) - coerce (into [] (keep #(coerce % opts))))) + coerce (into [] (keep #(coerce % opts))))) (defn path-conflicting-routes [routes opts] (-> (into {} @@ -239,14 +239,19 @@ [params] (maybe-map-values #(url-encode (into-string %)) params)) +(defn- query-parameter [k v] + (str (form-encode (into-string k)) + "=" + (form-encode (into-string v)))) + (defn query-string "shallow transform of query parameters into query string" [params] (->> params (map (fn [[k v]] - (str (form-encode (into-string k)) - "=" - (form-encode (into-string v))))) + (if (or (sequential? v) (set? v)) + (str/join "&" (map query-parameter (repeat k) v)) + (query-parameter k v)))) (str/join "&"))) (defmacro goog-extend [type base-type ctor & methods] diff --git a/modules/reitit-frontend/src/reitit/frontend.cljs b/modules/reitit-frontend/src/reitit/frontend.cljs index 8145404a..071e296d 100644 --- a/modules/reitit-frontend/src/reitit/frontend.cljs +++ b/modules/reitit-frontend/src/reitit/frontend.cljs @@ -3,7 +3,14 @@ [reitit.coercion :as coercion] [reitit.coercion :as rc] [reitit.core :as r]) - (:import goog.Uri)) + (:import goog.Uri + goog.Uri.QueryData)) + +(defn- query-param [^goog.Uri.QueryData q k] + (let [vs (.getValues q k)] + (if (< (alength vs) 2) + (aget vs 0) + (vec vs)))) (defn query-params "Given goog.Uri, read query parameters into Clojure map." @@ -11,7 +18,7 @@ (let [q (.getQueryData uri)] (->> q (.getKeys) - (map (juxt keyword #(.get q %))) + (map (juxt keyword #(query-param q %))) (into {})))) (defn match-by-path diff --git a/test/cljc/reitit/impl_test.cljc b/test/cljc/reitit/impl_test.cljc index d8ff062f..1b2165c5 100644 --- a/test/cljc/reitit/impl_test.cljc +++ b/test/cljc/reitit/impl_test.cljc @@ -51,13 +51,11 @@ {:a 1} "a=1" {:a nil} "a=" {:a :b :c "d"} "a=b&c=d" - {:a "b c"} "a=b+c")) - -; TODO: support seq values? -;{:a ["b" "c"]} "a=b&a=c" -;{:a ["c" "b"]} "a=c&a=b" -;{:a (seq [1 2])} "a=1&a=2" -;{:a #{"c" "b"}} "a=b&a=c" + {:a "b c"} "a=b+c" + {:a ["b" "c"]} "a=b&a=c" + {:a ["c" "b"]} "a=c&a=b" + {:a (seq [1 2])} "a=1&a=2" + {:a #{"c" "b"}} "a=b&a=c")) ;; test from https://github.com/playframework/playframework -> UriEncodingSpec.scala diff --git a/test/cljs/reitit/frontend/core_test.cljs b/test/cljs/reitit/frontend/core_test.cljs index 20009088..22ac1db2 100644 --- a/test/cljs/reitit/frontend/core_test.cljs +++ b/test/cljs/reitit/frontend/core_test.cljs @@ -39,6 +39,16 @@ :path {}}}) (rf/match-by-path router "/foo"))) + (is (= (r/map->Match + {:template "/foo" + :data {:name ::foo} + :path-params {} + :query-params {:mode ["foo", "bar"]} + :path "/foo" + :parameters {:query {:mode ["foo", "bar"]} + :path {}}}) + (rf/match-by-path router "/foo?mode=foo&mode=bar"))) + (is (= "/foo" (r/match->path (rf/match-by-name router ::foo))))