diff --git a/modules/reitit-core/src/reitit/core.cljc b/modules/reitit-core/src/reitit/core.cljc index bcd54ac4..8b62909a 100644 --- a/modules/reitit-core/src/reitit/core.cljc +++ b/modules/reitit-core/src/reitit/core.cljc @@ -360,7 +360,8 @@ | `: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" + | `:router` | Function of `routes opts => router` to override the actual router implementation + | `:merge` | Function of `route-data key value` to add new kv pair to route-data map. Default uses merge-merge." ([raw-routes] (router raw-routes {})) ([raw-routes opts] diff --git a/modules/reitit-core/src/reitit/impl.cljc b/modules/reitit-core/src/reitit/impl.cljc index 7d3729bb..7bd7bb70 100644 --- a/modules/reitit-core/src/reitit/impl.cljc +++ b/modules/reitit-core/src/reitit/impl.cljc @@ -4,9 +4,7 @@ [clojure.set :as set] [meta-merge.core :as mm] [reitit.trie :as trie] - [reitit.exception :as ex] - ;; FIXME: Can't be used directly, should be option enabled by malli coercion - malli.util) + [reitit.exception :as ex]) #?(:clj (:import (java.util HashMap Map) (java.net URLEncoder URLDecoder)))) @@ -62,21 +60,21 @@ (defn map-data [f routes] (mapv (fn [[p ds]] [p (f p ds)]) routes)) -(defn merge-data [p x] +(defn default-route-data-merge [acc k v] + (mm/meta-merge acc {k v})) + +(defn merge-data [route-data-merge p x] (reduce (fn [acc [k v]] (try - (case k - ;; TODO: Make this enabled from malli coercion - ;; TODO: Schema & spec - :parameters (assoc acc :parameters (merge-with malli.util/merge (:parameters acc) v)) - (mm/meta-merge acc {k v})) + (route-data-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)) +(defn resolve-routes [raw-routes {:keys [coerce _merge] :as opts}] + (cond->> (->> (walk raw-routes opts) + (map-data (partial merge-data (or (:merge opts) default-route-data-merge)))) coerce (into [] (keep #(coerce % opts))))) (defn path-conflicting-routes [routes opts] diff --git a/modules/reitit-malli/src/reitit/coercion/malli.cljc b/modules/reitit-malli/src/reitit/coercion/malli.cljc index ee4a4bdc..9c06a518 100644 --- a/modules/reitit-malli/src/reitit/coercion/malli.cljc +++ b/modules/reitit-malli/src/reitit/coercion/malli.cljc @@ -7,7 +7,8 @@ [malli.swagger :as swagger] [malli.core :as m] [clojure.set :as set] - [clojure.walk :as walk])) + [clojure.walk :as walk] + [meta-merge.core :as mm])) ;; ;; coercion @@ -182,3 +183,8 @@ (-coercer (compile schema options) :response transformers :encode opts)))))) (def coercion (create default-options)) + +(defn route-data-merge [acc k v] + (case k + :parameters (assoc acc :parameters (merge-with mu/merge (:parameters acc) v)) + (mm/meta-merge acc {k v}))) diff --git a/test/cljc/reitit/impl_test.cljc b/test/cljc/reitit/impl_test.cljc index 8a1fb948..48cfe5bf 100644 --- a/test/cljc/reitit/impl_test.cljc +++ b/test/cljc/reitit/impl_test.cljc @@ -1,6 +1,9 @@ (ns reitit.impl-test (:require [clojure.test :refer [deftest testing is are]] - [reitit.impl :as impl])) + [reitit.impl :as impl] + [reitit.coercion.malli :as rcm] + [malli.core :as m] + [schema.core :as schema])) (deftest strip-nils-test (is (= {:a 1, :c false} (impl/strip-nils {:a 1, :b nil, :c false})))) @@ -173,7 +176,8 @@ (deftest merge-data-test (is (= {:view 'b :controllers [1 2]} - (impl/merge-data "/" + (impl/merge-data impl/default-route-data-merge + "/" [[:view 'a] [:controllers [1]] [:view 'b] @@ -181,21 +185,32 @@ (is (= {:view 'b :controllers [2]} - (impl/merge-data "/" + (impl/merge-data impl/default-route-data-merge + "/" [[:view 'a] [:controllers [1]] [:view 'b] [:controllers ^:replace [2]]]))) + (is (= {:a schema/Str + :b schema/Str} + (-> (impl/merge-data rcm/route-data-merge + "/" + [[:parameters {:path {:a schema/Str}}] + [:parameters {:path {:b schema/Str}}]]) + :parameters + :path))) + (is (= [:map [:a 'string?] [:b 'int?]] - (-> (impl/merge-data "/" + (-> (impl/merge-data rcm/route-data-merge + "/" [[:parameters {:path [:map [:a 'string?]]}] [:parameters {:path [:map [:b 'int?]]}]]) :parameters :path - ;; Merge returns schmea object, convert back to form for comparison + ;; Merge returns schema object, convert back to form for comparison malli.core/form))) ;; TODO: Also test and support Schema and spec merging