mirror of
https://github.com/metosin/reitit.git
synced 2025-12-17 08:21:11 +00:00
Merge pull request #581 from metosin/add-support-for-fragment-parameters-2
Add reitit-frontend support for fragment string
This commit is contained in:
commit
310dcd0e99
3 changed files with 120 additions and 14 deletions
|
|
@ -39,7 +39,8 @@
|
|||
:body (->ParameterCoercion :body-params :body false false)
|
||||
:form (->ParameterCoercion :form-params :string true true)
|
||||
:header (->ParameterCoercion :headers :string true true)
|
||||
:path (->ParameterCoercion :path-params :string true true)})
|
||||
:path (->ParameterCoercion :path-params :string true true)
|
||||
:fragment (->ParameterCoercion :fragment :string true true)})
|
||||
|
||||
(defn ^:no-doc request-coercion-failed! [result coercion value in request serialize-failed-result]
|
||||
(throw
|
||||
|
|
|
|||
|
|
@ -37,12 +37,17 @@
|
|||
coercion/coerce!)]
|
||||
(if-let [match (r/match-by-path router (.getPath uri))]
|
||||
(let [q (query-params uri)
|
||||
match (assoc match :query-params q)
|
||||
fragment (when (.hasFragment uri)
|
||||
(.getFragment uri))
|
||||
match (assoc match
|
||||
:query-params q
|
||||
:fragment fragment)
|
||||
;; Return uncoerced values if coercion is not enabled - so
|
||||
;; that tha parameters are always accessible from same property.
|
||||
parameters (or (coerce! match)
|
||||
{:path (:path-params match)
|
||||
:query q})]
|
||||
:query q
|
||||
:fragment fragment})]
|
||||
(assoc match :parameters parameters))))))
|
||||
|
||||
(defn match-by-name
|
||||
|
|
|
|||
|
|
@ -4,12 +4,23 @@
|
|||
[reitit.frontend :as rf]
|
||||
[reitit.coercion :as rc]
|
||||
[schema.core :as s]
|
||||
[reitit.coercion.schema :as rsc]
|
||||
[reitit.coercion.schema :as rcs]
|
||||
[reitit.coercion.malli :as rcm]
|
||||
[reitit.frontend.test-utils :refer [capture-console]]))
|
||||
|
||||
(defn m [x]
|
||||
(assoc x :data nil :result nil))
|
||||
|
||||
(defn decode-form [s]
|
||||
;; RFC 6749 4.2.2 specifies OAuth token response uses
|
||||
;; form-urlencoded format to encode values in the fragment string.
|
||||
;; Use built-in JS function to decode.
|
||||
;; ring.util.codec/decode-form works on Clj.
|
||||
(when s
|
||||
(->> (.entries (js/URLSearchParams. s))
|
||||
(map (fn [[k v]] [(keyword k) v]))
|
||||
(into {}))))
|
||||
|
||||
(deftest match-by-path-test
|
||||
(testing "simple"
|
||||
(let [router (r/router ["/"
|
||||
|
|
@ -22,8 +33,10 @@
|
|||
:path-params {}
|
||||
:query-params {}
|
||||
:path "/"
|
||||
:fragment nil
|
||||
:parameters {:query {}
|
||||
:path {}}})
|
||||
:path {}
|
||||
:fragment nil}})
|
||||
(rf/match-by-path router "/")))
|
||||
|
||||
(is (= "/"
|
||||
|
|
@ -35,8 +48,10 @@
|
|||
:path-params {}
|
||||
:query-params {}
|
||||
:path "/foo"
|
||||
:fragment nil
|
||||
:parameters {:query {}
|
||||
:path {}}})
|
||||
:path {}
|
||||
:fragment nil}})
|
||||
(rf/match-by-path router "/foo")))
|
||||
|
||||
(is (= (r/map->Match
|
||||
|
|
@ -45,8 +60,10 @@
|
|||
:path-params {}
|
||||
:query-params {:mode ["foo", "bar"]}
|
||||
:path "/foo"
|
||||
:fragment nil
|
||||
:parameters {:query {:mode ["foo", "bar"]}
|
||||
:path {}}})
|
||||
:path {}
|
||||
:fragment nil}})
|
||||
(rf/match-by-path router "/foo?mode=foo&mode=bar")))
|
||||
|
||||
(is (= "/foo"
|
||||
|
|
@ -64,17 +81,20 @@
|
|||
(let [router (r/router ["/"
|
||||
[":id" {:name ::foo
|
||||
:parameters {:path {:id s/Int}
|
||||
:query {(s/optional-key :mode) s/Keyword}}}]]
|
||||
:query {(s/optional-key :mode) s/Keyword}
|
||||
:fragment (s/maybe s/Str)}}]]
|
||||
{:compile rc/compile-request-coercers
|
||||
:data {:coercion rsc/coercion}})]
|
||||
:data {:coercion rcs/coercion}})]
|
||||
|
||||
(is (= (r/map->Match
|
||||
{:template "/:id"
|
||||
:path-params {:id "5"}
|
||||
:query-params {}
|
||||
:path "/5"
|
||||
:fragment nil
|
||||
:parameters {:query {}
|
||||
:path {:id 5}}})
|
||||
:path {:id 5}
|
||||
:fragment nil}})
|
||||
(m (rf/match-by-path router "/5"))))
|
||||
|
||||
(is (= "/5"
|
||||
|
|
@ -99,21 +119,25 @@
|
|||
:path-params {:id "5"}
|
||||
:query-params {:mode "foo"}
|
||||
:path "/5"
|
||||
:fragment nil
|
||||
:parameters {:path {:id 5}
|
||||
:query {:mode :foo}}})
|
||||
:query {:mode :foo}
|
||||
:fragment nil}})
|
||||
(m (rf/match-by-path router "/5?mode=foo"))))
|
||||
|
||||
(is (= "/5?mode=foo"
|
||||
(r/match->path (rf/match-by-name router ::foo {:id 5}) {:mode :foo}))))
|
||||
|
||||
(testing "fragment is ignored"
|
||||
(testing "fragment string is read"
|
||||
(is (= (r/map->Match
|
||||
{:template "/:id"
|
||||
:path-params {:id "5"}
|
||||
:query-params {:mode "foo"}
|
||||
:path "/5"
|
||||
:fragment "fragment"
|
||||
:parameters {:path {:id 5}
|
||||
:query {:mode :foo}}})
|
||||
:query {:mode :foo}
|
||||
:fragment "fragment"}})
|
||||
(m (rf/match-by-path router "/5?mode=foo#fragment")))))
|
||||
|
||||
(testing "console warning about missing params"
|
||||
|
|
@ -126,4 +150,80 @@
|
|||
(:messages
|
||||
(capture-console
|
||||
(fn []
|
||||
(rf/match-by-name! router ::foo {}))))))))))
|
||||
(rf/match-by-name! router ::foo {})))))))))
|
||||
|
||||
(testing "malli coercion"
|
||||
(let [router (r/router ["/"
|
||||
[":id" {:name ::foo
|
||||
:parameters {:path [:map
|
||||
[:id :int]]
|
||||
:query [:map
|
||||
[:mode {:optional true} :keyword]]
|
||||
:fragment [:maybe
|
||||
[:map
|
||||
{:decode/string decode-form}
|
||||
[:access_token :string]
|
||||
[:refresh_token :string]
|
||||
[:expires_in :int]
|
||||
[:provider_token :string]
|
||||
[:token_type :string]]]}}]]
|
||||
{:compile rc/compile-request-coercers
|
||||
:data {:coercion rcm/coercion}})]
|
||||
|
||||
(is (= (r/map->Match
|
||||
{:template "/:id"
|
||||
:path-params {:id "5"}
|
||||
:query-params {}
|
||||
:path "/5"
|
||||
:fragment nil
|
||||
:parameters {:query {}
|
||||
:path {:id 5}
|
||||
:fragment nil}})
|
||||
(m (rf/match-by-path router "/5"))))
|
||||
|
||||
(is (= "/5"
|
||||
(r/match->path (rf/match-by-name router ::foo {:id 5}))))
|
||||
|
||||
(testing "coercion error"
|
||||
(testing "throws without options"
|
||||
(is (thrown? js/Error (m (rf/match-by-path router "/a")))))
|
||||
|
||||
(testing "thows and calles on-coercion-error"
|
||||
(let [exception (atom nil)
|
||||
match (atom nil)]
|
||||
(is (thrown? js/Error (m (rf/match-by-path router "/a" {:on-coercion-error (fn [m e]
|
||||
(reset! match m)
|
||||
(reset! exception e))}))))
|
||||
(is (= {:id "a"} (-> @match :path-params)))
|
||||
(is (= {:id "a"} (-> @exception (ex-data) :value))))))
|
||||
|
||||
(testing "query param is read"
|
||||
(is (= (r/map->Match
|
||||
{:template "/:id"
|
||||
:path-params {:id "5"}
|
||||
:query-params {:mode "foo"}
|
||||
:path "/5"
|
||||
:fragment nil
|
||||
:parameters {:path {:id 5}
|
||||
:query {:mode :foo}
|
||||
:fragment nil}})
|
||||
(m (rf/match-by-path router "/5?mode=foo"))))
|
||||
|
||||
(is (= "/5?mode=foo"
|
||||
(r/match->path (rf/match-by-name router ::foo {:id 5}) {:mode :foo}))))
|
||||
|
||||
(testing "fragment string is read"
|
||||
(is (= (r/map->Match
|
||||
{:template "/:id"
|
||||
:path-params {:id "5"}
|
||||
:query-params {:mode "foo"}
|
||||
:path "/5"
|
||||
:fragment "access_token=foo&refresh_token=bar&provider_token=baz&token_type=bearer&expires_in=3600"
|
||||
:parameters {:path {:id 5}
|
||||
:query {:mode :foo}
|
||||
:fragment {:access_token "foo"
|
||||
:refresh_token "bar"
|
||||
:provider_token "baz"
|
||||
:token_type "bearer"
|
||||
:expires_in 3600}}})
|
||||
(m (rf/match-by-path router "/5?mode=foo#access_token=foo&refresh_token=bar&provider_token=baz&token_type=bearer&expires_in=3600"))))))))
|
||||
|
|
|
|||
Loading…
Reference in a new issue