mirror of
https://github.com/metosin/reitit.git
synced 2025-12-22 18:41:10 +00:00
Merge pull request #104 from metosin/CoerceAllParametes
Coerce all parameters
This commit is contained in:
commit
5bc7b09e19
9 changed files with 122 additions and 24 deletions
39
CHANGELOG.md
39
CHANGELOG.md
|
|
@ -1,3 +1,42 @@
|
||||||
|
## UNRELEASED
|
||||||
|
|
||||||
|
## `reitit-core`
|
||||||
|
|
||||||
|
* `reitit.coercion/coerce!` coerced all parameters found in match, e.g. injecting in `:query-parameters` into `Match` with coerce those too if `:query` coercion is defined.
|
||||||
|
* `spec-tools.data-spec/maybe` can be used in spec-coercion.
|
||||||
|
|
||||||
|
```clj
|
||||||
|
(def router
|
||||||
|
(reitit.core/router
|
||||||
|
["/spec" {:coercion reitit.coercion.spec/coercion}
|
||||||
|
["/:number/:keyword" {:parameters {:path {:number int?
|
||||||
|
:keyword keyword?}
|
||||||
|
:query (ds/maybe {:int int?})}}]]
|
||||||
|
{:compile reitit.coercion/compile-request-coercers}))
|
||||||
|
|
||||||
|
(-> (reitit.core/match-by-path router "/spec/10/kikka")
|
||||||
|
(assoc :query-params {:int "10"})
|
||||||
|
(reitit.coercion/coerce!))
|
||||||
|
; {:path {:number 10, :keyword :kikka}
|
||||||
|
; :query {:int 10}}
|
||||||
|
```
|
||||||
|
|
||||||
|
* `reitit.core/match->path` to create full paths from match, including the query parameters:
|
||||||
|
|
||||||
|
```clj
|
||||||
|
(require '[reitit.core :as r])
|
||||||
|
|
||||||
|
(-> (r/router ["/:a/:b" ::route])
|
||||||
|
(r/match-by-name! ::route {:a "olipa", :b "kerran"})
|
||||||
|
(r/match->path))
|
||||||
|
; "/olipa/kerran"
|
||||||
|
|
||||||
|
(-> (r/router ["/:a/:b" ::route])
|
||||||
|
(r/match-by-name! ::route {:a "olipa", :b "kerran"})
|
||||||
|
(r/match->path {:iso "pöriläinen"}))
|
||||||
|
; "/olipa/kerran?iso=p%C3%B6ril%C3%A4inen"
|
||||||
|
```
|
||||||
|
|
||||||
## 0.1.2 (2018-6-6)
|
## 0.1.2 (2018-6-6)
|
||||||
|
|
||||||
### `reitit-core`
|
### `reitit-core`
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
|
|
||||||
(defrecord ParameterCoercion [in style keywordize? open?])
|
(defrecord ParameterCoercion [in style keywordize? open?])
|
||||||
|
|
||||||
(def ^:no-doc ring-parameter-coercion
|
(def ^:no-doc defaut-parameter-coercion
|
||||||
{:query (->ParameterCoercion :query-params :string true true)
|
{:query (->ParameterCoercion :query-params :string true true)
|
||||||
:body (->ParameterCoercion :body-params :body false false)
|
:body (->ParameterCoercion :body-params :body false false)
|
||||||
:form (->ParameterCoercion :form-params :string true true)
|
:form (->ParameterCoercion :form-params :string true true)
|
||||||
|
|
@ -73,7 +73,7 @@
|
||||||
(defn request-coercer [coercion type model {:keys [extract-request-format]
|
(defn request-coercer [coercion type model {:keys [extract-request-format]
|
||||||
:or {extract-request-format extract-request-format-default}}]
|
:or {extract-request-format extract-request-format-default}}]
|
||||||
(if coercion
|
(if coercion
|
||||||
(let [{:keys [keywordize? open? in style]} (ring-parameter-coercion type)
|
(let [{:keys [keywordize? open? in style]} (defaut-parameter-coercion type)
|
||||||
transform (comp (if keywordize? walk/keywordize-keys identity) in)
|
transform (comp (if keywordize? walk/keywordize-keys identity) in)
|
||||||
model (if open? (-open-model coercion model) model)
|
model (if open? (-open-model coercion model) model)
|
||||||
coercer (-request-coercer coercion style model)]
|
coercer (-request-coercer coercion style model)]
|
||||||
|
|
@ -155,5 +155,5 @@
|
||||||
coercers under `:result` (provided by [[compile-request-coercers]].
|
coercers under `:result` (provided by [[compile-request-coercers]].
|
||||||
If coercion or parameters are not defined, return `nil`"
|
If coercion or parameters are not defined, return `nil`"
|
||||||
[match]
|
[match]
|
||||||
(if-let [result (:result match)]
|
(if-let [coercers (:result match)]
|
||||||
(coerce-request result {:path-params (:path-params match)})))
|
(coerce-request coercers match)))
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,12 @@
|
||||||
(impl/throw-on-missing-path-params
|
(impl/throw-on-missing-path-params
|
||||||
(:template match) (:required match) path-params)))))
|
(:template match) (:required match) path-params)))))
|
||||||
|
|
||||||
|
(defn match->path
|
||||||
|
([match]
|
||||||
|
(match->path match nil))
|
||||||
|
([match query-params]
|
||||||
|
(some-> match :path (cond-> query-params (str "?" (impl/query-string query-params))))))
|
||||||
|
|
||||||
(def default-router-options
|
(def default-router-options
|
||||||
{:lookup name-lookup
|
{:lookup name-lookup
|
||||||
:expand expand
|
:expand expand
|
||||||
|
|
|
||||||
|
|
@ -196,13 +196,23 @@
|
||||||
|
|
||||||
#?(:clj Object
|
#?(:clj Object
|
||||||
:cljs object)
|
:cljs object)
|
||||||
(into-string [this] (str this)))
|
(into-string [this] (str this))
|
||||||
|
|
||||||
|
nil
|
||||||
|
(into-string [this]))
|
||||||
|
|
||||||
(defn path-params
|
(defn path-params
|
||||||
"shallow transform of the path-param values into strings"
|
"shallow transform of the path parameters values into strings"
|
||||||
[params]
|
[params]
|
||||||
(reduce-kv
|
(reduce-kv
|
||||||
(fn [m k v]
|
(fn [m k v]
|
||||||
(assoc m k (url-encode (into-string v))))
|
(assoc m k (url-encode (into-string v))))
|
||||||
{}
|
{}
|
||||||
params))
|
params))
|
||||||
|
|
||||||
|
(defn query-string
|
||||||
|
"shallow transform of query parameters into query string"
|
||||||
|
[params]
|
||||||
|
(->> params
|
||||||
|
(map (fn [[k v]] (str (url-encode (into-string k)) "=" (url-encode (into-string v)))))
|
||||||
|
(str/join "&")))
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
(ns reitit.coercion.spec
|
(ns reitit.coercion.spec
|
||||||
(:require [clojure.spec.alpha :as s]
|
(:require [clojure.spec.alpha :as s]
|
||||||
[spec-tools.core :as st #?@(:cljs [:refer [Spec]])]
|
[spec-tools.core :as st #?@(:cljs [:refer [Spec]])]
|
||||||
[spec-tools.data-spec :as ds]
|
[spec-tools.data-spec :as ds #?@(:cljs [:refer [Maybe]])]
|
||||||
[spec-tools.transform :as stt]
|
[spec-tools.transform :as stt]
|
||||||
[spec-tools.swagger.core :as swagger]
|
[spec-tools.swagger.core :as swagger]
|
||||||
[reitit.coercion :as coercion]
|
[reitit.coercion :as coercion]
|
||||||
[clojure.set :as set])
|
[clojure.set :as set])
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(:import (spec_tools.core Spec))))
|
(:import (spec_tools.core Spec)
|
||||||
|
(spec_tools.data_spec Maybe))))
|
||||||
|
|
||||||
(def string-transformer
|
(def string-transformer
|
||||||
(st/type-transformer
|
(st/type-transformer
|
||||||
|
|
@ -48,6 +49,10 @@
|
||||||
(into-spec [this name]
|
(into-spec [this name]
|
||||||
(ds/spec (ensure-name name) this))
|
(ds/spec (ensure-name name) this))
|
||||||
|
|
||||||
|
Maybe
|
||||||
|
(into-spec [this name]
|
||||||
|
(ds/spec (ensure-name name) this))
|
||||||
|
|
||||||
Spec
|
Spec
|
||||||
(into-spec [this _] this)
|
(into-spec [this _] this)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,26 @@
|
||||||
(ns reitit.coercion-test
|
(ns reitit.coercion-test
|
||||||
(:require [clojure.test :refer [deftest testing is]]
|
(:require [clojure.test :refer [deftest testing is]]
|
||||||
[schema.core :as s]
|
[schema.core :as s]
|
||||||
|
[spec-tools.data-spec :as ds]
|
||||||
[reitit.core :as r]
|
[reitit.core :as r]
|
||||||
[reitit.coercion :as coercion]
|
[reitit.coercion :as coercion]
|
||||||
[reitit.coercion.spec :as spec]
|
[reitit.coercion.spec]
|
||||||
[reitit.coercion.schema :as schema])
|
[reitit.coercion.schema])
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(:import (clojure.lang ExceptionInfo))))
|
(:import (clojure.lang ExceptionInfo))))
|
||||||
|
|
||||||
(deftest spec-coercion-test
|
(deftest coercion-test
|
||||||
(let [r (r/router
|
(let [r (r/router
|
||||||
[["/schema" {:coercion schema/coercion}
|
[["/schema" {:coercion reitit.coercion.schema/coercion}
|
||||||
["/:number/:keyword" {:name ::user
|
["/:number/:keyword" {:name ::user
|
||||||
:parameters {:path {:number s/Int
|
:parameters {:path {:number s/Int
|
||||||
:keyword s/Keyword}}}]]
|
:keyword s/Keyword}
|
||||||
["/spec" {:coercion spec/coercion}
|
:query (s/maybe {:int s/Int})}}]]
|
||||||
|
["/spec" {:coercion reitit.coercion.spec/coercion}
|
||||||
["/:number/:keyword" {:name ::user
|
["/:number/:keyword" {:name ::user
|
||||||
:parameters {:path {:number int?
|
:parameters {:path {:number int?
|
||||||
:keyword keyword?}}}]]
|
:keyword keyword?}
|
||||||
|
:query (ds/maybe {:int int?})}}]]
|
||||||
["/none"
|
["/none"
|
||||||
["/:number/:keyword" {:name ::user
|
["/:number/:keyword" {:name ::user
|
||||||
:parameters {:path {:number int?
|
:parameters {:path {:number int?
|
||||||
|
|
@ -27,8 +30,11 @@
|
||||||
(testing "schema-coercion"
|
(testing "schema-coercion"
|
||||||
(testing "succeeds"
|
(testing "succeeds"
|
||||||
(let [m (r/match-by-path r "/schema/1/abba")]
|
(let [m (r/match-by-path r "/schema/1/abba")]
|
||||||
(is (= {:path {:keyword :abba, :number 1}}
|
(is (= {:path {:keyword :abba, :number 1}, :query nil}
|
||||||
(coercion/coerce! m)))))
|
(coercion/coerce! m))))
|
||||||
|
(let [m (r/match-by-path r "/schema/1/abba")]
|
||||||
|
(is (= {:path {:keyword :abba, :number 1}, :query {:int 10}}
|
||||||
|
(coercion/coerce! (assoc m :query-params {:int "10"}))))))
|
||||||
(testing "throws with invalid input"
|
(testing "throws with invalid input"
|
||||||
(let [m (r/match-by-path r "/schema/kikka/abba")]
|
(let [m (r/match-by-path r "/schema/kikka/abba")]
|
||||||
(is (thrown? ExceptionInfo (coercion/coerce! m))))))
|
(is (thrown? ExceptionInfo (coercion/coerce! m))))))
|
||||||
|
|
@ -36,8 +42,11 @@
|
||||||
(testing "spec-coercion"
|
(testing "spec-coercion"
|
||||||
(testing "succeeds"
|
(testing "succeeds"
|
||||||
(let [m (r/match-by-path r "/spec/1/abba")]
|
(let [m (r/match-by-path r "/spec/1/abba")]
|
||||||
(is (= {:path {:keyword :abba, :number 1}}
|
(is (= {:path {:keyword :abba, :number 1}, :query nil}
|
||||||
(coercion/coerce! m)))))
|
(coercion/coerce! m))))
|
||||||
|
(let [m (r/match-by-path r "/schema/1/abba")]
|
||||||
|
(is (= {:path {:keyword :abba, :number 1}, :query {:int 10}}
|
||||||
|
(coercion/coerce! (assoc m :query-params {:int "10"}))))))
|
||||||
(testing "throws with invalid input"
|
(testing "throws with invalid input"
|
||||||
(let [m (r/match-by-path r "/spec/kikka/abba")]
|
(let [m (r/match-by-path r "/spec/kikka/abba")]
|
||||||
(is (thrown? ExceptionInfo (coercion/coerce! m))))))
|
(is (thrown? ExceptionInfo (coercion/coerce! m))))))
|
||||||
|
|
|
||||||
|
|
@ -246,3 +246,14 @@
|
||||||
[["/a"] ["/a"]]))))
|
[["/a"] ["/a"]]))))
|
||||||
(testing "can be configured to ignore"
|
(testing "can be configured to ignore"
|
||||||
(is (not (nil? (r/router [["/a"] ["/a"]] {:conflicts (constantly nil)})))))))
|
(is (not (nil? (r/router [["/a"] ["/a"]] {:conflicts (constantly nil)})))))))
|
||||||
|
|
||||||
|
(deftest match->path-test
|
||||||
|
(let [router (r/router ["/:a/:b" ::route])]
|
||||||
|
(is (= "/olipa/kerran"
|
||||||
|
(-> router
|
||||||
|
(r/match-by-name! ::route {:a "olipa", :b "kerran"})
|
||||||
|
(r/match->path))))
|
||||||
|
(is (= "/olipa/kerran?iso=p%C3%B6ril%C3%A4inen"
|
||||||
|
(-> router
|
||||||
|
(r/match-by-name! ::route {:a "olipa", :b "kerran"})
|
||||||
|
(r/match->path {:iso "pöriläinen"}))))))
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,8 @@
|
||||||
:s "kikka"
|
:s "kikka"
|
||||||
:u "c2541900-17a7-4353-9024-db8ac258ba4e"
|
:u "c2541900-17a7-4353-9024-db8ac258ba4e"
|
||||||
:k "kikka"
|
:k "kikka"
|
||||||
:qk "reitit.impl-test%2Fkikka"}
|
:qk "reitit.impl-test%2Fkikka"
|
||||||
|
:nil nil}
|
||||||
(impl/path-params {:n 1
|
(impl/path-params {:n 1
|
||||||
:n1 -1
|
:n1 -1
|
||||||
:n2 (long 1)
|
:n2 (long 1)
|
||||||
|
|
@ -45,4 +46,21 @@
|
||||||
:s "kikka"
|
:s "kikka"
|
||||||
:u #uuid "c2541900-17a7-4353-9024-db8ac258ba4e"
|
:u #uuid "c2541900-17a7-4353-9024-db8ac258ba4e"
|
||||||
:k :kikka
|
:k :kikka
|
||||||
:qk ::kikka}))))
|
:qk ::kikka
|
||||||
|
:nil nil}))))
|
||||||
|
|
||||||
|
(deftest query-params-test
|
||||||
|
(are [x y]
|
||||||
|
(= (impl/query-string x) y)
|
||||||
|
{:a "b"} "a=b"
|
||||||
|
{"a" "b"} "a=b"
|
||||||
|
{:a 1} "a=1"
|
||||||
|
{:a nil} "a="
|
||||||
|
{:a :b :c "d"} "a=b&c=d"
|
||||||
|
{:a "b c"} "a=b%20c"))
|
||||||
|
|
||||||
|
; 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"
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@
|
||||||
[reitit.ring.coercion :as rrc]
|
[reitit.ring.coercion :as rrc]
|
||||||
[reitit.coercion.spec :as spec]
|
[reitit.coercion.spec :as spec]
|
||||||
[reitit.coercion.schema :as schema]
|
[reitit.coercion.schema :as schema]
|
||||||
#?(:clj
|
#?@(:clj [
|
||||||
[muuntaja.middleware])
|
[muuntaja.middleware]
|
||||||
[jsonista.core :as j])
|
[jsonista.core :as j]]))
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(:import (clojure.lang ExceptionInfo)
|
(:import (clojure.lang ExceptionInfo)
|
||||||
(java.io ByteArrayInputStream))))
|
(java.io ByteArrayInputStream))))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue