mirror of
https://github.com/metosin/reitit.git
synced 2025-12-22 18:41:10 +00:00
Merge pull request #561 from pfeodrippe/meta-merge
add `:meta-merge-fn` option
This commit is contained in:
commit
26a581298a
7 changed files with 81 additions and 24 deletions
|
|
@ -4,15 +4,16 @@ Routers can be configured via options. The following options are available for t
|
||||||
|
|
||||||
| key | description
|
| key | description
|
||||||
|--------------|-------------
|
|--------------|-------------
|
||||||
| `:path` | Base-path for routes
|
| `:path` | Base-path for routes
|
||||||
| `:routes` | Initial resolved routes (default `[]`)
|
| `:routes` | Initial resolved routes (default `[]`)
|
||||||
| `:data` | Initial route data (default `{}`)
|
| `:data` | Initial route data (default `{}`)
|
||||||
| `:spec` | clojure.spec definition for a route data, see `reitit.spec` on how to use this
|
| `:spec` | clojure.spec definition for a route data, see `reitit.spec` on how to use this
|
||||||
| `:syntax` | Path-parameter syntax as keyword or set of keywords (default #{:bracket :colon})
|
| `:syntax` | Path-parameter syntax as keyword or set of keywords (default #{:bracket :colon})
|
||||||
| `:expand` | Function of `arg opts => data` to expand route arg to route data (default `reitit.core/expand`)
|
| `:expand` | Function of `arg opts => data` to expand route arg to route data (default `reitit.core/expand`)
|
||||||
| `:coerce` | Function of `route opts => route` to coerce resolved route, can throw or return `nil`
|
| `:coerce` | Function of `route opts => route` to coerce resolved route, can throw or return `nil`
|
||||||
| `:compile` | Function of `route opts => result` to compile a route handler
|
| `:meta-merge-fn` | Function which follows the signature of `meta-merge.core/meta-merge`, useful for when you want to have more control over the meta merging
|
||||||
| `:validate` | Function of `routes opts => ()` to validate route (data) via side-effects
|
| `:compile` | Function of `route opts => result` to compile a route handler
|
||||||
| `:conflicts` | Function of `{route #{route}} => ()` to handle conflicting routes
|
| `:validate` | Function of `routes opts => ()` to validate route (data) via side-effects
|
||||||
| `:exception` | Function of `Exception => Exception ` to handle creation time exceptions (default `reitit.exception/exception`)
|
| `:conflicts` | Function of `{route #{route}} => ()` to handle conflicting routes
|
||||||
| `:router` | Function of `routes opts => router` to override the actual router implementation
|
| `:exception` | Function of `Exception => Exception ` to handle creation time exceptions (default `reitit.exception/exception`)
|
||||||
|
| `:router` | Function of `routes opts => router` to override the actual router implementation
|
||||||
|
|
|
||||||
|
|
@ -60,17 +60,17 @@
|
||||||
(defn map-data [f routes]
|
(defn map-data [f routes]
|
||||||
(mapv (fn [[p ds]] [p (f p ds)]) routes))
|
(mapv (fn [[p ds]] [p (f p ds)]) routes))
|
||||||
|
|
||||||
(defn merge-data [p x]
|
(defn merge-data [{:keys [meta-merge-fn] :as g} p x]
|
||||||
(reduce
|
(reduce
|
||||||
(fn [acc [k v]]
|
(fn [acc [k v]]
|
||||||
(try
|
(try
|
||||||
(mm/meta-merge acc {k v})
|
((or meta-merge-fn mm/meta-merge) acc {k v})
|
||||||
(catch #?(:clj Exception, :cljs js/Error) e
|
(catch #?(:clj Exception, :cljs js/Error) e
|
||||||
(ex/fail! ::merge-data {:path p, :left acc, :right {k v}, :exception e}))))
|
(ex/fail! ::merge-data {:path p, :left acc, :right {k v}, :exception e}))))
|
||||||
{} x))
|
{} x))
|
||||||
|
|
||||||
(defn resolve-routes [raw-routes {:keys [coerce] :as opts}]
|
(defn resolve-routes [raw-routes {:keys [coerce] :as opts}]
|
||||||
(cond->> (->> (walk raw-routes opts) (map-data merge-data))
|
(cond->> (->> (walk raw-routes opts) (map-data #(merge-data opts %1 %2)))
|
||||||
coerce (into [] (keep #(coerce % opts)))))
|
coerce (into [] (keep #(coerce % opts)))))
|
||||||
|
|
||||||
(defn path-conflicting-routes [routes opts]
|
(defn path-conflicting-routes [routes opts]
|
||||||
|
|
|
||||||
|
|
@ -155,8 +155,8 @@
|
||||||
:handler get-user}]])"
|
:handler get-user}]])"
|
||||||
([data]
|
([data]
|
||||||
(router data nil))
|
(router data nil))
|
||||||
([data opts]
|
([data {:keys [meta-merge-fn] :as opts}]
|
||||||
(let [opts (meta-merge {:compile compile-result} opts)]
|
(let [opts ((or meta-merge-fn meta-merge) {:compile compile-result} opts)]
|
||||||
(r/router data opts))))
|
(r/router data opts))))
|
||||||
|
|
||||||
(defn interceptor-handler [router]
|
(defn interceptor-handler [router]
|
||||||
|
|
|
||||||
|
|
@ -138,8 +138,8 @@
|
||||||
:handler get-user}]])"
|
:handler get-user}]])"
|
||||||
([data]
|
([data]
|
||||||
(router data nil))
|
(router data nil))
|
||||||
([data opts]
|
([data {:keys [meta-merge-fn] :as opts}]
|
||||||
(let [opts (meta-merge {:compile compile-result} opts)]
|
(let [opts ((or meta-merge-fn meta-merge) {:compile compile-result} opts)]
|
||||||
(r/router data opts))))
|
(r/router data opts))))
|
||||||
|
|
||||||
(defn middleware-handler [router]
|
(defn middleware-handler [router]
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
(update acc method expand opts)
|
(update acc method expand opts)
|
||||||
acc)) data ring/http-methods)])
|
acc)) data ring/http-methods)])
|
||||||
|
|
||||||
(defn compile-result [[path data] {:keys [::default-options-endpoint expand] :as opts}]
|
(defn compile-result [[path data] {:keys [::default-options-endpoint expand meta-merge-fn] :as opts}]
|
||||||
(let [[top childs] (ring/group-keys data)
|
(let [[top childs] (ring/group-keys data)
|
||||||
childs (cond-> childs
|
childs (cond-> childs
|
||||||
(and (not (:options childs)) (not (:handler top)) default-options-endpoint)
|
(and (not (:options childs)) (not (:handler top)) default-options-endpoint)
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
(->methods true top)
|
(->methods true top)
|
||||||
(reduce-kv
|
(reduce-kv
|
||||||
(fn [acc method data]
|
(fn [acc method data]
|
||||||
(let [data (meta-merge top data)]
|
(let [data ((or meta-merge-fn meta-merge) top data)]
|
||||||
(assoc acc method (->endpoint path data method method))))
|
(assoc acc method (->endpoint path data method method))))
|
||||||
(->methods (:handler top) data)
|
(->methods (:handler top) data)
|
||||||
childs))))
|
childs))))
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
(update acc method expand opts)
|
(update acc method expand opts)
|
||||||
acc)) data http-methods)])
|
acc)) data http-methods)])
|
||||||
|
|
||||||
(defn compile-result [[path data] {:keys [::default-options-endpoint expand] :as opts}]
|
(defn compile-result [[path data] {:keys [::default-options-endpoint expand meta-merge-fn] :as opts}]
|
||||||
(let [[top childs] (group-keys data)
|
(let [[top childs] (group-keys data)
|
||||||
childs (cond-> childs
|
childs (cond-> childs
|
||||||
(and (not (:options childs)) (not (:handler top)) default-options-endpoint)
|
(and (not (:options childs)) (not (:handler top)) default-options-endpoint)
|
||||||
|
|
@ -50,7 +50,7 @@
|
||||||
(->methods true top)
|
(->methods true top)
|
||||||
(reduce-kv
|
(reduce-kv
|
||||||
(fn [acc method data]
|
(fn [acc method data]
|
||||||
(let [data (meta-merge top data)]
|
(let [data ((or meta-merge-fn meta-merge) top data)]
|
||||||
(assoc acc method (->endpoint path data method method))))
|
(assoc acc method (->endpoint path data method method))))
|
||||||
(->methods (:handler top) data)
|
(->methods (:handler top) data)
|
||||||
childs))))
|
childs))))
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@
|
||||||
[malli.experimental.lite :as l]
|
[malli.experimental.lite :as l]
|
||||||
#?@(:clj [[muuntaja.middleware]
|
#?@(:clj [[muuntaja.middleware]
|
||||||
[jsonista.core :as j]])
|
[jsonista.core :as j]])
|
||||||
|
[malli.core :as m]
|
||||||
|
[malli.util :as mu]
|
||||||
|
[meta-merge.core :refer [meta-merge]]
|
||||||
[reitit.coercion.malli :as malli]
|
[reitit.coercion.malli :as malli]
|
||||||
[reitit.coercion.schema :as schema]
|
[reitit.coercion.schema :as schema]
|
||||||
[reitit.coercion.spec :as spec]
|
[reitit.coercion.spec :as spec]
|
||||||
|
|
@ -208,6 +211,38 @@
|
||||||
(let [{:keys [status]} (app invalid-request2)]
|
(let [{:keys [status]} (app invalid-request2)]
|
||||||
(is (= 500 status))))))))
|
(is (= 500 status))))))))
|
||||||
|
|
||||||
|
(defn- custom-meta-merge-checking-schema
|
||||||
|
([] {})
|
||||||
|
([left] left)
|
||||||
|
([left right]
|
||||||
|
(cond
|
||||||
|
(and (map? left) (map? right))
|
||||||
|
(merge-with custom-meta-merge-checking-schema left right)
|
||||||
|
|
||||||
|
(and (m/schema? left)
|
||||||
|
(m/schema? right))
|
||||||
|
(mu/merge left right)
|
||||||
|
|
||||||
|
:else
|
||||||
|
(meta-merge left right)))
|
||||||
|
([left right & more]
|
||||||
|
(reduce custom-meta-merge-checking-schema left (cons right more))))
|
||||||
|
|
||||||
|
(defn- custom-meta-merge-checking-parameters
|
||||||
|
([] {})
|
||||||
|
([left] left)
|
||||||
|
([left right]
|
||||||
|
(if (and (map? left) (map? right)
|
||||||
|
(contains? left :parameters)
|
||||||
|
(contains? right :parameters))
|
||||||
|
(-> (merge-with custom-meta-merge-checking-parameters left right)
|
||||||
|
(assoc :parameters (merge-with mu/merge
|
||||||
|
(:parameters left)
|
||||||
|
(:parameters right))))
|
||||||
|
(meta-merge left right)))
|
||||||
|
([left right & more]
|
||||||
|
(reduce custom-meta-merge-checking-parameters left (cons right more))))
|
||||||
|
|
||||||
(deftest malli-coercion-test
|
(deftest malli-coercion-test
|
||||||
(let [create (fn [middleware routes]
|
(let [create (fn [middleware routes]
|
||||||
(ring/ring-handler
|
(ring/ring-handler
|
||||||
|
|
@ -524,7 +559,28 @@
|
||||||
(is (= {:status 200, :body {:total -4}} (call "application/json" [:int {:encode/json -}]))))
|
(is (= {:status 200, :body {:total -4}} (call "application/json" [:int {:encode/json -}]))))
|
||||||
|
|
||||||
(testing "edn encoding (nada)"
|
(testing "edn encoding (nada)"
|
||||||
(is (= {:status 200, :body {:total +4}} (call "application/edn" [:int {:encode/json -}]))))))))
|
(is (= {:status 200, :body {:total +4}} (call "application/edn" [:int {:encode/json -}]))))))
|
||||||
|
|
||||||
|
(testing "using custom meta-merge function"
|
||||||
|
(let [->app (fn [schema-fn meta-merge-fn]
|
||||||
|
(ring/ring-handler
|
||||||
|
(ring/router
|
||||||
|
["/merging-params/:foo" {:parameters {:path (schema-fn [:map [:foo :string]])}}
|
||||||
|
["/:bar" {:parameters {:path (schema-fn [:map [:bar :string]])}
|
||||||
|
:get {:handler (fn [{{{:keys [foo bar]} :path} :parameters}]
|
||||||
|
{:status 200
|
||||||
|
:body {:total (str "FOO: " foo ", "
|
||||||
|
"BAR: " bar)}})}}]]
|
||||||
|
{:data {:middleware [rrc/coerce-request-middleware
|
||||||
|
rrc/coerce-response-middleware]
|
||||||
|
:coercion malli/coercion}
|
||||||
|
:meta-merge-fn meta-merge-fn})))
|
||||||
|
call (fn [schema-fn meta-merge-fn]
|
||||||
|
((->app schema-fn meta-merge-fn) {:uri "/merging-params/this/that"
|
||||||
|
:request-method :get}))]
|
||||||
|
|
||||||
|
(is (= {:status 200, :body {:total "FOO: this, BAR: that"}} (call m/schema custom-meta-merge-checking-schema)))
|
||||||
|
(is (= {:status 200, :body {:total "FOO: this, BAR: that"}} (call identity custom-meta-merge-checking-parameters)))))))
|
||||||
|
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(deftest muuntaja-test
|
(deftest muuntaja-test
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue