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
|
||||
|--------------|-------------
|
||||
| `:path` | Base-path for routes
|
||||
| `:routes` | Initial resolved routes (default `[]`)
|
||||
| `:data` | Initial route data (default `{}`)
|
||||
| `: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})
|
||||
| `: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`
|
||||
| `:compile` | Function of `route opts => result` to compile a route handler
|
||||
| `:validate` | Function of `routes opts => ()` to validate route (data) via side-effects
|
||||
| `:conflicts` | Function of `{route #{route}} => ()` to handle conflicting routes
|
||||
| `: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
|
||||
| `:path` | Base-path for routes
|
||||
| `:routes` | Initial resolved routes (default `[]`)
|
||||
| `:data` | Initial route data (default `{}`)
|
||||
| `: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})
|
||||
| `: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`
|
||||
| `: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
|
||||
| `:compile` | Function of `route opts => result` to compile a route handler
|
||||
| `:validate` | Function of `routes opts => ()` to validate route (data) via side-effects
|
||||
| `:conflicts` | Function of `{route #{route}} => ()` to handle conflicting routes
|
||||
| `: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]
|
||||
(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
|
||||
(fn [acc [k v]]
|
||||
(try
|
||||
(mm/meta-merge acc {k v})
|
||||
((or meta-merge-fn mm/meta-merge) acc {k v})
|
||||
(catch #?(:clj Exception, :cljs js/Error) e
|
||||
(ex/fail! ::merge-data {:path p, :left acc, :right {k v}, :exception e}))))
|
||||
{} x))
|
||||
|
||||
(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)))))
|
||||
|
||||
(defn path-conflicting-routes [routes opts]
|
||||
|
|
|
|||
|
|
@ -155,8 +155,8 @@
|
|||
:handler get-user}]])"
|
||||
([data]
|
||||
(router data nil))
|
||||
([data opts]
|
||||
(let [opts (meta-merge {:compile compile-result} opts)]
|
||||
([data {:keys [meta-merge-fn] :as opts}]
|
||||
(let [opts ((or meta-merge-fn meta-merge) {:compile compile-result} opts)]
|
||||
(r/router data opts))))
|
||||
|
||||
(defn interceptor-handler [router]
|
||||
|
|
|
|||
|
|
@ -138,8 +138,8 @@
|
|||
:handler get-user}]])"
|
||||
([data]
|
||||
(router data nil))
|
||||
([data opts]
|
||||
(let [opts (meta-merge {:compile compile-result} opts)]
|
||||
([data {:keys [meta-merge-fn] :as opts}]
|
||||
(let [opts ((or meta-merge-fn meta-merge) {:compile compile-result} opts)]
|
||||
(r/router data opts))))
|
||||
|
||||
(defn middleware-handler [router]
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
(update acc method expand opts)
|
||||
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)
|
||||
childs (cond-> childs
|
||||
(and (not (:options childs)) (not (:handler top)) default-options-endpoint)
|
||||
|
|
@ -38,7 +38,7 @@
|
|||
(->methods true top)
|
||||
(reduce-kv
|
||||
(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))))
|
||||
(->methods (:handler top) data)
|
||||
childs))))
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
(update acc method expand opts)
|
||||
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)
|
||||
childs (cond-> childs
|
||||
(and (not (:options childs)) (not (:handler top)) default-options-endpoint)
|
||||
|
|
@ -50,7 +50,7 @@
|
|||
(->methods true top)
|
||||
(reduce-kv
|
||||
(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))))
|
||||
(->methods (:handler top) data)
|
||||
childs))))
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@
|
|||
[malli.experimental.lite :as l]
|
||||
#?@(:clj [[muuntaja.middleware]
|
||||
[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.schema :as schema]
|
||||
[reitit.coercion.spec :as spec]
|
||||
|
|
@ -208,6 +211,38 @@
|
|||
(let [{:keys [status]} (app invalid-request2)]
|
||||
(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
|
||||
(let [create (fn [middleware routes]
|
||||
(ring/ring-handler
|
||||
|
|
@ -524,7 +559,28 @@
|
|||
(is (= {:status 200, :body {:total -4}} (call "application/json" [:int {:encode/json -}]))))
|
||||
|
||||
(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
|
||||
(deftest muuntaja-test
|
||||
|
|
|
|||
Loading…
Reference in a new issue