mirror of
https://github.com/metosin/reitit.git
synced 2025-12-18 08:51:12 +00:00
Use coercion to encode query-string values in match->path
This commit is contained in:
parent
a19b6034dd
commit
25dd0abcaf
6 changed files with 72 additions and 9 deletions
|
|
@ -19,7 +19,8 @@
|
||||||
(-open-model [this model] "Returns a new model which allows extra keys in maps")
|
(-open-model [this model] "Returns a new model which allows extra keys in maps")
|
||||||
(-encode-error [this error] "Converts error in to a serializable format")
|
(-encode-error [this error] "Converts error in to a serializable format")
|
||||||
(-request-coercer [this type model] "Returns a `value format => value` request coercion function")
|
(-request-coercer [this type model] "Returns a `value format => value` request coercion function")
|
||||||
(-response-coercer [this model] "Returns a `value format => value` response coercion function"))
|
(-response-coercer [this model] "Returns a `value format => value` response coercion function")
|
||||||
|
(-query-string-coercer [this model] "Returns a `value => value` query string coercion function"))
|
||||||
|
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(defmethod print-method ::coercion [coercion ^Writer w]
|
(defmethod print-method ::coercion [coercion ^Writer w]
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
(ns reitit.core
|
(ns reitit.core
|
||||||
(:require [reitit.exception :as exception]
|
(:require [reitit.exception :as exception]
|
||||||
[reitit.impl :as impl]
|
[reitit.impl :as impl]
|
||||||
[reitit.trie :as trie]))
|
[reitit.trie :as trie]
|
||||||
|
;; FIXME: Should avoid coercion require here?
|
||||||
|
[reitit.coercion :as coercion]))
|
||||||
|
|
||||||
;;
|
;;
|
||||||
;; Expand
|
;; Expand
|
||||||
|
|
@ -71,7 +73,22 @@
|
||||||
([match]
|
([match]
|
||||||
(match->path match nil))
|
(match->path match nil))
|
||||||
([match query-params]
|
([match query-params]
|
||||||
(some-> match :path (cond-> (seq query-params) (str "?" (impl/query-string query-params))))))
|
(some-> match :path (cond-> (seq query-params)
|
||||||
|
;; TODO: Should the coercion be applied elsewhere (FE ns?) so the core ns doesn't depend
|
||||||
|
;; on the coercion?
|
||||||
|
;; NOTE: Re-creates coercer on every call, could this be pre-compiled somewhere
|
||||||
|
;; or memoized? Does it matter much?
|
||||||
|
(str "?" (let [schema (-> match :data :parameters :query
|
||||||
|
;; FIXME: Why?
|
||||||
|
first)
|
||||||
|
coercion (-> match :data :coercion)
|
||||||
|
coercer (when (and schema coercion)
|
||||||
|
(coercion/-query-string-coercer coercion schema))
|
||||||
|
query-params (or (when coercer
|
||||||
|
(coercer query-params :default))
|
||||||
|
query-params)]
|
||||||
|
;; Default encoding for values will handle values that aren't encoded using coercer
|
||||||
|
(impl/query-string query-params)))))))
|
||||||
|
|
||||||
;;
|
;;
|
||||||
;; Different routers
|
;; Different routers
|
||||||
|
|
|
||||||
|
|
@ -176,6 +176,8 @@
|
||||||
(-request-coercer [_ type schema]
|
(-request-coercer [_ type schema]
|
||||||
(-coercer schema type transformers :decode opts))
|
(-coercer schema type transformers :decode opts))
|
||||||
(-response-coercer [_ schema]
|
(-response-coercer [_ schema]
|
||||||
(-coercer schema :response transformers :encode opts))))))
|
(-coercer schema :response transformers :encode opts))
|
||||||
|
(-query-string-coercer [_ schema]
|
||||||
|
(-coercer schema :string transformers :encode opts))))))
|
||||||
|
|
||||||
(def coercion (create default-options))
|
(def coercion (create default-options))
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,9 @@
|
||||||
value))))
|
value))))
|
||||||
(-response-coercer [this schema]
|
(-response-coercer [this schema]
|
||||||
(if (coerce-response? schema)
|
(if (coerce-response? schema)
|
||||||
(coercion/-request-coercer this :response schema)))))
|
(coercion/-request-coercer this :response schema)))
|
||||||
|
(-query-string-coercer [this schema]
|
||||||
|
;; TODO: Can this be implemented?
|
||||||
|
nil)))
|
||||||
|
|
||||||
(def coercion (create default-options))
|
(def coercion (create default-options))
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,9 @@
|
||||||
value))))
|
value))))
|
||||||
(-response-coercer [this spec]
|
(-response-coercer [this spec]
|
||||||
(if (coerce-response? spec)
|
(if (coerce-response? spec)
|
||||||
(coercion/-request-coercer this :response spec)))))
|
(coercion/-request-coercer this :response spec)))
|
||||||
|
(-query-string-coercer [this spec]
|
||||||
|
;; TODO: Can this be implemented?
|
||||||
|
nil)))
|
||||||
|
|
||||||
(def coercion (create default-options))
|
(def coercion (create default-options))
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
(ns reitit.coercion-test
|
(ns reitit.coercion-test
|
||||||
(:require [clojure.test :refer [deftest is testing]]
|
(:require [clojure.spec.alpha :as cs]
|
||||||
|
[clojure.string :as str]
|
||||||
|
[clojure.test :refer [deftest is testing]]
|
||||||
|
[malli.core :as m]
|
||||||
[malli.experimental.lite :as l]
|
[malli.experimental.lite :as l]
|
||||||
[reitit.coercion :as coercion]
|
[reitit.coercion :as coercion]
|
||||||
[reitit.coercion.malli]
|
[reitit.coercion.malli]
|
||||||
|
|
@ -7,8 +10,8 @@
|
||||||
[reitit.coercion.spec]
|
[reitit.coercion.spec]
|
||||||
[reitit.core :as r]
|
[reitit.core :as r]
|
||||||
[schema.core :as s]
|
[schema.core :as s]
|
||||||
[clojure.spec.alpha :as cs]
|
[spec-tools.data-spec :as ds]
|
||||||
[spec-tools.data-spec :as ds])
|
[malli.transform :as mt])
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(:import (clojure.lang ExceptionInfo))))
|
(:import (clojure.lang ExceptionInfo))))
|
||||||
|
|
||||||
|
|
@ -150,3 +153,37 @@
|
||||||
{:compile coercion/compile-request-coercers})]
|
{:compile coercion/compile-request-coercers})]
|
||||||
(is (= {:path {:user-id 123, :company "metosin"}}
|
(is (= {:path {:user-id 123, :company "metosin"}}
|
||||||
(:parameters (match-by-path-and-coerce! router "/metosin/users/123"))))))
|
(:parameters (match-by-path-and-coerce! router "/metosin/users/123"))))))
|
||||||
|
|
||||||
|
(deftest match->path-parameter-coercion-test
|
||||||
|
(testing "default handling for query-string collection"
|
||||||
|
(let [router (r/router ["/:a/:b" ::route])]
|
||||||
|
(is (= "/olipa/kerran?x=a&x=b"
|
||||||
|
(-> router
|
||||||
|
(r/match-by-name! ::route {:a "olipa", :b "kerran"})
|
||||||
|
(r/match->path {:x [:a :b]}))))))
|
||||||
|
|
||||||
|
(testing "custom encode/string for a collection"
|
||||||
|
(let [router (r/router ["/:a/:b"
|
||||||
|
{:name ::route
|
||||||
|
:coercion reitit.coercion.malli/coercion
|
||||||
|
:parameters {:query [:map
|
||||||
|
[:x
|
||||||
|
[:vector
|
||||||
|
{:encode/string (fn [xs]
|
||||||
|
(str/join "," (map name xs)))
|
||||||
|
:decode/string (fn [s]
|
||||||
|
(if (string? s)
|
||||||
|
(mapv keyword (str/split s #","))
|
||||||
|
s))}
|
||||||
|
:keyword]]]}}]
|
||||||
|
{:compile coercion/compile-request-coercers})]
|
||||||
|
;; NOTE: "," is urlencoded by the impl/query-string step, is that ok?
|
||||||
|
(is (= "/olipa/kerran?x=a%2Cb"
|
||||||
|
(-> router
|
||||||
|
(r/match-by-name! ::route {:a "olipa", :b "kerran"})
|
||||||
|
(r/match->path {:x [:a :b]}))))
|
||||||
|
|
||||||
|
(is (= {:query {:x [:a :b]}}
|
||||||
|
(-> (r/match-by-path router "/olipa/kerran")
|
||||||
|
(assoc :query-params {:x "a,b"})
|
||||||
|
(coercion/coerce!)))))))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue