This commit is contained in:
Tommi Reiman 2022-02-12 22:34:26 +02:00
parent b7cc420fde
commit 5d4c886d35
52 changed files with 1686 additions and 1690 deletions

View file

@ -43,28 +43,28 @@
(defn ^:no-doc request-coercion-failed! [result coercion value in request]
(throw
(ex-info
(str "Request coercion failed: " (pr-str result))
(merge
(into {} result)
{:type ::request-coercion
:coercion coercion
:value value
:in [:request in]
:request request}))))
(ex-info
(str "Request coercion failed: " (pr-str result))
(merge
(into {} result)
{:type ::request-coercion
:coercion coercion
:value value
:in [:request in]
:request request}))))
(defn ^:no-doc response-coercion-failed! [result coercion value request response]
(throw
(ex-info
(str "Response coercion failed: " (pr-str result))
(merge
(into {} result)
{:type ::response-coercion
:coercion coercion
:value value
:in [:response :body]
:request request
:response response}))))
(ex-info
(str "Response coercion failed: " (pr-str result))
(merge
(into {} result)
{:type ::response-coercion
:coercion coercion
:value value
:in [:response :body]
:request request
:response response}))))
(defn extract-request-format-default [request]
(-> request :muuntaja/request :format))
@ -109,9 +109,9 @@
(defn coerce-request [coercers request]
(reduce-kv
(fn [acc k coercer]
(impl/fast-assoc acc k (coercer request)))
{} coercers))
(fn [acc k coercer]
(impl/fast-assoc acc k (coercer request)))
{} coercers))
(defn coerce-response [coercers request response]
(if response
@ -147,13 +147,13 @@
:multipart :formData}]
(case specification
:swagger (->> (update
data
:parameters
(fn [parameters]
(->> parameters
(map (fn [[k v]] [(swagger-parameter k) v]))
(filter first)
(into {}))))
data
:parameters
(fn [parameters]
(->> parameters
(map (fn [[k v]] [(swagger-parameter k) v]))
(filter first)
(into {}))))
(-get-apidocs coercion specification)))))
;;

View file

@ -61,7 +61,7 @@
(if-not (partial-match? match)
match
(impl/throw-on-missing-path-params
(:template match) (:required match) path-params)))))
(:template match) (:required match) path-params)))))
(defn match->path
([match]
@ -87,15 +87,15 @@
(let [compiler (::trie/trie-compiler opts (trie/compiler))
names (impl/find-names compiled-routes opts)
[pl nl] (reduce
(fn [[pl nl] [p {:keys [name] :as data} result]]
(let [{:keys [path-params] :as route} (impl/parse p opts)
f #(if-let [path (impl/path-for route %)]
(->Match p data result (impl/url-decode-coll %) path)
(->PartialMatch p data result (impl/url-decode-coll %) path-params))]
[(conj pl (-> (trie/insert nil p (->Match p data result nil nil) opts) (trie/compile)))
(if name (assoc nl name f) nl)]))
[[] {}]
compiled-routes)
(fn [[pl nl] [p {:keys [name] :as data} result]]
(let [{:keys [path-params] :as route} (impl/parse p opts)
f #(if-let [path (impl/path-for route %)]
(->Match p data result (impl/url-decode-coll %) path)
(->PartialMatch p data result (impl/url-decode-coll %) path-params))]
[(conj pl (-> (trie/insert nil p (->Match p data result nil nil) opts) (trie/compile)))
(if name (assoc nl name f) nl)]))
[[] {}]
compiled-routes)
lookup (impl/fast-map nl)
matcher (trie/linear-matcher compiler pl true)
match-by-path (trie/path-matcher matcher compiler)
@ -133,18 +133,18 @@
([compiled-routes opts]
(when-let [wilds (seq (filter (impl/->wild-route? opts) compiled-routes))]
(exception/fail!
(str "can't create :lookup-router with wildcard routes: " wilds)
{:wilds wilds
:routes compiled-routes}))
(str "can't create :lookup-router with wildcard routes: " wilds)
{:wilds wilds
:routes compiled-routes}))
(let [names (impl/find-names compiled-routes opts)
[pl nl] (reduce
(fn [[pl nl] [p {:keys [name] :as data} result]]
[(assoc pl p (->Match p data result {} p))
(if name
(assoc nl name #(->Match p data result % p))
nl)])
[{} {}]
compiled-routes)
(fn [[pl nl] [p {:keys [name] :as data} result]]
[(assoc pl p (->Match p data result {} p))
(if name
(assoc nl name #(->Match p data result % p))
nl)])
[{} {}]
compiled-routes)
data (impl/fast-map pl)
lookup (impl/fast-map nl)
routes (impl/uncompile-routes compiled-routes)]
@ -183,15 +183,15 @@
(let [compiler (::trie/trie-compiler opts (trie/compiler))
names (impl/find-names compiled-routes opts)
[pl nl] (reduce
(fn [[pl nl] [p {:keys [name] :as data} result]]
(let [{:keys [path-params] :as route} (impl/parse p opts)
f #(if-let [path (impl/path-for route %)]
(->Match p data result (impl/url-decode-coll %) path)
(->PartialMatch p data result (impl/url-decode-coll %) path-params))]
[(trie/insert pl p (->Match p data result nil nil) opts)
(if name (assoc nl name f) nl)]))
[nil {}]
compiled-routes)
(fn [[pl nl] [p {:keys [name] :as data} result]]
(let [{:keys [path-params] :as route} (impl/parse p opts)
f #(if-let [path (impl/path-for route %)]
(->Match p data result (impl/url-decode-coll %) path)
(->PartialMatch p data result (impl/url-decode-coll %) path-params))]
[(trie/insert pl p (->Match p data result nil nil) opts)
(if name (assoc nl name f) nl)]))
[nil {}]
compiled-routes)
matcher (trie/compile pl compiler)
match-by-path (trie/path-matcher matcher compiler)
lookup (impl/fast-map nl)
@ -229,8 +229,8 @@
([compiled-routes opts]
(when (or (not= (count compiled-routes) 1) (some (impl/->wild-route? opts) compiled-routes))
(exception/fail!
(str ":single-static-path-router requires exactly 1 static route: " compiled-routes)
{:routes compiled-routes}))
(str ":single-static-path-router requires exactly 1 static route: " compiled-routes)
{:routes compiled-routes}))
(let [[n :as names] (impl/find-names compiled-routes opts)
[[p data result]] compiled-routes
p #?(:clj (.intern ^String p) :cljs p)

View file

@ -10,8 +10,8 @@
(map (fn [provide]
(when (contains? acc provide)
(exception/fail!
(str "multiple providers for: " provide)
{::multiple-providers provide}))
(str "multiple providers for: " provide)
{::multiple-providers provide}))
[provide dependent]))
(get-provides dependent)))
{} nodes))
@ -22,8 +22,8 @@
(if (contains? providers k)
(get providers k)
(exception/fail!
(str "provider missing for dependency: " k)
{::missing-provider k})))
(str "provider missing for dependency: " k)
{::missing-provider k})))
(defn post-order
"Put `nodes` in post-order. Can also be described as a reverse topological sort.

View file

@ -31,20 +31,20 @@
path " " (not-empty (select-keys route-data [:conflicting]))))]
(apply str "Router contains conflicting route paths:\n\n"
(mapv
(fn [[[path route-data] vals]]
(str (resolve-str path route-data)
"\n"
(str/join "\n" (mapv (fn [[path route-data]]
(resolve-str path route-data)) vals))
"\n\n"))
conflicts))))
(fn [[[path route-data] vals]]
(str (resolve-str path route-data)
"\n"
(str/join "\n" (mapv (fn [[path route-data]]
(resolve-str path route-data)) vals))
"\n\n"))
conflicts))))
(defmethod format-exception :name-conflicts [_ _ conflicts]
(apply str "Router contains conflicting route names:\n\n"
(mapv
(fn [[name vals]]
(str name "\n-> " (str/join "\n-> " (mapv first vals)) "\n"))
conflicts)))
(fn [[name vals]]
(str name "\n-> " (str/join "\n-> " (mapv first vals)) "\n"))
conflicts)))
(defmethod format-exception :reitit.impl/merge-data [_ _ data]
(str "Error merging route-data\n\n" (pr-str data)))

View file

@ -28,33 +28,33 @@
Also works on vectors. Maintains key for maps, order for vectors."
[f coll]
(reduce-kv
(fn [coll k v]
(if-some [v' (f v)]
(assoc coll k v')
coll))
coll
coll))
(fn [coll k v]
(if-some [v' (f v)]
(assoc coll k v')
coll))
coll
coll))
(defn walk [raw-routes {:keys [path data routes expand]
:or {data [], routes []}
:as opts}]
(letfn
[(walk-many [p m r]
(reduce #(into %1 (walk-one p m %2)) [] r))
(walk-one [pacc macc routes]
(if (vector? (first routes))
(walk-many pacc macc routes)
(when (string? (first routes))
(let [[path & [maybe-arg :as args]] routes
[data childs] (if (or (vector? maybe-arg)
(and (sequential? maybe-arg)
(sequential? (first maybe-arg)))
(nil? maybe-arg))
[{} args]
[maybe-arg (rest args)])
macc (into macc (expand data opts))
child-routes (walk-many (str pacc path) macc (keep identity childs))]
(if (seq childs) (seq child-routes) [[(str pacc path) macc]])))))]
[(walk-many [p m r]
(reduce #(into %1 (walk-one p m %2)) [] r))
(walk-one [pacc macc routes]
(if (vector? (first routes))
(walk-many pacc macc routes)
(when (string? (first routes))
(let [[path & [maybe-arg :as args]] routes
[data childs] (if (or (vector? maybe-arg)
(and (sequential? maybe-arg)
(sequential? (first maybe-arg)))
(nil? maybe-arg))
[{} args]
[maybe-arg (rest args)])
macc (into macc (expand data opts))
child-routes (walk-many (str pacc path) macc (keep identity childs))]
(if (seq childs) (seq child-routes) [[(str pacc path) macc]])))))]
(walk-one path (mapv identity data) raw-routes)))
(defn map-data [f routes]
@ -62,25 +62,25 @@
(defn merge-data [p x]
(reduce
(fn [acc [k v]]
(try
(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))
(fn [acc [k v]]
(try
(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))
coerce (into [] (keep #(coerce % opts)))))
coerce (into [] (keep #(coerce % opts)))))
(defn path-conflicting-routes [routes opts]
(let [parts-and-routes (mapv (fn [[s :as r]] [(trie/split-path s opts) r]) routes)]
(-> (into {} (comp (map-indexed (fn [index [p r]]
[r (reduce
(fn [acc [p' r']]
(if (trie/conflicting-parts? p p')
(conj acc r') acc))
#{} (subvec parts-and-routes (inc index)))]))
(fn [acc [p' r']]
(if (trie/conflicting-parts? p p')
(conj acc r') acc))
#{} (subvec parts-and-routes (inc index)))]))
(filter (comp seq second))) parts-and-routes)
(not-empty))))
@ -123,13 +123,13 @@
(defn path-for [route path-params]
(if (:path-params route)
(if-let [parts (reduce
(fn [acc part]
(if (string? part)
(conj acc part)
(if-let [p (get path-params (:value part))]
(conj acc p)
(reduced nil))))
[] (:path-parts route))]
(fn [acc part]
(if (string? part)
(conj acc part)
(if-let [p (get path-params (:value part))]
(conj acc p)
(reduced nil))))
[] (:path-parts route))]
(apply str parts))
(:path route)))
@ -138,8 +138,8 @@
(let [defined (-> path-params keys set)
missing (set/difference required defined)]
(ex/fail!
(str "missing path-params for route " template " -> " missing)
{:path-params path-params, :required required}))))
(str "missing path-params for route " template " -> " missing)
{:path-params path-params, :required required}))))
(defn fast-assoc
#?@(:clj [[^clojure.lang.Associative a k v] (.assoc a k v)]
@ -178,10 +178,10 @@
(if s
#?(:clj (if (.contains ^String s "%")
(URLDecoder/decode
(if (.contains ^String s "+")
(.replace ^String s "+" "%2B")
^String s)
"UTF-8"))
(if (.contains ^String s "+")
(.replace ^String s "+" "%2B")
^String s)
"UTF-8"))
:cljs (js/decodeURIComponent s))))
(defn url-decode [s]

View file

@ -37,36 +37,36 @@
(if-let [interceptor (if registry (registry this))]
(into-interceptor interceptor data opts)
(throw
(ex-info
(str
"Interceptor " this " not found in registry.\n\n"
(if (seq registry)
(str
"Available interceptors in registry:\n"
(with-out-str
(pprint/print-table [:id :description] (for [[k v] registry] {:id k :description v}))))
"see [reitit.interceptor/router] on how to add interceptor to the registry.\n") "\n")
{:id this
:registry registry}))))
(ex-info
(str
"Interceptor " this " not found in registry.\n\n"
(if (seq registry)
(str
"Available interceptors in registry:\n"
(with-out-str
(pprint/print-table [:id :description] (for [[k v] registry] {:id k :description v}))))
"see [reitit.interceptor/router] on how to add interceptor to the registry.\n") "\n")
{:id this
:registry registry}))))
#?(:clj clojure.lang.APersistentVector
:cljs cljs.core.PersistentVector)
(into-interceptor [[f & args :as form] data opts]
(when (and (seq args) (not (fn? f)))
(exception/fail!
(str "Invalid Interceptor form: " form "")
{:form form}))
(str "Invalid Interceptor form: " form "")
{:form form}))
(into-interceptor (apply f args) data opts))
#?(:clj clojure.lang.Fn
:cljs function)
(into-interceptor [this data opts]
(into-interceptor
{:name ::handler
::handler this
:enter (fn [ctx]
(assoc ctx :response (this (:request ctx))))}
data opts))
{:name ::handler
::handler this
:enter (fn [ctx]
(assoc ctx :response (this (:request ctx))))}
data opts))
#?(:clj clojure.lang.PersistentArrayMap
:cljs cljs.core.PersistentArrayMap)
@ -86,13 +86,13 @@
opts (assoc opts ::compiled (inc ^long compiled))]
(when (>= ^long compiled ^long *max-compile-depth*)
(exception/fail!
(str "Too deep Interceptor compilation - " compiled)
{:this this, :data data, :opts opts}))
(str "Too deep Interceptor compilation - " compiled)
{:this this, :data data, :opts opts}))
(if-let [interceptor (into-interceptor (compile data opts) data opts)]
(map->Interceptor
(merge
(dissoc this :compile)
(impl/strip-nils interceptor)))))))
(merge
(dissoc this :compile)
(impl/strip-nils interceptor)))))))
nil
(into-interceptor [_ _ _]))
@ -122,9 +122,9 @@
([[_ {:keys [interceptors handler] :as data}] {::keys [queue] :as opts} _]
(let [chain (chain (into (vec interceptors) [handler]) data opts)]
(map->Endpoint
{:interceptors chain
:queue ((or queue identity) chain)
:data data}))))
{:interceptors chain
:queue ((or queue identity) chain)
:data data}))))
(defn transform-butlast
"Returns a function to that takes a interceptor transformation function and
@ -132,8 +132,8 @@
[f]
(fn [interceptors]
(concat
(f (butlast interceptors))
[(last interceptors)])))
(f (butlast interceptors))
[(last interceptors)])))
(defn router
"Creates a [[reitit.core/Router]] from raw route data and optionally an options map with

View file

@ -21,17 +21,17 @@
(if-let [middleware (if registry (registry this))]
(into-middleware middleware data opts)
(throw
(ex-info
(str
"Middleware " this " not found in registry.\n\n"
(if (seq registry)
(str
"Available middleware in registry:\n"
(with-out-str
(pprint/print-table [:id :description] (for [[k v] registry] {:id k :description v}))))
"see [reitit.middleware/router] on how to add middleware to the registry.\n") "\n")
{:id this
:registry registry}))))
(ex-info
(str
"Middleware " this " not found in registry.\n\n"
(if (seq registry)
(str
"Available middleware in registry:\n"
(with-out-str
(pprint/print-table [:id :description] (for [[k v] registry] {:id k :description v}))))
"see [reitit.middleware/router] on how to add middleware to the registry.\n") "\n")
{:id this
:registry registry}))))
#?(:clj clojure.lang.APersistentVector
:cljs cljs.core.PersistentVector)
@ -43,7 +43,7 @@
:cljs function)
(into-middleware [this _ _]
(map->Middleware
{:wrap this}))
{:wrap this}))
#?(:clj clojure.lang.PersistentArrayMap
:cljs cljs.core.PersistentArrayMap)
@ -63,13 +63,13 @@
opts (assoc opts ::compiled (inc ^long compiled))]
(when (>= ^long compiled ^long *max-compile-depth*)
(exception/fail!
(str "Too deep Middleware compilation - " compiled)
{:this this, :data data, :opts opts}))
(str "Too deep Middleware compilation - " compiled)
{:this this, :data data, :opts opts}))
(if-let [middeware (into-middleware (compile data opts) data opts)]
(map->Middleware
(merge
(dissoc this :compile)
(impl/strip-nils middeware)))))))
(merge
(dissoc this :compile)
(impl/strip-nils middeware)))))))
nil
(into-middleware [_ _ _]))
@ -77,10 +77,10 @@
(defn- ensure-handler! [path data scope]
(when-not (:handler data)
(exception/fail!
(str "path \"" path "\" doesn't have a :handler defined"
(if scope (str " for " scope)))
(merge {:path path, :data data}
(if scope {:scope scope})))))
(str "path \"" path "\" doesn't have a :handler defined"
(if scope (str " for " scope)))
(merge {:path path, :data data}
(if scope {:scope scope})))))
(defn- expand-and-transform
[middleware data {::keys [transform] :or {transform identity} :as opts}]
@ -116,9 +116,9 @@
(ensure-handler! path data scope)
(let [middleware (expand-and-transform middleware data opts)]
(map->Endpoint
{:handler (compile-handler middleware handler)
:middleware middleware
:data data}))))
{:handler (compile-handler middleware handler)
:middleware middleware
:data data}))))
(defn router
"Creates a [[reitit.core/Router]] from raw route data and optionally an options map with

View file

@ -16,9 +16,9 @@
(s/def ::raw-route
(s/nilable
(s/cat :path ::path
:arg (s/? ::arg)
:childs (s/* (s/and (s/nilable ::raw-routes))))))
(s/cat :path ::path
:arg (s/? ::arg)
:childs (s/* (s/and (s/nilable ::raw-routes))))))
(s/def ::raw-routes
(s/or :route ::raw-route
@ -60,19 +60,19 @@
(s/def ::opts
(s/nilable
(s/keys :opt-un [:reitit.router/path
:reitit.router/routes
:reitit.router/data
:reitit.router/expand
:reitit.router/coerce
:reitit.router/compile
:reitit.router/conflicts
:reitit.router/router])))
(s/keys :opt-un [:reitit.router/path
:reitit.router/routes
:reitit.router/data
:reitit.router/expand
:reitit.router/coerce
:reitit.router/compile
:reitit.router/conflicts
:reitit.router/router])))
(s/fdef r/router
:args (s/or :1arity (s/cat :data (s/spec ::raw-routes))
:2arity (s/cat :data (s/spec ::raw-routes), :opts ::opts))
:ret ::router)
:args (s/or :1arity (s/cat :data (s/spec ::raw-routes))
:2arity (s/cat :data (s/spec ::raw-routes), :opts ::opts))
:ret ::router)
;;
;; coercion
@ -128,15 +128,15 @@
(defn validate [routes {:keys [spec] ::keys [wrap] :or {spec ::default-data, wrap identity}}]
(when-let [problems (validate-route-data routes wrap spec)]
(exception/fail!
::invalid-route-data
{:problems problems})))
::invalid-route-data
{:problems problems})))
(defmethod exception/format-exception :reitit.spec/invalid-route-data [_ _ {:keys [problems]}]
(apply str "Invalid route data:\n\n"
(mapv
(fn [{:keys [path scope data spec]}]
(str "-- On route -----------------------\n\n"
(pr-str path) (if scope (str " " (pr-str scope))) "\n\n"
(pr-str data) "\n\n"
(s/explain-str spec data) "\n"))
problems)))
(fn [{:keys [path scope data spec]}]
(str "-- On route -----------------------\n\n"
(pr-str path) (if scope (str " " (pr-str scope))) "\n\n"
(pr-str data) "\n\n"
(s/explain-str spec data) "\n"))
problems)))

View file

@ -90,12 +90,12 @@
(defn join-path [xs]
(reduce
(fn [s x]
(str s (cond
(string? x) x
(instance? Wild x) (str "{" (-> x :value str (subs 1)) "}")
(instance? CatchAll x) (str "{*" (-> x :value str (subs 1)) "}"))))
"" xs))
(fn [s x]
(str s (cond
(string? x) x
(instance? Wild x) (str "{" (-> x :value str (subs 1)) "}")
(instance? CatchAll x) (str "{*" (-> x :value str (subs 1)) "}"))))
"" xs))
(defn normalize [s opts]
(-> s (split-path opts) (join-path)))
@ -172,25 +172,25 @@
:else
(or
(reduce
(fn [_ [p n]]
(if-let [cp (common-prefix p path)]
(if (= cp p)
(reduce
(fn [_ [p n]]
(if-let [cp (common-prefix p path)]
(if (= cp p)
;; insert into child node
(let [n' (-insert n (conj ps (subs path (count p))) fp params data)]
(reduced (assoc-in node [:children p] n')))
(let [n' (-insert n (conj ps (subs path (count p))) fp params data)]
(reduced (assoc-in node [:children p] n')))
;; split child node
(let [rp (subs p (count cp))
rp' (subs path (count cp))
n' (-insert (-node {}) ps fp params data)
n'' (-insert (-node {:children {rp n, rp' n'}}) nil nil nil nil)]
(reduced (update node :children (fn [children]
(-> children
(dissoc p)
(assoc cp n'')))))))))
nil (:children node))
(let [rp (subs p (count cp))
rp' (subs path (count cp))
n' (-insert (-node {}) ps fp params data)
n'' (-insert (-node {:children {rp n, rp' n'}}) nil nil nil nil)]
(reduced (update node :children (fn [children]
(-> children
(dissoc p)
(assoc cp n'')))))))))
nil (:children node))
;; new child node
(assoc-in node [:children path] (-insert (-node {}) ps fp params data))))]
(assoc-in node [:children path] (-insert (-node {}) ps fp params data))))]
(if-let [child (get-in node' [:children ""])]
;; optimize by removing empty paths
(-> (merge-with merge (dissoc node' :data) child)
@ -202,10 +202,10 @@
(if percent?
#?(:cljs (js/decodeURIComponent param)
:clj (URLDecoder/decode
(if (.contains ^String param "+")
(.replace ^String param "+" "%2B")
param)
"UTF-8"))
(if (.contains ^String param "+")
(.replace ^String param "+" "%2B")
param)
"UTF-8"))
param)))
;;
@ -313,13 +313,13 @@
(def record-parameters
"Memoized function to transform parameters into runtime generated Record."
(memoize
(fn [keys]
(if (some qualified-keyword? keys)
(map-parameters keys)
(let [sym (gensym "PathParams")
ctor (symbol (str "map->" sym))]
(binding [*ns* (find-ns 'user)]
(eval `(do (defrecord ~sym ~(mapv (comp symbol name) keys)) (~ctor {}))))))))))
(fn [keys]
(if (some qualified-keyword? keys)
(map-parameters keys)
(let [sym (gensym "PathParams")
ctor (symbol (str "map->" sym))]
(binding [*ns* (find-ns 'user)]
(eval `(do (defrecord ~sym ~(mapv (comp symbol name) keys)) (~ctor {}))))))))))
(defn insert
"Returns a trie with routes added to it."
@ -327,9 +327,9 @@
(insert nil routes))
([node routes]
(reduce
(fn [acc [p d]]
(insert acc p d))
node routes))
(fn [acc [p d]]
(insert acc p d))
node routes))
([node path data]
(insert node path data nil))
([node path data {::keys [parameters] :or {parameters map-parameters} :as opts}]
@ -355,12 +355,12 @@
(cond-> data (conj (data-matcher compiler params data)))
(into (for [[p c] children] (static-matcher compiler p (compile c compiler (conj cp p)))))
(into
(for [[p c] wilds]
(let [pv (:value p)
ends (ends c)]
(if (next ends)
(ex/fail! ::multiple-terminators {:terminators ends, :path (join-path (conj cp p))})
(wild-matcher compiler pv (ffirst ends) (compile c compiler (conj cp pv)))))))
(for [[p c] wilds]
(let [pv (:value p)
ends (ends c)]
(if (next ends)
(ex/fail! ::multiple-terminators {:terminators ends, :path (join-path (conj cp p))})
(wild-matcher compiler pv (ffirst ends) (compile c compiler (conj cp pv)))))))
(into (for [[p c] catch-all] (catch-all-matcher compiler (:value p) params (:data c)))))]
(cond
(> (count matchers) 1) (linear-matcher compiler matchers false)
@ -387,61 +387,61 @@
(comment
(->
[["/v2/whoami" 1]
["/v2/users/:user-id/datasets" 2]
["/v2/public/projects/:project-id/datasets" 3]
["/v1/public/topics/:topic" 4]
["/v1/users/:user-id/orgs/:org-id" 5]
["/v1/search/topics/:term" 6]
["/v1/users/:user-id/invitations" 7]
["/v1/users/:user-id/topics" 9]
["/v1/users/:user-id/bookmarks/followers" 10]
["/v2/datasets/:dataset-id" 11]
["/v1/orgs/:org-id/usage-stats" 12]
["/v1/orgs/:org-id/devices/:client-id" 13]
["/v1/messages/user/:user-id" 14]
["/v1/users/:user-id/devices" 15]
["/v1/public/users/:user-id" 16]
["/v1/orgs/:org-id/errors" 17]
["/v1/public/orgs/:org-id" 18]
["/v1/orgs/:org-id/invitations" 19]
["/v1/users/:user-id/device-errors" 22]
["/v2/login" 23]
["/v1/users/:user-id/usage-stats" 24]
["/v2/users/:user-id/devices" 25]
["/v1/users/:user-id/claim-device/:client-id" 26]
["/v2/public/projects/:project-id" 27]
["/v2/public/datasets/:dataset-id" 28]
["/v2/users/:user-id/topics/bulk" 29]
["/v1/messages/device/:client-id" 30]
["/v1/users/:user-id/owned-orgs" 31]
["/v1/topics/:topic" 32]
["/v1/users/:user-id/bookmark/:topic" 33]
["/v1/orgs/:org-id/members/:user-id" 34]
["/v1/users/:user-id/devices/:client-id" 35]
["/v1/users/:user-id" 36]
["/v1/orgs/:org-id/devices" 37]
["/v1/orgs/:org-id/members" 38]
["/v2/orgs/:org-id/topics" 40]
["/v1/whoami" 41]
["/v1/orgs/:org-id" 42]
["/v1/users/:user-id/api-key" 43]
["/v2/schemas" 44]
["/v2/users/:user-id/topics" 45]
["/v1/orgs/:org-id/confirm-membership/:token" 46]
["/v2/topics/:topic" 47]
["/v1/messages/topic/:topic" 48]
["/v1/users/:user-id/devices/:client-id/reset-password" 49]
["/v2/topics" 50]
["/v1/login" 51]
["/v1/users/:user-id/orgs" 52]
["/v2/public/messages/dataset/:dataset-id" 53]
["/v1/topics" 54]
["/v1/orgs" 55]
["/v1/users/:user-id/bookmarks" 56]
["/v1/orgs/:org-id/topics" 57]
["/command1 {arg1} {arg2}" ::cmd1]
["/command2 {arg1} {arg2} {arg3}" ::cmd2]]
(insert)
(compile)
(pretty)))
[["/v2/whoami" 1]
["/v2/users/:user-id/datasets" 2]
["/v2/public/projects/:project-id/datasets" 3]
["/v1/public/topics/:topic" 4]
["/v1/users/:user-id/orgs/:org-id" 5]
["/v1/search/topics/:term" 6]
["/v1/users/:user-id/invitations" 7]
["/v1/users/:user-id/topics" 9]
["/v1/users/:user-id/bookmarks/followers" 10]
["/v2/datasets/:dataset-id" 11]
["/v1/orgs/:org-id/usage-stats" 12]
["/v1/orgs/:org-id/devices/:client-id" 13]
["/v1/messages/user/:user-id" 14]
["/v1/users/:user-id/devices" 15]
["/v1/public/users/:user-id" 16]
["/v1/orgs/:org-id/errors" 17]
["/v1/public/orgs/:org-id" 18]
["/v1/orgs/:org-id/invitations" 19]
["/v1/users/:user-id/device-errors" 22]
["/v2/login" 23]
["/v1/users/:user-id/usage-stats" 24]
["/v2/users/:user-id/devices" 25]
["/v1/users/:user-id/claim-device/:client-id" 26]
["/v2/public/projects/:project-id" 27]
["/v2/public/datasets/:dataset-id" 28]
["/v2/users/:user-id/topics/bulk" 29]
["/v1/messages/device/:client-id" 30]
["/v1/users/:user-id/owned-orgs" 31]
["/v1/topics/:topic" 32]
["/v1/users/:user-id/bookmark/:topic" 33]
["/v1/orgs/:org-id/members/:user-id" 34]
["/v1/users/:user-id/devices/:client-id" 35]
["/v1/users/:user-id" 36]
["/v1/orgs/:org-id/devices" 37]
["/v1/orgs/:org-id/members" 38]
["/v2/orgs/:org-id/topics" 40]
["/v1/whoami" 41]
["/v1/orgs/:org-id" 42]
["/v1/users/:user-id/api-key" 43]
["/v2/schemas" 44]
["/v2/users/:user-id/topics" 45]
["/v1/orgs/:org-id/confirm-membership/:token" 46]
["/v2/topics/:topic" 47]
["/v1/messages/topic/:topic" 48]
["/v1/users/:user-id/devices/:client-id/reset-password" 49]
["/v2/topics" 50]
["/v1/login" 51]
["/v1/users/:user-id/orgs" 52]
["/v2/public/messages/dataset/:dataset-id" 53]
["/v1/topics" 54]
["/v1/orgs" 55]
["/v1/users/:user-id/bookmarks" 56]
["/v1/orgs/:org-id/topics" 57]
["/command1 {arg1} {arg2}" ::cmd1]
["/command2 {arg1} {arg2} {arg3}" ::cmd2]]
(insert)
(compile)
(pretty)))

View file

@ -152,13 +152,13 @@
(printer nil))
([options]
(map->EdnPrinter
(merge
{:width 80
:symbols {}
:print-length *print-length*
:print-level *print-level*
:print-meta *print-meta*}
options))))
(merge
{:width 80
:symbols {}
:print-length *print-length*
:print-level *print-level*
:print-meta *print-meta*}
options))))
(defn pprint
([x] (pprint x {}))
@ -209,13 +209,13 @@
(defn exception-str [message source printer]
(with-out-str
(print-doc
[:group
(title "Router creation failed" source printer)
[:break] [:break]
message
[:break]
(footer printer)]
printer)))
[:group
(title "Router creation failed" source printer)
[:break] [:break]
message
[:break]
(footer printer)]
printer)))
(defmulti format-exception (fn [type _ _] type))
@ -231,11 +231,11 @@
(defn de-expound-colors [^String s mappings]
(let [s' (reduce
(fn [s [from to]]
(.replace ^String s
^String (expound.ansi/esc [from])
^String (-start (colors to))))
s mappings)]
(fn [s [from to]]
(.replace ^String s
^String (expound.ansi/esc [from])
^String (-start (colors to))))
s mappings)]
(.replace ^String s'
^String (expound.ansi/esc [:none])
(str (expound.ansi/esc [:none]) (-start (colors :text))))))
@ -254,9 +254,9 @@
(def expound-printer
(expound.alpha/custom-printer
{:theme :figwheel-theme
:show-valid-values? false
:print-specs? false}))
{:theme :figwheel-theme
:show-valid-values? false
:print-specs? false}))
;;
;; Formatters
@ -276,18 +276,18 @@
" ")
(edn (not-empty (select-keys route-data [:conflicting])))])]
(into
[:group]
(mapv
(fn [[[path route-data] vals]]
[:group
(path-report path route-data)
(into
[:group]
(map
(fn [[path route-data]] (path-report path route-data))
vals))
[:break]])
conflicts)))
[:group]
(mapv
(fn [[[path route-data] vals]]
[:group
(path-report path route-data)
(into
[:group]
(map
(fn [[path route-data]] (path-report path route-data))
vals))
[:break]])
conflicts)))
[:span (text "Either fix the conflicting paths or disable the conflict resolution")
[:break] (text "by setting route data for conflicting route: ") [:break] [:break]
(edn {:conflicting true} {:margin 3})
@ -302,19 +302,19 @@
(text "Router contains conflicting route names:")
[:break] [:break]
(into
[:group]
(mapv
(fn [[name vals]]
[:group
[:span (text name)]
[:break]
(into
[:group]
(map
(fn [p] [:span (color :grey "-> " p) [:break]])
(mapv first vals)))
[:break]])
conflicts))
[:group]
(mapv
(fn [[name vals]]
[:group
[:span (text name)]
[:break]
(into
[:group]
(map
(fn [p] [:span (color :grey "-> " p) [:break]])
(mapv first vals)))
[:break]])
conflicts))
(color :white "https://cljdoc.org/d/metosin/reitit/CURRENT/doc/basics/route-conflicts")
[:break]])
@ -323,22 +323,22 @@
(text "Invalid route data:")
[:break] [:break]
(into
[:group]
(map
(fn [{:keys [data path spec scope]}]
[:group
[:span (color :grey "-- On route -----------------------")]
[:break]
[:break]
(text path) (if scope [:span " " (text scope)])
[:break]
[:break]
(-> (s/explain-data spec data)
(expound-printer)
(with-out-str)
(fippify))
[:break]])
problems))
[:group]
(map
(fn [{:keys [data path spec scope]}]
[:group
[:span (color :grey "-- On route -----------------------")]
[:break]
[:break]
(text path) (if scope [:span " " (text scope)])
[:break]
[:break]
(-> (s/explain-data spec data)
(expound-printer)
(with-out-str)
(fippify))
[:break]])
problems))
(color :white "https://cljdoc.org/d/metosin/reitit/CURRENT/doc/basics/route-data-validation")
[:break]])

View file

@ -64,11 +64,11 @@
(let [defined (-> path-params keys set)
missing (set/difference (:required match) defined)]
(js/console.warn
"missing path-params for route" name
{:template (:template match)
:missing missing
:path-params path-params
:required (:required match)})
"missing path-params for route" name
{:template (:template match)
:missing missing
:path-params path-params
:required (:required match)})
nil))
match)
(do (js/console.warn "missing route" name)

View file

@ -38,7 +38,7 @@
(when (nil? @history)
(reset! history this))
(on-navigate m this))
opts))
opts))
(defn
^{:see-also ["reitit.frontend.history/href"]}

View file

@ -9,16 +9,16 @@
(defn coerce-handler [[path data] {:keys [expand] :as opts}]
[path (reduce
(fn [acc method]
(if (contains? acc method)
(update acc method expand opts)
acc)) data ring/http-methods)])
(fn [acc method]
(if (contains? acc method)
(update acc method expand opts)
acc)) data ring/http-methods)])
(defn compile-result [[path data] {:keys [::default-options-endpoint expand] :as opts}]
(let [[top childs] (ring/group-keys data)
childs (cond-> childs
(and (not (:options childs)) (not (:handler top)) default-options-endpoint)
(assoc :options (expand default-options-endpoint opts)))
(and (not (:options childs)) (not (:handler top)) default-options-endpoint)
(assoc :options (expand default-options-endpoint opts)))
compile (fn [[path data] opts scope]
(interceptor/compile-result [path data] opts scope))
->endpoint (fn [p d m s]
@ -29,19 +29,19 @@
(assoc :method m))))
->methods (fn [any? data]
(reduce
(fn [acc method]
(cond-> acc
any? (assoc method (->endpoint path data method nil))))
(ring/map->Methods {})
ring/http-methods))]
(fn [acc method]
(cond-> acc
any? (assoc method (->endpoint path data method nil))))
(ring/map->Methods {})
ring/http-methods))]
(if-not (seq childs)
(->methods true top)
(reduce-kv
(fn [acc method data]
(let [data (meta-merge top data)]
(assoc acc method (->endpoint path data method method))))
(->methods (:handler top) data)
childs))))
(fn [acc method data]
(let [data (meta-merge top data)]
(assoc acc method (->endpoint path data method method))))
(->methods (:handler top) data)
childs))))
(defn router
"Creates a [[reitit.core/Router]] from raw route data and optionally an options map with
@ -133,7 +133,7 @@
(assoc ::interceptor/queue (partial interceptor/queue executor))
(dissoc :data) ; data is already merged into routes
(cond-> (seq interceptors)
(update-in [:data :interceptors] (partial into (vec interceptors)))))
(update-in [:data :interceptors] (partial into (vec interceptors)))))
router (reitit.http/router (r/routes router) router-opts) ;; will re-compile the interceptors
enrich-request (ring/create-enrich-request inject-match? inject-router?)
enrich-default-request (ring/create-enrich-default-request inject-router?)]

View file

@ -22,5 +22,5 @@
[routes {:keys [spec ::rs/wrap] :or {spec ::data, wrap identity}}]
(when-let [problems (rrs/validate-route-data routes :interceptors wrap spec)]
(exception/fail!
::rs/invalid-route-data
{:problems problems})))
::rs/invalid-route-data
{:problems problems})))

View file

@ -36,16 +36,16 @@
[stages {:keys [enter leave error name] :as interceptor}]
(if (->> (select-keys interceptor stages) (vals) (keep identity) (seq))
(cond-> {:name ::diff}
(and enter (stages :enter)) (assoc :enter (handle name :enter))
(and leave (stages :leave)) (assoc :leave (handle name :leave))
(and error (stages :error)) (assoc :error (handle name :error)))))
(and enter (stages :enter)) (assoc :enter (handle name :enter))
(and leave (stages :leave)) (assoc :leave (handle name :leave))
(and error (stages :error)) (assoc :error (handle name :error)))))
(defn print-context-diffs
"A interceptor chain transformer that adds a context diff printer between all interceptors"
[interceptors]
(reduce
(fn [chain interceptor]
(into chain (keep identity [(diff-interceptor #{:leave :error} interceptor)
interceptor
(diff-interceptor #{:enter} interceptor)])))
[(diff-interceptor #{:enter :leave :error} {:enter identity})] interceptors))
(fn [chain interceptor]
(into chain (keep identity [(diff-interceptor #{:leave :error} interceptor)
interceptor
(diff-interceptor #{:enter} interceptor)])))
[(diff-interceptor #{:enter :leave :error} {:enter identity})] interceptors))

View file

@ -25,11 +25,11 @@
error-handler (or (get handlers type)
(get handlers ex-class)
(some
(partial get handlers)
(descendants type))
(partial get handlers)
(descendants type))
(some
(partial get handlers)
(super-classes ex-class))
(partial get handlers)
(super-classes ex-class))
(get handlers ::default))]
(if-let [wrap (get handlers ::wrap)]
(wrap error-handler error request)

View file

@ -18,14 +18,14 @@
(def temp-file-part
"Spec for file param created by ring.middleware.multipart-params.temp-file store."
(st/spec
{:spec (s/keys :req-un [::filename ::content-type ::tempfile ::size])
:swagger/type "file"}))
{:spec (s/keys :req-un [::filename ::content-type ::tempfile ::size])
:swagger/type "file"}))
(def bytes-part
"Spec for file param created by ring.middleware.multipart-params.byte-array store."
(st/spec
{:spec (s/keys :req-un [::filename ::content-type ::bytes])
:swagger/type "file"}))
{:spec (s/keys :req-un [::filename ::content-type ::bytes])
:swagger/type "file"}))
(defn- coerced-request [request coercers]
(if-let [coerced (if coercers (coercion/coerce-request coercers request))]
@ -49,7 +49,7 @@
:compile (fn [{:keys [parameters coercion]} opts]
(if-let [multipart (:multipart parameters)]
(let [parameter-coercion {:multipart (coercion/->ParameterCoercion
:multipart-params :string true true)}
:multipart-params :string true true)}
opts (assoc opts ::coercion/parameter-coercion parameter-coercion)
coercers (if multipart (coercion/request-coercers coercion parameters opts))]
{:data {:swagger {:consumes ^:replace #{"multipart/form-data"}}}

View file

@ -40,10 +40,10 @@
:compile (fn [{:keys [muuntaja parameters]} _]
(if-let [muuntaja (or muuntaja default-muuntaja)]
(merge
(stripped (muuntaja.interceptor/format-interceptor muuntaja))
(if (publish-swagger-data? parameters)
{:data {:swagger {:produces (displace (m/encodes muuntaja))
:consumes (displace (m/decodes muuntaja))}}}))))}))
(stripped (muuntaja.interceptor/format-interceptor muuntaja))
(if (publish-swagger-data? parameters)
{:data {:swagger {:produces (displace (m/encodes muuntaja))
:consumes (displace (m/decodes muuntaja))}}}))))}))
(defn format-negotiate-interceptor
"Interceptor for content-negotiation.
@ -87,9 +87,9 @@
:compile (fn [{:keys [muuntaja parameters]} _]
(if-let [muuntaja (or muuntaja default-muuntaja)]
(merge
(stripped (muuntaja.interceptor/format-request-interceptor muuntaja))
(when (publish-swagger-data? parameters)
{:data {:swagger {:consumes (displace (m/decodes muuntaja))}}}))))}))
(stripped (muuntaja.interceptor/format-request-interceptor muuntaja))
(when (publish-swagger-data? parameters)
{:data {:swagger {:consumes (displace (m/decodes muuntaja))}}}))))}))
(defn format-response-interceptor
"Interceptor for response formatting.
@ -112,6 +112,6 @@
:compile (fn [{:keys [muuntaja parameters]} _]
(if-let [muuntaja (or muuntaja default-muuntaja)]
(merge
(stripped (muuntaja.interceptor/format-response-interceptor muuntaja))
(when (publish-swagger-data? parameters)
{:data {:swagger {:produces (displace (m/encodes muuntaja))}}}))))}))
(stripped (muuntaja.interceptor/format-response-interceptor muuntaja))
(when (publish-swagger-data? parameters)
{:data {:swagger {:produces (displace (m/encodes muuntaja))}}}))))}))

View file

@ -26,9 +26,9 @@
(reify TransformationProvider
(-transformer [_ {:keys [strip-extra-keys default-values]}]
(mt/transformer
(if strip-extra-keys (mt/strip-extra-keys-transformer))
transformer
(if default-values (mt/default-value-transformer))))))
(if strip-extra-keys (mt/strip-extra-keys-transformer))
transformer
(if default-values (mt/default-value-transformer))))))
(def string-transformer-provider (-provider (mt/string-transformer)))
(def json-transformer-provider (-provider (mt/json-transformer)))
@ -61,7 +61,7 @@
transformed
(let [error (-explain coercer transformed)]
(coercion/map->CoercionError
(assoc error :transformed transformed)))))
(assoc error :transformed transformed)))))
value))
;; encode: decode -> validate -> encode
(fn [value format]
@ -71,7 +71,7 @@
(-encode coercer transformed)
(let [error (-explain coercer transformed)]
(coercion/map->CoercionError
(assoc error :transformed transformed))))
(assoc error :transformed transformed))))
value))))))))
;;
@ -91,15 +91,15 @@
(defmethod extract-parameter :default [in schema options]
(let [{:keys [properties required]} (swagger/transform schema (merge options {:in in, :type :parameter}))]
(mapv
(fn [[k {:keys [type] :as schema}]]
(merge
{:in (name in)
:name k
:description (:description schema "")
:type type
:required (contains? (set required) k)}
schema))
properties)))
(fn [[k {:keys [type] :as schema}]]
(merge
{:in (name in)
:name k
:description (:description schema "")
:type type
:required (contains? (set required) k)}
schema))
properties)))
;;
;; public api
@ -143,39 +143,39 @@
(-get-apidocs [_ specification {:keys [parameters responses]}]
(case specification
:swagger (merge
(if parameters
{:parameters
(->> (for [[in schema] parameters
parameter (extract-parameter in (compile schema options) options)]
parameter)
(into []))})
(if responses
{:responses
(into
(empty responses)
(for [[status response] responses]
[status (as-> response $
(set/rename-keys $ {:body :schema})
(update $ :description (fnil identity ""))
(if (:schema $)
(-> $
(update :schema compile options)
(update :schema swagger/transform {:type :schema}))
$))]))}))
(if parameters
{:parameters
(->> (for [[in schema] parameters
parameter (extract-parameter in (compile schema options) options)]
parameter)
(into []))})
(if responses
{:responses
(into
(empty responses)
(for [[status response] responses]
[status (as-> response $
(set/rename-keys $ {:body :schema})
(update $ :description (fnil identity ""))
(if (:schema $)
(-> $
(update :schema compile options)
(update :schema swagger/transform {:type :schema}))
$))]))}))
(throw
(ex-info
(str "Can't produce Schema apidocs for " specification)
{:type specification, :coercion :schema}))))
(ex-info
(str "Can't produce Schema apidocs for " specification)
{:type specification, :coercion :schema}))))
(-compile-model [_ model _] (compile model options))
(-open-model [_ schema] schema)
(-encode-error [_ error]
(cond-> error
(show? :humanized) (assoc :humanized (me/humanize error {:wrap :message}))
(show? :schema) (update :schema edn/write-string opts)
(show? :errors) (-> (me/with-error-messages opts)
(update :errors (partial map #(update % :schema edn/write-string opts))))
(seq error-keys) (select-keys error-keys)
encode-error (encode-error)))
(show? :humanized) (assoc :humanized (me/humanize error {:wrap :message}))
(show? :schema) (update :schema edn/write-string opts)
(show? :errors) (-> (me/with-error-messages opts)
(update :errors (partial map #(update % :schema edn/write-string opts))))
(seq error-keys) (select-keys error-keys)
encode-error (encode-error)))
(-request-coercer [_ type schema]
(-coercer (compile schema options) type transformers :decode opts))
(-response-coercer [_ schema]

View file

@ -48,6 +48,6 @@
printer between all middleware."
[chain]
(reduce
(fn [chain mw]
(into chain [mw (print-diff-middleware (select-keys mw [:name]))]))
[(print-diff-middleware)] chain))
(fn [chain mw]
(into chain [mw (print-diff-middleware (select-keys mw [:name]))]))
[(print-diff-middleware)] chain))

View file

@ -25,11 +25,11 @@
error-handler (or (get handlers type)
(get handlers ex-class)
(some
(partial get handlers)
(descendants type))
(partial get handlers)
(descendants type))
(some
(partial get handlers)
(super-classes ex-class))
(partial get handlers)
(super-classes ex-class))
(get handlers ::default))]
(if-let [wrap (get handlers ::wrap)]
(wrap error-handler error request)

View file

@ -15,14 +15,14 @@
(def temp-file-part
"Spec for file param created by ring.middleware.multipart-params.temp-file store."
(st/spec
{:spec (s/keys :req-un [::filename ::content-type ::tempfile ::size])
:swagger/type "file"}))
{:spec (s/keys :req-un [::filename ::content-type ::tempfile ::size])
:swagger/type "file"}))
(def bytes-part
"Spec for file param created by ring.middleware.multipart-params.byte-array store."
(st/spec
{:spec (s/keys :req-un [::filename ::content-type ::bytes])
:swagger/type "file"}))
{:spec (s/keys :req-un [::filename ::content-type ::bytes])
:swagger/type "file"}))
(defn- coerced-request [request coercers]
(if-let [coerced (if coercers (coercion/coerce-request coercers request))]
@ -33,7 +33,7 @@
(fn [{:keys [parameters coercion]} opts]
(if-let [multipart (:multipart parameters)]
(let [parameter-coercion {:multipart (coercion/->ParameterCoercion
:multipart-params :string true true)}
:multipart-params :string true true)}
opts (assoc opts ::coercion/parameter-coercion parameter-coercion)
coercers (if multipart (coercion/request-coercers coercion parameters opts))]
{:data {:swagger {:consumes ^:replace #{"multipart/form-data"}}}

View file

@ -34,10 +34,10 @@
:compile (fn [{:keys [muuntaja parameters]} _]
(if muuntaja
(merge
(if (publish-swagger-data? parameters)
{:data {:swagger {:produces (displace (m/encodes muuntaja))
:consumes (displace (m/decodes muuntaja))}}})
{:wrap #(muuntaja.middleware/wrap-format % muuntaja)})))})
(if (publish-swagger-data? parameters)
{:data {:swagger {:produces (displace (m/encodes muuntaja))
:consumes (displace (m/decodes muuntaja))}}})
{:wrap #(muuntaja.middleware/wrap-format % muuntaja)})))})
(def format-negotiate-middleware
"Middleware for content-negotiation.
@ -71,9 +71,9 @@
:compile (fn [{:keys [muuntaja parameters]} _]
(if muuntaja
(merge
(when (publish-swagger-data? parameters)
{:data {:swagger {:consumes (displace (m/decodes muuntaja))}}})
{:wrap #(muuntaja.middleware/wrap-format-request % muuntaja)})))})
(when (publish-swagger-data? parameters)
{:data {:swagger {:consumes (displace (m/decodes muuntaja))}}})
{:wrap #(muuntaja.middleware/wrap-format-request % muuntaja)})))})
(def format-response-middleware
"Middleware for response formatting.
@ -91,6 +91,6 @@
:compile (fn [{:keys [muuntaja parameters]} _]
(if muuntaja
(merge
(when (publish-swagger-data? parameters)
{:data {:swagger {:produces (displace (m/encodes muuntaja))}}})
{:wrap #(muuntaja.middleware/wrap-format-response % muuntaja)})))})
(when (publish-swagger-data? parameters)
{:data {:swagger {:produces (displace (m/encodes muuntaja))}}})
{:wrap #(muuntaja.middleware/wrap-format-response % muuntaja)})))})

View file

@ -36,9 +36,9 @@
interceptor
(->> (select-keys interceptor [:enter :leave :error]) (vals) (keep identity) (seq))
(interceptor/interceptor
(if (error-without-arity-2? interceptor)
(wrap-error-arity-2->1 interceptor)
interceptor))))
(if (error-without-arity-2? interceptor)
(wrap-error-arity-2->1 interceptor)
interceptor))))
;;
;; Public API
@ -62,11 +62,11 @@
(routing-interceptor router default-handler nil))
([router default-handler {:keys [interceptors]}]
(interceptor/interceptor
(reitit.http/routing-interceptor
router
default-handler
{:executor pedestal-executor
:interceptors interceptors}))))
(reitit.http/routing-interceptor
router
default-handler
{:executor pedestal-executor
:interceptors interceptors}))))
(defn replace-last-interceptor [service-map interceptor]
(-> service-map

View file

@ -17,23 +17,23 @@
(defn ^:no-wiki group-keys [data]
(reduce-kv
(fn [[top childs] k v]
(if (http-methods k)
[top (assoc childs k v)]
[(assoc top k v) childs])) [{} {}] data))
(fn [[top childs] k v]
(if (http-methods k)
[top (assoc childs k v)]
[(assoc top k v) childs])) [{} {}] data))
(defn coerce-handler [[path data] {:keys [expand] :as opts}]
[path (reduce
(fn [acc method]
(if (contains? acc method)
(update acc method expand opts)
acc)) data http-methods)])
(fn [acc method]
(if (contains? acc method)
(update acc method expand opts)
acc)) data http-methods)])
(defn compile-result [[path data] {:keys [::default-options-endpoint expand] :as opts}]
(let [[top childs] (group-keys data)
childs (cond-> childs
(and (not (:options childs)) (not (:handler top)) default-options-endpoint)
(assoc :options (expand default-options-endpoint opts)))
(and (not (:options childs)) (not (:handler top)) default-options-endpoint)
(assoc :options (expand default-options-endpoint opts)))
->endpoint (fn [p d m s]
(-> (middleware/compile-result [p d] opts s)
(map->Endpoint)
@ -41,19 +41,19 @@
(assoc :method m)))
->methods (fn [any? data]
(reduce
(fn [acc method]
(cond-> acc
any? (assoc method (->endpoint path data method nil))))
(map->Methods {})
http-methods))]
(fn [acc method]
(cond-> acc
any? (assoc method (->endpoint path data method nil))))
(map->Methods {})
http-methods))]
(if-not (seq childs)
(->methods true top)
(reduce-kv
(fn [acc method data]
(let [data (meta-merge top data)]
(assoc acc method (->endpoint path data method method))))
(->methods (:handler top) data)
childs))))
(fn [acc method data]
(let [data (meta-merge top data)]
(assoc acc method (->endpoint path data method method))))
(->methods (:handler top) data)
childs))))
(def default-options-handler
(let [handle (fn [request]
@ -318,26 +318,26 @@
enrich-default-request (create-enrich-default-request inject-router?)]
(with-meta
(wrap
(fn
([request]
(if-let [match (r/match-by-path router (:uri request))]
(let [method (:request-method request)
path-params (:path-params match)
result (:result match)
handler (-> result method :handler (or default-handler))
request (enrich-request request path-params match router)]
(or (handler request) (default-handler request)))
(default-handler (enrich-default-request request router))))
([request respond raise]
(if-let [match (r/match-by-path router (:uri request))]
(let [method (:request-method request)
path-params (:path-params match)
result (:result match)
handler (-> result method :handler (or default-handler))
request (enrich-request request path-params match router)]
((routes handler default-handler) request respond raise))
(default-handler (enrich-default-request request router) respond raise))
nil)))
(fn
([request]
(if-let [match (r/match-by-path router (:uri request))]
(let [method (:request-method request)
path-params (:path-params match)
result (:result match)
handler (-> result method :handler (or default-handler))
request (enrich-request request path-params match router)]
(or (handler request) (default-handler request)))
(default-handler (enrich-default-request request router))))
([request respond raise]
(if-let [match (r/match-by-path router (:uri request))]
(let [method (:request-method request)
path-params (:path-params match)
result (:result match)
handler (-> result method :handler (or default-handler))
request (enrich-request request path-params match router)]
((routes handler default-handler) request respond raise))
(default-handler (enrich-default-request request router) respond raise))
nil)))
{::r/router router}))))
(defn get-router [handler]

View file

@ -10,8 +10,8 @@
::coercion/response-coercion 500
nil)]
(respond
{:status status
:body (coercion/encode-error data)})
{:status status
:body (coercion/encode-error data)})
(raise e))))
;;

View file

@ -19,7 +19,6 @@
(s/def ::trace map?)
(s/def ::patch map?)
(s/def ::data
(s/keys :opt-un [::rs/handler ::rs/name ::rs/no-doc ::middleware]))
@ -30,9 +29,9 @@
(defn merge-specs [specs]
(when-let [non-specs (seq (remove #(or (s/spec? %) (s/get-spec %)) specs))]
(exception/fail!
::invalid-specs
{:specs specs
:invalid non-specs}))
::invalid-specs
{:specs specs
:invalid non-specs}))
(s/merge-spec-impl (vec specs) (vec specs) nil))
(defn validate-route-data [routes key wrap spec]
@ -51,5 +50,5 @@
[routes {:keys [spec ::rs/wrap] :or {spec ::data, wrap identity}}]
(when-let [problems (validate-route-data routes :middleware wrap spec)]
(exception/fail!
::rs/invalid-route-data
{:problems problems})))
::rs/invalid-route-data
{:problems problems})))

View file

@ -23,16 +23,16 @@
(defn stringify [schema]
(walk/prewalk
(fn [x]
(cond
#?@(:clj [(class? x) (.getName ^Class x)])
(instance? schema.core.OptionalKey x) (pr-str (list 'opt (:k x)))
(instance? schema.core.RequiredKey x) (pr-str (list 'req (:k x)))
(and (satisfies? s/Schema x) (record? x)) (try (pr-str (s/explain x)) (catch #?(:clj Exception :cljs js/Error) _ x))
(instance? schema.utils.ValidationError x) (str (su/validation-error-explain x))
(instance? schema.utils.NamedError x) (str (su/named-error-explain x))
:else x))
schema))
(fn [x]
(cond
#?@(:clj [(class? x) (.getName ^Class x)])
(instance? schema.core.OptionalKey x) (pr-str (list 'opt (:k x)))
(instance? schema.core.RequiredKey x) (pr-str (list 'req (:k x)))
(and (satisfies? s/Schema x) (record? x)) (try (pr-str (s/explain x)) (catch #?(:clj Exception :cljs js/Error) _ x))
(instance? schema.utils.ValidationError x) (str (su/validation-error-explain x))
(instance? schema.utils.NamedError x) (str (su/named-error-explain x))
:else x))
schema))
(def default-options
{:coerce-response? coerce-response?
@ -50,27 +50,27 @@
;; TODO: this looks identical to spec, refactor when schema is done.
(case specification
:swagger (swagger/swagger-spec
(merge
(if parameters
{::swagger/parameters
(into
(empty parameters)
(for [[k v] parameters]
[k (coercion/-compile-model this v nil)]))})
(if responses
{::swagger/responses
(into
(empty responses)
(for [[k response] responses]
[k (as-> response $
(set/rename-keys $ {:body :schema})
(if (:schema $)
(update $ :schema #(coercion/-compile-model this % nil))
$))]))})))
(merge
(if parameters
{::swagger/parameters
(into
(empty parameters)
(for [[k v] parameters]
[k (coercion/-compile-model this v nil)]))})
(if responses
{::swagger/responses
(into
(empty responses)
(for [[k response] responses]
[k (as-> response $
(set/rename-keys $ {:body :schema})
(if (:schema $)
(update $ :schema #(coercion/-compile-model this % nil))
$))]))})))
(throw
(ex-info
(str "Can't produce Schema apidocs for " specification)
{:type specification, :coercion :schema}))))
(ex-info
(str "Can't produce Schema apidocs for " specification)
{:type specification, :coercion :schema}))))
(-compile-model [_ model _] model)
(-open-model [_ schema] (st/open-schema schema))
(-encode-error [_ error]
@ -88,8 +88,8 @@
coerced (coercer value)]
(if-let [error (su/error-val coerced)]
(coercion/map->CoercionError
{:schema schema
:errors error})
{:schema schema
:errors error})
coerced))
value))))
(-response-coercer [this schema]

View file

@ -8,10 +8,10 @@
interceptor/Executor
(queue [_ interceptors]
(queue/into-queue
(map
(fn [{::interceptor/keys [handler] :as interceptor}]
(or handler interceptor))
interceptors)))
(map
(fn [{::interceptor/keys [handler] :as interceptor}]
(or handler interceptor))
interceptors)))
(execute [_ interceptors request]
(sieppari/execute interceptors request))
(execute [_ interceptors request respond raise]

View file

@ -11,13 +11,13 @@
(def string-transformer
(st/type-transformer
st/strip-extra-keys-transformer
st/string-transformer))
st/strip-extra-keys-transformer
st/string-transformer))
(def json-transformer
(st/type-transformer
st/strip-extra-keys-transformer
st/json-transformer))
st/strip-extra-keys-transformer
st/json-transformer))
(def strip-extra-keys-transformer
st/strip-extra-keys-transformer)
@ -88,27 +88,27 @@
(-get-apidocs [this specification {:keys [parameters responses]}]
(case specification
:swagger (swagger/swagger-spec
(merge
(if parameters
{::swagger/parameters
(into
(empty parameters)
(for [[k v] parameters]
[k (coercion/-compile-model this v nil)]))})
(if responses
{::swagger/responses
(into
(empty responses)
(for [[k response] responses]
[k (as-> response $
(set/rename-keys $ {:body :schema})
(if (:schema $)
(update $ :schema #(coercion/-compile-model this % nil))
$))]))})))
(merge
(if parameters
{::swagger/parameters
(into
(empty parameters)
(for [[k v] parameters]
[k (coercion/-compile-model this v nil)]))})
(if responses
{::swagger/responses
(into
(empty responses)
(for [[k response] responses]
[k (as-> response $
(set/rename-keys $ {:body :schema})
(if (:schema $)
(update $ :schema #(coercion/-compile-model this % nil))
$))]))})))
(throw
(ex-info
(str "Can't produce Spec apidocs for " specification)
{:specification specification, :coercion :spec}))))
(ex-info
(str "Can't produce Spec apidocs for " specification)
{:specification specification, :coercion :spec}))))
(-compile-model [_ model name]
(into-spec model name))
(-open-model [_ spec] spec)
@ -129,8 +129,8 @@
(if (s/invalid? transformed)
(let [problems (st/explain-data spec coerced transformer)]
(coercion/map->CoercionError
{:spec spec
:problems problems}))
{:spec spec
:problems problems}))
(s/unform spec transformed)))))
value))))
(-response-coercer [this spec]

View file

@ -31,10 +31,10 @@
([options]
(let [config-json (fn [{:keys [url config]}] (j/write-value-as-string (merge config {:url url})))
options (as-> options $
(update $ :root (fnil identity "swagger-ui"))
(update $ :url (fnil identity "/swagger.json"))
(assoc $ :paths {"/config.json" {:headers {"Content-Type" "application/json"}
:status 200
:body (config-json $)}}))]
(update $ :root (fnil identity "swagger-ui"))
(update $ :url (fnil identity "/swagger.json"))
(assoc $ :paths {"/config.json" {:headers {"Content-Type" "application/json"}
:status 200
:body (config-json $)}}))]
(ring/routes
(ring/create-resource-handler options))))))
(ring/create-resource-handler options))))))

View file

@ -88,13 +88,13 @@
(if (and data (not no-doc))
[method
(meta-merge
base-swagger-spec
(apply meta-merge (keep (comp :swagger :data) middleware))
(apply meta-merge (keep (comp :swagger :data) interceptors))
(if coercion
(coercion/get-apidocs coercion :swagger data))
(select-keys data [:tags :summary :description])
(strip-top-level-keys swagger))]))
base-swagger-spec
(apply meta-merge (keep (comp :swagger :data) middleware))
(apply meta-merge (keep (comp :swagger :data) interceptors))
(if coercion
(coercion/get-apidocs coercion :swagger data))
(select-keys data [:tags :summary :description])
(strip-top-level-keys swagger))]))
transform-path (fn [[p _ c]]
(if-let [endpoint (some->> c (keep transform-endpoint) (seq) (into {}))]
[(swagger-path p (r/options router)) endpoint]))

View file

@ -11,11 +11,10 @@
wrap (if (pos? (count indent)) vector identity)]
(wrap [name {:file (str "doc/" file)}])))
(reduce
(fn [acc data]
(if (vector? (first data))
(update-in acc [(dec (count acc)) 2] (fnil into []) data)
(conj acc data))
) [])
(fn [acc data]
(if (vector? (first data))
(update-in acc [(dec (count acc)) 2] (fnil into []) data)
(conj acc data))) [])
;; third sweep to flatten chids...
(mapv (fn [[n o c]] (if c (into [n o] c) [n o]))))
data {:cljdoc/include-namespaces-from-dependencies ['metosin/reitit

View file

@ -17,23 +17,23 @@
(create f nil))
([f wrap]
(http/ring-handler
(http/router
[["/defaults"
{:handler f}]
["/coercion"
{:interceptors [(reitit.http.coercion/coerce-request-interceptor)
(reitit.http.coercion/coerce-response-interceptor)]
:coercion reitit.coercion.spec/coercion
:parameters {:query {:x int?, :y int?}}
:responses {200 {:body {:total pos-int?}}}
:handler f}]]
{:data {:interceptors [(exception/exception-interceptor
(merge
exception/default-handlers
{::kikka (constantly {:status 400, :body "kikka"})
SQLException (constantly {:status 400, :body "sql"})
::exception/wrap wrap}))]}})
{:executor sieppari/executor})))]
(http/router
[["/defaults"
{:handler f}]
["/coercion"
{:interceptors [(reitit.http.coercion/coerce-request-interceptor)
(reitit.http.coercion/coerce-response-interceptor)]
:coercion reitit.coercion.spec/coercion
:parameters {:query {:x int?, :y int?}}
:responses {200 {:body {:total pos-int?}}}
:handler f}]]
{:data {:interceptors [(exception/exception-interceptor
(merge
exception/default-handlers
{::kikka (constantly {:status 400, :body "kikka"})
SQLException (constantly {:status 400, :body "sql"})
::exception/wrap wrap}))]}})
{:executor sieppari/executor})))]
(testing "normal calls work ok"
(let [response {:status 200, :body "ok"}

View file

@ -9,11 +9,11 @@
(deftest muuntaja-test
(let [data {:kikka "kukka"}
app (http/ring-handler
(http/router
["/ping" {:get (constantly {:status 200, :body data})}]
{:data {:muuntaja m/instance
:interceptors [(muuntaja/format-interceptor)]}})
{:executor sieppari/executor})]
(http/router
["/ping" {:get (constantly {:status 200, :body data})}]
{:data {:muuntaja m/instance
:interceptors [(muuntaja/format-interceptor)]}})
{:executor sieppari/executor})]
(is (= data (->> {:request-method :get, :uri "/ping"}
(app)
:body
@ -24,27 +24,27 @@
no-edn-decode (m/create (-> m/default-options (update-in [:formats "application/edn"] dissoc :decoder)))
just-edn (m/create (-> m/default-options (m/select-formats ["application/edn"])))
app (http/ring-handler
(http/router
[["/defaults"
{:get identity}]
["/explicit-defaults"
{:muuntaja with-defaults
:get identity}]
["/no-edn-decode"
{:muuntaja no-edn-decode
:get identity}]
["/just-edn"
{:muuntaja just-edn
:get identity}]
["/form-params"
{:post {:parameters {:form {:x string?}}
:handler identity}}]
["/swagger.json"
{:get {:no-doc true
:handler (swagger/create-swagger-handler)}}]]
{:data {:muuntaja m/instance
:interceptors [(muuntaja/format-interceptor)]}})
{:executor sieppari/executor})
(http/router
[["/defaults"
{:get identity}]
["/explicit-defaults"
{:muuntaja with-defaults
:get identity}]
["/no-edn-decode"
{:muuntaja no-edn-decode
:get identity}]
["/just-edn"
{:muuntaja just-edn
:get identity}]
["/form-params"
{:post {:parameters {:form {:x string?}}
:handler identity}}]
["/swagger.json"
{:get {:no-doc true
:handler (swagger/create-swagger-handler)}}]]
{:data {:muuntaja m/instance
:interceptors [(muuntaja/format-interceptor)]}})
{:executor sieppari/executor})
spec (fn [method path]
(let [path (keyword path)]
(-> {:request-method :get :uri "/swagger.json"}
@ -99,42 +99,42 @@
(deftest muuntaja-swagger-parts-test
(let [app (http/ring-handler
(http/router
[["/request"
{:interceptors [(muuntaja/format-negotiate-interceptor)
(muuntaja/format-request-interceptor)]
:get identity}]
["/response"
{:interceptors [(muuntaja/format-negotiate-interceptor)
(muuntaja/format-response-interceptor)]
:get identity}]
["/both"
{:interceptors [(muuntaja/format-negotiate-interceptor)
(muuntaja/format-response-interceptor)
(muuntaja/format-request-interceptor)]
:get identity}]
["/form-request"
{:interceptors [(muuntaja/format-negotiate-interceptor)
(http/router
[["/request"
{:interceptors [(muuntaja/format-negotiate-interceptor)
(muuntaja/format-request-interceptor)]
:post {:parameters {:form {:x string?}}
:handler identity}}]
["/form-response"
{:interceptors [(muuntaja/format-negotiate-interceptor)
:get identity}]
["/response"
{:interceptors [(muuntaja/format-negotiate-interceptor)
(muuntaja/format-response-interceptor)]
:post {:parameters {:form {:x string?}}
:handler identity}}]
["/form-with-both"
{:interceptors [(muuntaja/format-negotiate-interceptor)
:get identity}]
["/both"
{:interceptors [(muuntaja/format-negotiate-interceptor)
(muuntaja/format-response-interceptor)
(muuntaja/format-request-interceptor)]
:post {:parameters {:form {:x string?}}
:handler identity}}]
:get identity}]
["/form-request"
{:interceptors [(muuntaja/format-negotiate-interceptor)
(muuntaja/format-request-interceptor)]
:post {:parameters {:form {:x string?}}
:handler identity}}]
["/form-response"
{:interceptors [(muuntaja/format-negotiate-interceptor)
(muuntaja/format-response-interceptor)]
:post {:parameters {:form {:x string?}}
:handler identity}}]
["/form-with-both"
{:interceptors [(muuntaja/format-negotiate-interceptor)
(muuntaja/format-response-interceptor)
(muuntaja/format-request-interceptor)]
:post {:parameters {:form {:x string?}}
:handler identity}}]
["/swagger.json"
{:get {:no-doc true
:handler (swagger/create-swagger-handler)}}]]
{:data {:muuntaja m/instance}})
{:executor sieppari/executor})
["/swagger.json"
{:get {:no-doc true
:handler (swagger/create-swagger-handler)}}]]
{:data {:muuntaja m/instance}})
{:executor sieppari/executor})
spec (fn [method path]
(-> {:request-method :get :uri "/swagger.json"}
(app) :body :paths (get path) method))

View file

@ -7,10 +7,10 @@
(deftest parameters-test
(let [app (http/ring-handler
(http/router
["/ping" {:get #(select-keys % [:params :query-params])}]
{:data {:interceptors [(parameters/parameters-interceptor)]}})
{:executor sieppari/executor})]
(http/router
["/ping" {:get #(select-keys % [:params :query-params])}]
{:data {:interceptors [(parameters/parameters-interceptor)]}})
{:executor sieppari/executor})]
(is (= {:query-params {"kikka" "kukka"}
:params {"kikka" "kukka"}}
(app {:request-method :get
@ -19,15 +19,15 @@
(deftest parameters-swagger-test
(let [app (http/ring-handler
(http/router
[["/form-params" {:post {:parameters {:form {:x string?}}
:handler identity}}]
["/body-params" {:post {:parameters {:body {:x string?}}
:handler identity}}]
["/swagger.json" {:get {:no-doc true
:handler (swagger/create-swagger-handler)}}]]
{:data {:interceptors [(parameters/parameters-interceptor)]}})
 {:executor sieppari/executor})
(http/router
[["/form-params" {:post {:parameters {:form {:x string?}}
:handler identity}}]
["/body-params" {:post {:parameters {:body {:x string?}}
:handler identity}}]
["/swagger.json" {:get {:no-doc true
:handler (swagger/create-swagger-handler)}}]]
{:data {:interceptors [(parameters/parameters-interceptor)]}})
{:executor sieppari/executor})
spec (fn [path]
(-> {:request-method :get :uri "/swagger.json"}
app

View file

@ -74,20 +74,20 @@
(deftest spec-coercion-test
(let [create (fn [interceptors]
(http/ring-handler
(http/router
["/api"
["/plus/:e"
{:get {:parameters {:query {:a int?}
:body {:b int?}
:form {:c int?}
:header {:d int?}
:path {:e int?}}
:responses {200 {:body {:total pos-int?}}
500 {:description "fail"}}
:handler handler}}]]
{:data {:interceptors interceptors
:coercion spec/coercion}})
{:executor sieppari/executor}))]
(http/router
["/api"
["/plus/:e"
{:get {:parameters {:query {:a int?}
:body {:b int?}
:form {:c int?}
:header {:d int?}
:path {:e int?}}
:responses {200 {:body {:total pos-int?}}
500 {:description "fail"}}
:handler handler}}]]
{:data {:interceptors interceptors
:coercion spec/coercion}})
{:executor sieppari/executor}))]
(testing "without exception handling"
(let [app (create [(rrc/coerce-request-interceptor)
@ -103,15 +103,15 @@
(testing "invalid request"
(is (thrown-with-msg?
ExceptionInfo
#"Request coercion failed"
(app invalid-request1))))
ExceptionInfo
#"Request coercion failed"
(app invalid-request1))))
(testing "invalid response"
(is (thrown-with-msg?
ExceptionInfo
#"Response coercion failed"
(app invalid-request2))))))
ExceptionInfo
#"Response coercion failed"
(app invalid-request2))))))
(testing "with exception handling"
(let [app (create [(rrc/coerce-exceptions-interceptor)
@ -134,20 +134,20 @@
(deftest schema-coercion-test
(let [create (fn [middleware]
(http/ring-handler
(http/router
["/api"
["/plus/:e"
{:get {:parameters {:query {:a s/Int}
:body {:b s/Int}
:form {:c s/Int}
:header {:d s/Int}
:path {:e s/Int}}
:responses {200 {:body {:total (s/constrained s/Int pos? 'positive)}}
500 {:description "fail"}}
:handler handler}}]]
{:data {:interceptors middleware
:coercion schema/coercion}})
{:executor sieppari/executor}))]
(http/router
["/api"
["/plus/:e"
{:get {:parameters {:query {:a s/Int}
:body {:b s/Int}
:form {:c s/Int}
:header {:d s/Int}
:path {:e s/Int}}
:responses {200 {:body {:total (s/constrained s/Int pos? 'positive)}}
500 {:description "fail"}}
:handler handler}}]]
{:data {:interceptors middleware
:coercion schema/coercion}})
{:executor sieppari/executor}))]
(testing "without exception handling"
(let [app (create [(rrc/coerce-request-interceptor)
@ -163,15 +163,15 @@
(testing "invalid request"
(is (thrown-with-msg?
ExceptionInfo
#"Request coercion failed"
(app invalid-request1))))
ExceptionInfo
#"Request coercion failed"
(app invalid-request1))))
(testing "invalid response"
(is (thrown-with-msg?
ExceptionInfo
#"Response coercion failed"
(app invalid-request2))))
ExceptionInfo
#"Response coercion failed"
(app invalid-request2))))
(testing "with exception handling"
(let [app (create [(rrc/coerce-exceptions-interceptor)
@ -194,51 +194,51 @@
(deftest malli-coercion-test
(let [create (fn [interceptors]
(http/ring-handler
(http/router
["/api"
(http/router
["/api"
["/validate" {:summary "just validation"
:coercion (reitit.coercion.malli/create {:transformers {}})
:post {:parameters {:body [:map [:x int?]]}
:responses {200 {:body [:map [:x int?]]}}
:handler (fn [req]
{:status 200
:body (-> req :parameters :body)})}}]
["/validate" {:summary "just validation"
:coercion (reitit.coercion.malli/create {:transformers {}})
:post {:parameters {:body [:map [:x int?]]}
:responses {200 {:body [:map [:x int?]]}}
:handler (fn [req]
{:status 200
:body (-> req :parameters :body)})}}]
["/no-op" {:summary "no-operation"
:coercion (reitit.coercion.malli/create {:transformers {}, :validate false})
:post {:parameters {:body [:map [:x int?]]}
:responses {200 {:body [:map [:x int?]]}}
:handler (fn [req]
{:status 200
:body (-> req :parameters :body)})}}]
["/no-op" {:summary "no-operation"
:coercion (reitit.coercion.malli/create {:transformers {}, :validate false})
:post {:parameters {:body [:map [:x int?]]}
:responses {200 {:body [:map [:x int?]]}}
:handler (fn [req]
{:status 200
:body (-> req :parameters :body)})}}]
["/skip" {:summary "skip"
:coercion (reitit.coercion.malli/create {:enabled false})
:post {:parameters {:body [:map [:x int?]]}
:responses {200 {:body [:map [:x int?]]}}
:handler (fn [req]
{:status 200
:body (-> req :parameters :body)})}}]
["/or" {:post {:summary "accepts either of two map schemas"
:parameters {:body [:or [:map [:x int?]] [:map [:y int?]]]}
:responses {200 {:body [:map [:msg string?]]}}
:handler (fn [{{{:keys [x]} :body} :parameters}]
["/skip" {:summary "skip"
:coercion (reitit.coercion.malli/create {:enabled false})
:post {:parameters {:body [:map [:x int?]]}
:responses {200 {:body [:map [:x int?]]}}
:handler (fn [req]
{:status 200
:body {:msg (if x "you sent x" "you sent y")}})}}]
:body (-> req :parameters :body)})}}]
["/plus/:e" {:get {:parameters {:query [:map [:a {:optional true} int?]]
:body [:map [:b int?]]
:form [:map [:c [int? {:default 3}]]]
:header [:map [:d int?]]
:path [:map [:e int?]]}
:responses {200 {:body [:map [:total pos-int?]]}
500 {:description "fail"}}
:handler handler}}]]
{:data {:interceptors interceptors
:coercion malli/coercion}})
{:executor sieppari/executor}))]
["/or" {:post {:summary "accepts either of two map schemas"
:parameters {:body [:or [:map [:x int?]] [:map [:y int?]]]}
:responses {200 {:body [:map [:msg string?]]}}
:handler (fn [{{{:keys [x]} :body} :parameters}]
{:status 200
:body {:msg (if x "you sent x" "you sent y")}})}}]
["/plus/:e" {:get {:parameters {:query [:map [:a {:optional true} int?]]
:body [:map [:b int?]]
:form [:map [:c [int? {:default 3}]]]
:header [:map [:d int?]]
:path [:map [:e int?]]}
:responses {200 {:body [:map [:total pos-int?]]}
500 {:description "fail"}}
:handler handler}}]]
{:data {:interceptors interceptors
:coercion malli/coercion}})
{:executor sieppari/executor}))]
(testing "withut exception handling"
(let [app (create [(rrc/coerce-request-interceptor)
@ -249,8 +249,8 @@
:body {:total 15}}
(app valid-request1)))
#_(is (= {:status 200
:body {:total 115}}
(app valid-request2)))
:body {:total 115}}
(app valid-request2)))
(is (= {:status 200
:body {:total 15}}
(app valid-request3)))
@ -264,15 +264,15 @@
(testing "invalid request"
(is (thrown-with-msg?
ExceptionInfo
#"Request coercion failed"
(app invalid-request1))))
ExceptionInfo
#"Request coercion failed"
(app invalid-request1))))
(testing "invalid response"
(is (thrown-with-msg?
ExceptionInfo
#"Response coercion failed"
(app invalid-request2))))))
ExceptionInfo
#"Response coercion failed"
(app invalid-request2))))))
(testing "with exception handling"
(let [app (create [(rrc/coerce-exceptions-interceptor)
@ -339,16 +339,16 @@
{:status 200, :body (assoc body :response true)})}})
->app (fn [options]
(http/ring-handler
(http/router
["/api"
["/default" (endpoint [:map [:x int?]])]
["/closed" (endpoint [:map {:closed true} [:x int?]])]
["/open" (endpoint [:map {:closed false} [:x int?]])]]
{:data {:interceptors [(rrc/coerce-exceptions-interceptor)
(rrc/coerce-request-interceptor)
(rrc/coerce-response-interceptor)]
:coercion (malli/create options)}})
{:executor sieppari/executor}))
(http/router
["/api"
["/default" (endpoint [:map [:x int?]])]
["/closed" (endpoint [:map {:closed true} [:x int?]])]
["/open" (endpoint [:map {:closed false} [:x int?]])]]
{:data {:interceptors [(rrc/coerce-exceptions-interceptor)
(rrc/coerce-request-interceptor)
(rrc/coerce-response-interceptor)]
:coercion (malli/create options)}})
{:executor sieppari/executor}))
->request (fn [uri] {:uri (str "/api/" uri)
:request-method :get
:muuntaja/request {:format "application/json"}
@ -399,23 +399,23 @@
(testing "sequence schemas"
(let [app (http/ring-handler
(http/router
["/ping" {:get {:parameters {:body [:vector [:map [:message string?]]]}
:responses {200 {:body [:vector [:map [:pong string?]]]}
501 {:body [:vector [:map [:error string?]]]}}
:handler (fn [{{[{:keys [message]}] :body} :parameters :as req}]
(condp = message
"ping" {:status 200
:body [{:pong message}]}
"fail" {:status 501
:body [{:error "fail"}]}
{:status 200
:body {:invalid "response"}}))}}]
{:data {:interceptors [(rrc/coerce-exceptions-interceptor)
(rrc/coerce-request-interceptor)
(rrc/coerce-response-interceptor)]
:coercion malli/coercion}})
{:executor sieppari/executor})
(http/router
["/ping" {:get {:parameters {:body [:vector [:map [:message string?]]]}
:responses {200 {:body [:vector [:map [:pong string?]]]}
501 {:body [:vector [:map [:error string?]]]}}
:handler (fn [{{[{:keys [message]}] :body} :parameters :as req}]
(condp = message
"ping" {:status 200
:body [{:pong message}]}
"fail" {:status 501
:body [{:error "fail"}]}
{:status 200
:body {:invalid "response"}}))}}]
{:data {:interceptors [(rrc/coerce-exceptions-interceptor)
(rrc/coerce-request-interceptor)
(rrc/coerce-response-interceptor)]
:coercion malli/coercion}})
{:executor sieppari/executor})
->request (fn [body]
{:uri "/ping"
:request-method :get
@ -439,19 +439,19 @@
(deftest muuntaja-test
(let [app (http/ring-handler
(http/router
["/api"
["/plus"
{:post {:parameters {:body {:int int?, :keyword keyword?}}
:responses {200 {:body {:int int?, :keyword keyword?}}}
:handler (fn [{{:keys [body]} :parameters}]
{:status 200
:body body})}}]]
{:data {:interceptors [(muuntaja.interceptor/format-interceptor)
(rrc/coerce-response-interceptor)
(rrc/coerce-request-interceptor)]
:coercion spec/coercion}})
{:executor sieppari/executor})
(http/router
["/api"
["/plus"
{:post {:parameters {:body {:int int?, :keyword keyword?}}
:responses {200 {:body {:int int?, :keyword keyword?}}}
:handler (fn [{{:keys [body]} :parameters}]
{:status 200
:body body})}}]]
{:data {:interceptors [(muuntaja.interceptor/format-interceptor)
(rrc/coerce-response-interceptor)
(rrc/coerce-request-interceptor)]
:coercion spec/coercion}})
{:executor sieppari/executor})
request (fn [content-type body]
(-> {:request-method :post
:headers {"content-type" content-type, "accept" content-type}

View file

@ -21,14 +21,14 @@
(testing "http-handler"
(let [api-interceptor (interceptor :api)
router (http/router
["/api" {:interceptors [api-interceptor]}
["/all" handler]
["/get" {:get handler}]
["/users" {:interceptors [[interceptor :users]]
:get handler
:post {:handler handler
:interceptors [[interceptor :post]]}
:handler handler}]])
["/api" {:interceptors [api-interceptor]}
["/all" handler]
["/get" {:get handler}]
["/users" {:interceptors [[interceptor :users]]
:get handler
:post {:handler handler
:interceptors [[interceptor :post]]}
:handler handler}]])
app (http/ring-handler router nil {:executor sieppari/executor})]
(testing "router can be extracted"
@ -68,14 +68,14 @@
(testing "named routes"
(let [router (http/router
[["/api"
["/all" {:handler handler :name ::all}]
["/get" {:get {:handler handler :name ::HIDDEN}
:name ::get}]
["/users" {:get handler
:post handler
:handler handler
:name ::users}]]])
[["/api"
["/all" {:handler handler :name ::all}]
["/get" {:get {:handler handler :name ::HIDDEN}
:name ::get}]
["/users" {:get handler
:post handler
:handler handler
:name ::users}]]])
app (http/ring-handler router nil {:executor sieppari/executor})]
(testing "router can be extracted"
@ -102,13 +102,13 @@
(deftest enforcing-data-rules-at-runtime-test
(let [handler (constantly {:status 200, :body "ok"})
app (http/ring-handler
(http/router
[["/api"
["/ping" handler]
["/admin" {::roles #{:admin}}
["/ping" handler]]]]
{:data {:interceptors [enforce-roles-interceptor]}})
nil {:executor sieppari/executor})]
(http/router
[["/api"
["/ping" handler]
["/admin" {::roles #{:admin}}
["/ping" handler]]]]
{:data {:interceptors [enforce-roles-interceptor]}})
nil {:executor sieppari/executor})]
(testing "public handler"
(is (= {:status 200, :body "ok"}
@ -128,8 +128,8 @@
(deftest default-handler-test
(let [response {:status 200, :body "ok"}
router (http/router
[["/ping" {:get (constantly response)}]
["/pong" (constantly nil)]])
[["/ping" {:get (constantly response)}]
["/pong" (constantly nil)]])
app (http/ring-handler router nil {:executor sieppari/executor})]
(testing "match"
@ -146,9 +146,9 @@
(testing "with default http responses"
(let [app (http/ring-handler
router
(ring/create-default-handler)
{:executor sieppari/executor})]
router
(ring/create-default-handler)
{:executor sieppari/executor})]
(testing "route doesn't match yields 404"
(is (= 404 (:status (app {:request-method :get, :uri "/"})))))
(testing "method doesn't match yields 405"
@ -158,12 +158,12 @@
(testing "with custom http responses"
(let [app (http/ring-handler
router
(ring/create-default-handler
{:not-found (constantly {:status -404})
:method-not-allowed (constantly {:status -405})
:not-acceptable (constantly {:status -406})})
{:executor sieppari/executor})]
router
(ring/create-default-handler
{:not-found (constantly {:status -404})
:method-not-allowed (constantly {:status -405})
:not-acceptable (constantly {:status -406})})
{:executor sieppari/executor})]
(testing "route doesn't match"
(is (= -404 (:status (app {:request-method :get, :uri "/"})))))
(testing "method doesn't match"
@ -180,12 +180,12 @@
(testing "with defaults"
(let [app (http/ring-handler
(http/router
[["/get" {:get (constantly response)
:post (constantly response)}]
["/options" {:options (constantly response)}]
["/any" (constantly response)]])
{:executor sieppari/executor})]
(http/router
[["/get" {:get (constantly response)
:post (constantly response)}]
["/options" {:options (constantly response)}]
["/any" (constantly response)]])
{:executor sieppari/executor})]
(testing "endpoint with a non-options handler"
(is (= response (app {:request-method :get, :uri "/get"})))
@ -201,12 +201,12 @@
(testing "disabled via options"
(let [app (http/ring-handler
(http/router
[["/get" {:get (constantly response)}]
["/options" {:options (constantly response)}]
["/any" (constantly response)]]
{::http/default-options-endpoint nil})
{:executor sieppari/executor})]
(http/router
[["/get" {:get (constantly response)}]
["/options" {:options (constantly response)}]
["/any" (constantly response)]]
{::http/default-options-endpoint nil})
{:executor sieppari/executor})]
(testing "endpoint with a non-options handler"
(is (= response (app {:request-method :get, :uri "/get"})))
@ -227,8 +227,8 @@
(reset! value x))))
response {:status 200, :body "ok"}
router (http/router
[["/ping" {:get (fn [_] response)}]
["/pong" (fn [_] nil)]])
[["/ping" {:get (fn [_] response)}]
["/pong" (fn [_] nil)]])
app (http/ring-handler router nil {:executor sieppari/executor})]
(testing "match"
@ -287,11 +287,11 @@
(require '[sieppari.async.core-async])
(let [response {:status 200, :body "ok"}
app (http/ring-handler
(http/router
["/ping" {:get {:interceptors [{:enter #(a/go %)}]
:handler (fn [_] (a/go response))}}])
(ring/create-default-handler)
{:executor sieppari/executor})]
(http/router
["/ping" {:get {:interceptors [{:enter #(a/go %)}]
:handler (fn [_] (a/go response))}}])
(ring/create-default-handler)
{:executor sieppari/executor})]
(let [respond (promise)]
(app {:request-method :get, :uri "/ping"} respond ::irrelevant)
(is (= response (deref respond 100 ::timeout)))))))
@ -302,11 +302,11 @@
(testing "works if registered"
(let [response {:status 200, :body "ok"}
app (http/ring-handler
(http/router
["/ping" {:get {:interceptors [{:enter map->MyAsyncContext}]
:handler (fn [_] response)}}])
(ring/create-default-handler)
{:executor sieppari/executor})
(http/router
["/ping" {:get {:interceptors [{:enter map->MyAsyncContext}]
:handler (fn [_] response)}}])
(ring/create-default-handler)
{:executor sieppari/executor})
respond (promise)
raise (promise)]
(app {:request-method :get, :uri "/ping"} respond raise)
@ -321,14 +321,14 @@
request {:uri "/api/avaruus" :request-method :get}
create (fn [options]
(http/ring-handler
(http/router
["/api" {:interceptors [(interceptor :olipa)]}
["/avaruus" {:interceptors [(interceptor :kerran)]
:get {:handler handler
:interceptors [(interceptor :avaruus)]}}]]
options)
nil
{:executor sieppari/executor}))]
(http/router
["/api" {:interceptors [(interceptor :olipa)]}
["/avaruus" {:interceptors [(interceptor :kerran)]
:get {:handler handler
:interceptors [(interceptor :avaruus)]}}]]
options)
nil
{:executor sieppari/executor}))]
(testing "by default, all middleware are applied in order"
(let [app (create nil)]
@ -352,10 +352,10 @@
(testing "from root"
(let [app (http/ring-handler
(http/router
["/*" (ring/create-resource-handler)])
(ring/create-default-handler)
{:executor sieppari/executor})]
(http/router
["/*" (ring/create-resource-handler)])
(ring/create-default-handler)
{:executor sieppari/executor})]
(testing test
(testing "different file-types"
(let [response (app (request "/hello.json"))]
@ -388,10 +388,10 @@
(testing "from path"
(let [app (http/ring-handler
(http/router
["/files/*" (ring/create-resource-handler)])
(ring/create-default-handler)
{:executor sieppari/executor})
(http/router
["/files/*" (ring/create-resource-handler)])
(ring/create-default-handler)
{:executor sieppari/executor})
request #(request (str "/files" %))
redirect #(redirect (str "/files" %))]
(testing test
@ -428,11 +428,11 @@
(testing "from root"
(let [app (http/ring-handler
(http/router [])
(ring/routes
(ring/create-resource-handler {:path "/"})
(ring/create-default-handler))
{:executor sieppari/executor})]
(http/router [])
(ring/routes
(ring/create-resource-handler {:path "/"})
(ring/create-default-handler))
{:executor sieppari/executor})]
(testing test
(testing "different file-types"
(let [response (app (request "/hello.json"))]
@ -465,11 +465,11 @@
(testing "from path"
(let [app (http/ring-handler
(http/router [])
(ring/routes
(ring/create-resource-handler {:path "/files"})
(ring/create-default-handler))
{:executor sieppari/executor})
(http/router [])
(ring/routes
(ring/create-resource-handler {:path "/files"})
(ring/create-default-handler))
{:executor sieppari/executor})
request #(request (str "/files" %))
redirect #(redirect (str "/files" %))]
(testing test
@ -510,18 +510,18 @@
interceptor (fn [x] {:enter (fn [ctx] (swap! times update-in [:enter x] (fnil inc 0)) ctx)
:leave (fn [ctx] (swap! times update-in [:leave x] (fnil inc 0)) ctx)})
app (http/ring-handler
(http/router
["/api"
{:interceptors [(interceptor :api)]}
["/ping"
{:interceptors [(interceptor :ping)]
:get {:interceptors [(interceptor :get)]
:handler (fn [_] response)}}]])
(ring/routes
(ring/create-default-handler)
{:data {:interceptors [(interceptor :router)]}})
{:executor sieppari/executor
:interceptors [(interceptor :top)]})]
(http/router
["/api"
{:interceptors [(interceptor :api)]}
["/ping"
{:interceptors [(interceptor :ping)]
:get {:interceptors [(interceptor :get)]
:handler (fn [_] response)}}]])
(ring/routes
(ring/create-default-handler)
{:data {:interceptors [(interceptor :router)]}})
{:executor sieppari/executor
:interceptors [(interceptor :top)]})]
(is (= response (app {:request-method :get, :uri "/api/ping"})))
(is (= {:enter {:top 1, :api 1, :ping 1, :get 1}
:leave {:get 1, :ping 1, :api 1, :top 1}}
@ -530,15 +530,15 @@
(deftest router-available-in-default-branch
(testing "1-arity"
((http/ring-handler
(http/router [])
(fn [{::r/keys [router]}]
(is router))
{:executor sieppari/executor})
(http/router [])
(fn [{::r/keys [router]}]
(is router))
{:executor sieppari/executor})
{}))
(testing "3-arity"
((http/ring-handler
(http/router [])
(fn [{::r/keys [router]}]
(is router))
{:executor sieppari/executor})
(http/router [])
(fn [{::r/keys [router]}]
(is router))
{:executor sieppari/executor})
{} ::respond ::raise)))

View file

@ -28,11 +28,11 @@
(deftest pedestal-e2e-test
(let [router (pedestal/routing-interceptor
(http/router
[""
{:interceptors [{:name :nop} (exception/exception-interceptor)]}
["/ok" (fn [_] {:status 200, :body "ok"})]
["/fail" (fn [_] (throw (ex-info "kosh" {})))]]))
(http/router
[""
{:interceptors [{:name :nop} (exception/exception-interceptor)]}
["/ok" (fn [_] {:status 200, :body "ok"})]
["/fail" (fn [_] (throw (ex-info "kosh" {})))]]))
service (-> {:io.pedestal.http/request-logger nil
:io.pedestal.http/routes []}
(io.pedestal.http/default-interceptors)

View file

@ -18,25 +18,25 @@
(create f nil))
([f wrap]
(ring/ring-handler
(ring/router
[["/defaults"
{:handler f}]
["/http-response"
{:handler (fn [req]
(http-response/unauthorized! "Unauthorized"))}]
["/coercion"
{:middleware [reitit.ring.coercion/coerce-request-middleware
reitit.ring.coercion/coerce-response-middleware]
:coercion reitit.coercion.spec/coercion
:parameters {:query {:x int?, :y int?}}
:responses {200 {:body {:total pos-int?}}}
:handler f}]]
{:data {:middleware [(exception/create-exception-middleware
(merge
exception/default-handlers
{::kikka (constantly {:status 400, :body "kikka"})
SQLException (constantly {:status 400, :body "sql"})
::exception/wrap wrap}))]}}))))]
(ring/router
[["/defaults"
{:handler f}]
["/http-response"
{:handler (fn [req]
(http-response/unauthorized! "Unauthorized"))}]
["/coercion"
{:middleware [reitit.ring.coercion/coerce-request-middleware
reitit.ring.coercion/coerce-response-middleware]
:coercion reitit.coercion.spec/coercion
:parameters {:query {:x int?, :y int?}}
:responses {200 {:body {:total pos-int?}}}
:handler f}]]
{:data {:middleware [(exception/create-exception-middleware
(merge
exception/default-handlers
{::kikka (constantly {:status 400, :body "kikka"})
SQLException (constantly {:status 400, :body "sql"})
::exception/wrap wrap}))]}}))))]
(testing "normal calls work ok"
(let [response {:status 200, :body "ok"}
@ -66,7 +66,6 @@
:response response}))))]
(is (= response (app {:request-method :post, :uri "/http-response"})))))
(testing ":muuntaja/decode"
(let [app (create (fn [_] (m/decode m/instance "application/json" "{:so \"invalid\"}")))]
(is (= {:body "Malformed \"application/json\" request."
@ -130,21 +129,21 @@
(deftest spec-coercion-exception-test
(let [app (ring/ring-handler
(ring/router
["/plus"
{:get
{:parameters {:query {:x int?, :y int?}}
:responses {200 {:body {:total pos-int?}}}
:handler (fn [{{{:keys [x y]} :query} :parameters}]
{:status 200, :body {:total (+ x y)}})}}]
{:data {:coercion reitit.coercion.spec/coercion
:middleware [(exception/create-exception-middleware
(merge
exception/default-handlers
{::coercion/request-coercion (fn [e _] {:status 400, :body (ex-data e)})
::coercion/response-coercion (fn [e _] {:status 500, :body (ex-data e)})}))
reitit.ring.coercion/coerce-request-middleware
reitit.ring.coercion/coerce-response-middleware]}}))]
(ring/router
["/plus"
{:get
{:parameters {:query {:x int?, :y int?}}
:responses {200 {:body {:total pos-int?}}}
:handler (fn [{{{:keys [x y]} :query} :parameters}]
{:status 200, :body {:total (+ x y)}})}}]
{:data {:coercion reitit.coercion.spec/coercion
:middleware [(exception/create-exception-middleware
(merge
exception/default-handlers
{::coercion/request-coercion (fn [e _] {:status 400, :body (ex-data e)})
::coercion/response-coercion (fn [e _] {:status 500, :body (ex-data e)})}))
reitit.ring.coercion/coerce-request-middleware
reitit.ring.coercion/coerce-response-middleware]}}))]
(testing "success"
(let [{:keys [status body]} (app {:uri "/plus", :request-method :get, :query-params {"x" "1", "y" "2"}})]
(is (= 200 status))

View file

@ -8,10 +8,10 @@
(deftest muuntaja-test
(let [data {:kikka "kukka"}
app (ring/ring-handler
(ring/router
["/ping" {:get (constantly {:status 200, :body data})}]
{:data {:muuntaja m/instance
:middleware [muuntaja/format-middleware]}}))]
(ring/router
["/ping" {:get (constantly {:status 200, :body data})}]
{:data {:muuntaja m/instance
:middleware [muuntaja/format-middleware]}}))]
(is (= data (->> {:request-method :get, :uri "/ping"}
(app)
:body
@ -22,26 +22,26 @@
no-edn-decode (m/create (-> m/default-options (update-in [:formats "application/edn"] dissoc :decoder)))
just-edn (m/create (-> m/default-options (m/select-formats ["application/edn"])))
app (ring/ring-handler
(ring/router
[["/defaults"
{:get identity}]
["/explicit-defaults"
{:muuntaja with-defaults
:get identity}]
["/no-edn-decode"
{:muuntaja no-edn-decode
:get identity}]
["/just-edn"
{:muuntaja just-edn
:get identity}]
["/form-params"
{:post {:parameters {:form {:x string?}}
:handler identity}}]
["/swagger.json"
{:get {:no-doc true
:handler (swagger/create-swagger-handler)}}]]
{:data {:muuntaja m/instance
:middleware [muuntaja/format-middleware]}}))
(ring/router
[["/defaults"
{:get identity}]
["/explicit-defaults"
{:muuntaja with-defaults
:get identity}]
["/no-edn-decode"
{:muuntaja no-edn-decode
:get identity}]
["/just-edn"
{:muuntaja just-edn
:get identity}]
["/form-params"
{:post {:parameters {:form {:x string?}}
:handler identity}}]
["/swagger.json"
{:get {:no-doc true
:handler (swagger/create-swagger-handler)}}]]
{:data {:muuntaja m/instance
:middleware [muuntaja/format-middleware]}}))
spec (fn [method path]
(let [path (keyword path)]
(-> {:request-method :get :uri "/swagger.json"}
@ -97,41 +97,41 @@
(deftest muuntaja-swagger-parts-test
(let [app (ring/ring-handler
(ring/router
[["/request"
{:middleware [muuntaja/format-negotiate-middleware
muuntaja/format-request-middleware]
:get identity}]
["/response"
{:middleware [muuntaja/format-negotiate-middleware
muuntaja/format-response-middleware]
:get identity}]
["/both"
{:middleware [muuntaja/format-negotiate-middleware
muuntaja/format-response-middleware
muuntaja/format-request-middleware]
:get identity}]
["/form-request"
{:middleware [muuntaja/format-negotiate-middleware
muuntaja/format-request-middleware]
:post {:parameters {:form {:x string?}}
:handler identity}}]
["/form-response"
{:middleware [muuntaja/format-negotiate-middleware
muuntaja/format-response-middleware]
:post {:parameters {:form {:x string?}}
:handler identity}}]
["/form-with-both"
{:middleware [muuntaja/format-negotiate-middleware
muuntaja/format-response-middleware
muuntaja/format-request-middleware]
:post {:parameters {:form {:x string?}}
:handler identity}}]
(ring/router
[["/request"
{:middleware [muuntaja/format-negotiate-middleware
muuntaja/format-request-middleware]
:get identity}]
["/response"
{:middleware [muuntaja/format-negotiate-middleware
muuntaja/format-response-middleware]
:get identity}]
["/both"
{:middleware [muuntaja/format-negotiate-middleware
muuntaja/format-response-middleware
muuntaja/format-request-middleware]
:get identity}]
["/form-request"
{:middleware [muuntaja/format-negotiate-middleware
muuntaja/format-request-middleware]
:post {:parameters {:form {:x string?}}
:handler identity}}]
["/form-response"
{:middleware [muuntaja/format-negotiate-middleware
muuntaja/format-response-middleware]
:post {:parameters {:form {:x string?}}
:handler identity}}]
["/form-with-both"
{:middleware [muuntaja/format-negotiate-middleware
muuntaja/format-response-middleware
muuntaja/format-request-middleware]
:post {:parameters {:form {:x string?}}
:handler identity}}]
["/swagger.json"
{:get {:no-doc true
:handler (swagger/create-swagger-handler)}}]]
{:data {:muuntaja m/instance}}))
["/swagger.json"
{:get {:no-doc true
:handler (swagger/create-swagger-handler)}}]]
{:data {:muuntaja m/instance}}))
spec (fn [method path]
(-> {:request-method :get :uri "/swagger.json"}
(app) :body :paths (get path) method))

View file

@ -6,9 +6,9 @@
(deftest parameters-test
(let [app (ring/ring-handler
(ring/router
["/ping" {:get #(select-keys % [:params :query-params])}]
{:data {:middleware [parameters/parameters-middleware]}}))]
(ring/router
["/ping" {:get #(select-keys % [:params :query-params])}]
{:data {:middleware [parameters/parameters-middleware]}}))]
(is (= {:query-params {"kikka" "kukka"}
:params {"kikka" "kukka"}}
(app {:request-method :get
@ -17,14 +17,14 @@
(deftest parameters-swagger-test
(let [app (ring/ring-handler
(ring/router
[["/form-params" {:post {:parameters {:form {:x string?}}
:handler identity}}]
["/body-params" {:post {:parameters {:body {:x string?}}
:handler identity}}]
["/swagger.json" {:get {:no-doc true
:handler (swagger/create-swagger-handler)}}]]
{:data {:middleware [parameters/parameters-middleware]}}))
(ring/router
[["/form-params" {:post {:parameters {:form {:x string?}}
:handler identity}}]
["/body-params" {:post {:parameters {:body {:x string?}}
:handler identity}}]
["/swagger.json" {:get {:no-doc true
:handler (swagger/create-swagger-handler)}}]]
{:data {:middleware [parameters/parameters-middleware]}}))
spec (fn [path]
(-> {:request-method :get :uri "/swagger.json"}
app

View file

@ -12,23 +12,23 @@
(deftest coercion-test
(let [r (r/router
[["/schema" {:coercion reitit.coercion.schema/coercion}
["/:number/:keyword" {:parameters {:path {:number s/Int
:keyword s/Keyword}
:query (s/maybe {:int s/Int, :ints [s/Int], :map {s/Int s/Int}})}}]]
["/malli" {:coercion reitit.coercion.malli/coercion}
["/:number/:keyword" {:parameters {:path [:map [:number int?] [:keyword keyword?]]
:query [:maybe [:map [:int int?]
[:ints [:vector int?]]
[:map [:map-of int? int?]]]]}}]]
["/spec" {:coercion reitit.coercion.spec/coercion}
["/:number/:keyword" {:parameters {:path {:number int?
:keyword keyword?}
:query (ds/maybe {:int int?, :ints [int?], :map {int? int?}})}}]]
["/none"
["/:number/:keyword" {:parameters {:path {:number int?
:keyword keyword?}}}]]]
{:compile coercion/compile-request-coercers})]
[["/schema" {:coercion reitit.coercion.schema/coercion}
["/:number/:keyword" {:parameters {:path {:number s/Int
:keyword s/Keyword}
:query (s/maybe {:int s/Int, :ints [s/Int], :map {s/Int s/Int}})}}]]
["/malli" {:coercion reitit.coercion.malli/coercion}
["/:number/:keyword" {:parameters {:path [:map [:number int?] [:keyword keyword?]]
:query [:maybe [:map [:int int?]
[:ints [:vector int?]]
[:map [:map-of int? int?]]]]}}]]
["/spec" {:coercion reitit.coercion.spec/coercion}
["/:number/:keyword" {:parameters {:path {:number int?
:keyword keyword?}
:query (ds/maybe {:int int?, :ints [int?], :map {int? int?}})}}]]
["/none"
["/:number/:keyword" {:parameters {:path {:number int?
:keyword keyword?}}}]]]
{:compile coercion/compile-request-coercers})]
(testing "schema-coercion"
(testing "succeeds"
@ -80,10 +80,10 @@
(deftest data-spec-example-test
(let [router (r/router
["/:company/users/:user-id" {:name ::user-view
:coercion reitit.coercion.spec/coercion
:parameters {:path {:company string?
:user-id int?}}}]
{:compile coercion/compile-request-coercers})]
["/:company/users/:user-id" {:name ::user-view
:coercion reitit.coercion.spec/coercion
:parameters {:path {:company string?
:user-id int?}}}]
{:compile coercion/compile-request-coercers})]
(is (= {:path {:user-id 123, :company "metosin"}}
(:parameters (match-by-path-and-coerce! router "/metosin/users/123"))))))

View file

@ -21,38 +21,38 @@
(is (= nil
(r/match-by-path router "/api")))
(is (= (r/map->Match
{:template "/api/ipa/:size"
:data {:name ::beer}
:path "/api/ipa/large"
:path-params {:size "large"}})
{:template "/api/ipa/:size"
:data {:name ::beer}
:path "/api/ipa/large"
:path-params {:size "large"}})
(r/match-by-path router "/api/ipa/large")))
(is (= (r/map->Match
{:template "/api/ipa/:size"
:data {:name ::beer}
:path "/api/ipa/large"
:path-params {:size "large"}})
{:template "/api/ipa/:size"
:data {:name ::beer}
:path "/api/ipa/large"
:path-params {:size "large"}})
(r/match-by-name router ::beer {:size "large"})))
(is (= (r/map->Match
{:template "/api/ipa/:size"
:data {:name ::beer}
:path "/api/ipa/large"
:path-params {:size "large"}})
{:template "/api/ipa/:size"
:data {:name ::beer}
:path "/api/ipa/large"
:path-params {:size "large"}})
(r/match-by-name router ::beer {:size :large})))
(is (= nil (r/match-by-name router "ILLEGAL")))
(is (= [::beer] (r/route-names router)))
(testing "name-based routing with missing parameters"
(is (= (r/map->PartialMatch
{:template "/api/ipa/:size"
:data {:name ::beer}
:required #{:size}
:path-params nil})
{:template "/api/ipa/:size"
:data {:name ::beer}
:required #{:size}
:path-params nil})
(r/match-by-name router ::beer)))
(is (r/partial-match? (r/match-by-name router ::beer)))
(is (thrown-with-msg?
ExceptionInfo
#"^missing path-params for route /api/ipa/:size -> \#\{:size\}$"
(r/match-by-name! router ::beer))))))
ExceptionInfo
#"^missing path-params for route /api/ipa/:size -> \#\{:size\}$"
(r/match-by-name! router ::beer))))))
(testing "decode %-encoded path params"
(let [router (r/router [["/one-param-path/:param1" ::one]
@ -71,14 +71,14 @@
(testing "complex"
(let [router (r/router
[["/:abba" ::abba]
["/abba/1" ::abba2]
["/:jabba/2" ::jabba2]
["/:abba/:dabba/doo" ::doo]
["/abba/dabba/boo/baa" ::baa]
["/abba/:dabba/boo" ::boo]
["/:jabba/:dabba/:doo/:daa/*foo" ::wild]]
{:router r})
[["/:abba" ::abba]
["/abba/1" ::abba2]
["/:jabba/2" ::jabba2]
["/:abba/:dabba/doo" ::doo]
["/abba/dabba/boo/baa" ::baa]
["/abba/:dabba/boo" ::boo]
["/:jabba/:dabba/:doo/:daa/*foo" ::wild]]
{:router r})
by-path #(-> router (r/match-by-path %) :data :name)]
(is (= ::abba (by-path "/abba")))
(is (= ::abba2 (by-path "/abba/1")))
@ -93,19 +93,19 @@
(testing "bracket-params"
(testing "successful"
(let [router (r/router
[["/{abba}" ::abba]
["/abba/1" ::abba2]
["/{jabba}/2" ::jabba2]
["/{abba}/{dabba}/doo" ::doo]
["/abba/dabba/boo/baa" ::baa]
["/abba/{dabba}/boo" ::boo]
["/{a/jabba}/{a.b/dabba}/{a.b.c/doo}/{a.b.c.d/daa}/{*foo/bar}" ::wild]
["/files/file-{name}.html" ::html]
["/files/file-{name}.json" ::json]
["/{eskon}/{saum}/pium\u2215paum" ::loru]
["/{🌈}🤔/🎈" ::emoji]
["/extra-end}s-are/ok" ::bracket]]
{:router r})
[["/{abba}" ::abba]
["/abba/1" ::abba2]
["/{jabba}/2" ::jabba2]
["/{abba}/{dabba}/doo" ::doo]
["/abba/dabba/boo/baa" ::baa]
["/abba/{dabba}/boo" ::boo]
["/{a/jabba}/{a.b/dabba}/{a.b.c/doo}/{a.b.c.d/daa}/{*foo/bar}" ::wild]
["/files/file-{name}.html" ::html]
["/files/file-{name}.json" ::json]
["/{eskon}/{saum}/pium\u2215paum" ::loru]
["/{🌈}🤔/🎈" ::emoji]
["/extra-end}s-are/ok" ::bracket]]
{:router r})
by-path #(-> router (r/match-by-path %) ((juxt (comp :name :data) :path-params)))]
(is (= [::abba {:abba "abba"}] (by-path "/abba")))
(is (= [::abba2 {}] (by-path "/abba/1")))
@ -130,22 +130,22 @@
(testing "invalid syntax fails fast"
(testing "unclosed brackets"
(is (thrown-with-msg?
ExceptionInfo
#":reitit.trie/unclosed-brackets"
(r/router ["/kikka/{kukka"]))))
ExceptionInfo
#":reitit.trie/unclosed-brackets"
(r/router ["/kikka/{kukka"]))))
(testing "multiple terminators"
(is (thrown-with-msg?
ExceptionInfo
#":reitit.trie/multiple-terminators"
(r/router [["/{kukka}.json"]
["/{kukka}-json"]]))))))
ExceptionInfo
#":reitit.trie/multiple-terminators"
(r/router [["/{kukka}.json"]
["/{kukka}-json"]]))))))
(testing "empty path segments"
(let [router (r/router
[["/items" ::list]
["/items/:id" ::item]
["/items/:id/:side" ::deep]]
{:router r})
[["/items" ::list]
["/items/:id" ::item]
["/items/:id/:side" ::deep]]
{:router r})
matches #(-> router (r/match-by-path %) :data :name)]
(is (= ::list (matches "/items")))
(is (= nil (matches "/items/")))
@ -169,28 +169,28 @@
(is (= nil
(r/match-by-path router "/api")))
(is (= (r/map->Match
{:template "/api/ipa/large"
:data {:name ::beer}
:path "/api/ipa/large"
:path-params {}})
{:template "/api/ipa/large"
:data {:name ::beer}
:path "/api/ipa/large"
:path-params {}})
(r/match-by-path router "/api/ipa/large")))
(is (= (r/map->Match
{:template "/api/ipa/large"
:data {:name ::beer}
:path "/api/ipa/large"
:path-params {:size "large"}})
{:template "/api/ipa/large"
:data {:name ::beer}
:path "/api/ipa/large"
:path-params {:size "large"}})
(r/match-by-name router ::beer {:size "large"})))
(is (= nil (r/match-by-name router "ILLEGAL")))
(is (= [::beer] (r/route-names router)))
(testing "can't be created with wildcard routes"
(is (thrown-with-msg?
ExceptionInfo
#"can't create :lookup-router with wildcard routes"
(r/lookup-router
(impl/resolve-routes
["/api/:version/ping"]
(r/default-router-options)))))))
ExceptionInfo
#"can't create :lookup-router with wildcard routes"
(r/lookup-router
(impl/resolve-routes
["/api/:version/ping"]
(r/default-router-options)))))))
r/lookup-router :lookup-router
r/single-static-path-router :single-static-path-router
@ -216,14 +216,14 @@
(swap! compile-times inc)
(constantly path))
router (r/router
["/api" {:roles #{:admin}}
["/ping" ::ping]
["/pong" ::pong]
["/hidden" {:invalid? true}
["/utter"]
["/crap"]]]
{:coerce coerce
:compile compile})]
["/api" {:roles #{:admin}}
["/ping" ::ping]
["/pong" ::pong]
["/hidden" {:invalid? true}
["/utter"]
["/crap"]]]
{:coerce coerce
:compile compile})]
(testing "routes are coerced"
(is (= [["/api/ping" {:name ::ping
@ -281,10 +281,10 @@
router (r/router routes)]
(is (= expected (impl/resolve-routes routes (r/default-router-options))))
(is (= (r/map->Match
{:template "/api/user/:id/:sub-id"
:data {:mw [:api], :parameters {:id "String", :sub-id "String"}}
:path "/api/user/1/2"
:path-params {:id "1", :sub-id "2"}})
{:template "/api/user/:id/:sub-id"
:data {:mw [:api], :parameters {:id "String", :sub-id "String"}}
:path "/api/user/1/2"
:path-params {:id "1", :sub-id "2"}})
(r/match-by-path router "/api/user/1/2"))))))
(deftest conflicting-routes-test
@ -334,10 +334,10 @@
(testing "router with conflicting routes"
(testing "throws by default"
(is (thrown-with-msg?
ExceptionInfo
#"Router contains conflicting route paths"
(r/router
[["/a"] ["/a"]]))))
ExceptionInfo
#"Router contains conflicting route paths"
(r/router
[["/a"] ["/a"]]))))
(testing "can be configured to ignore with route data"
(are [paths expected]
(let [router (r/router paths)]
@ -364,9 +364,9 @@
(testing "unmarked path conflicts throw"
(are [paths]
(is (thrown-with-msg?
ExceptionInfo
#"Router contains conflicting route paths"
(r/router paths)))
ExceptionInfo
#"Router contains conflicting route paths"
(r/router paths)))
[["/a"] ["/a" {:conflicting true}]]
[["/a" {:conflicting true}] ["/a"]])))
(testing "can be configured to ignore with router option"
@ -375,10 +375,10 @@
(testing "name conflicts"
(testing "router with conflicting routes always throws"
(is (thrown-with-msg?
ExceptionInfo
#"Router contains conflicting route names"
(r/router
[["/1" ::1] ["/2" ::1]]))))))
ExceptionInfo
#"Router contains conflicting route names"
(r/router
[["/1" ::1] ["/2" ::1]]))))))
(deftest match->path-test
(let [router (r/router ["/:a/:b" ::route])]
@ -406,10 +406,10 @@
(r/routes)))))
(testing "sequential route definition fails"
(is (thrown?
#?(:clj Exception, :cljs js/Error)
(-> ["/api"
(list "/ipa")]
(r/router))))))
#?(:clj Exception, :cljs js/Error)
(-> ["/api"
(list "/ipa")]
(r/router))))))
(defrecord Named [n]
r/Expand
@ -422,12 +422,12 @@
(deftest routing-order-test-229
(let [router (r/router
[["/" :root]
["/" {:name :create :method :post}]]
{:conflicts nil})
[["/" :root]
["/" {:name :create :method :post}]]
{:conflicts nil})
router2 (r/router
[["/*a" :root]
["/:a/b/c/d" {:name :create :method :post}]]
{:conflicts nil})]
[["/*a" :root]
["/:a/b/c/d" {:name :create :method :post}]]
{:conflicts nil})]
(is (= :root (-> (r/match-by-path router "/") :data :name)))
(is (= :root (-> (r/match-by-path router2 "/") :data :name)))))

View file

@ -17,12 +17,12 @@
(are [exception]
(are [error routes]
(is (thrown-with-msg?
ExceptionInfo
error
(r/router
routes
{:validate rs/validate
:exception exception})))
ExceptionInfo
error
(r/router
routes
{:validate rs/validate
:exception exception})))
#"Router contains conflicting route paths"
[["/:a/1"]

View file

@ -9,9 +9,9 @@
(defn execute [interceptors ctx]
(as-> ctx $
(reduce #(%2 %1) $ (keep :enter interceptors))
(reduce #(%2 %1) $ (reverse (keep :leave interceptors)))
(:response $)))
(reduce #(%2 %1) $ (keep :enter interceptors))
(reduce #(%2 %1) $ (reverse (keep :leave interceptors)))
(:response $)))
(defn f [value ctx]
(update ctx :request conj value))
@ -36,8 +36,8 @@
(create interceptors nil))
([interceptors opts]
(let [chain (interceptor/chain
(conj interceptors handler)
:data opts)]
(conj interceptors handler)
:data opts)]
(partial execute chain))))
(deftest expand-interceptor-test
@ -74,9 +74,9 @@
(testing "missing keyword"
(is (thrown-with-msg?
ExceptionInfo
#"Interceptor :enter not found in registry"
(create [:enter]))))
ExceptionInfo
#"Interceptor :enter not found in registry"
(create [:enter]))))
(testing "existing keyword, compiling to nil"
(let [app (create [:enter] {::interceptor/registry {:enter {:compile (constantly nil)}}})]
@ -134,9 +134,9 @@
(testing "too deeply compiled interceptor fails"
(binding [interceptor/*max-compile-depth* 2]
(is (thrown?
ExceptionInfo
#"Too deep Interceptor compilation"
(create [[i3 :value]])))))
ExceptionInfo
#"Too deep Interceptor compilation"
(create [[i3 :value]])))))
(testing "nil unmounts the interceptor"
(let [app (create [{:compile (constantly nil)}
@ -155,11 +155,11 @@
(testing "interceptor-handler"
(let [api-interceptor (interceptor :api)
router (interceptor/router
[["/ping" handler]
["/api" {:interceptors [api-interceptor]}
["/ping" handler]
["/admin" {:interceptors [[interceptor :admin]]}
["/ping" handler]]]])
[["/ping" handler]
["/api" {:interceptors [api-interceptor]}
["/ping" handler]
["/admin" {:interceptors [[interceptor :admin]]}
["/ping" handler]]]])
app (create-app router)]
(testing "not found"
@ -179,8 +179,8 @@
i2 {:name ::i2, :compile (constantly nil)}
i3 (interceptor ::i3)
router (interceptor/router
["/api" {:interceptors [i1 i2 i3 i2]
:handler handler}])
["/api" {:interceptors [i1 i2 i3 i2]
:handler handler}])
app (create-app router)]
(is (= [::enter_i1 ::enter_i3 :ok ::leave_i3 ::leave_i1] (app "/api")))
@ -221,12 +221,12 @@
(let [debug-i (enter ::debug)
create (fn [options]
(create-app
(interceptor/router
["/ping" {:interceptors [(enter ::olipa)
(enter ::kerran)
(enter ::avaruus)]
:handler handler}]
options)))
(interceptor/router
["/ping" {:interceptors [(enter ::olipa)
(enter ::kerran)
(enter ::avaruus)]
:handler handler}]
options)))
inject-debug (interceptor/transform-butlast #(interleave % (repeat debug-i)))
sort-interceptors (interceptor/transform-butlast (partial sort-by :name))]

View file

@ -15,10 +15,10 @@
(create middleware nil))
([middleware opts]
(middleware/chain
middleware
handler
:data
opts)))
middleware
handler
:data
opts)))
(deftest expand-middleware-test
@ -61,9 +61,9 @@
(testing "missing keyword"
(is (thrown-with-msg?
ExceptionInfo
#"Middleware :wrap not found in registry"
(create [:wrap]))))
ExceptionInfo
#"Middleware :wrap not found in registry"
(create [:wrap]))))
(testing "existing keyword, compiling to nil"
(let [app (create [:wrap] {::middleware/registry {:wrap {:compile (constantly nil)}}})]
@ -142,9 +142,9 @@
(testing "too deeply compiled Middleware fails"
(binding [middleware/*max-compile-depth* 2]
(is (thrown?
ExceptionInfo
#"Too deep Middleware compilation"
(create [[(middleware/map->Middleware mw3) :value]])))))
ExceptionInfo
#"Too deep Middleware compilation"
(create [[(middleware/map->Middleware mw3) :value]])))))
(testing "nil unmounts the middleware"
(let [app (create [{:compile (constantly nil)}
@ -162,9 +162,9 @@
(testing "all paths should have a handler"
(is (thrown-with-msg?
ExceptionInfo
#"path \"/ping\" doesn't have a :handler defined"
(middleware/router ["/ping"]))))
ExceptionInfo
#"path \"/ping\" doesn't have a :handler defined"
(middleware/router ["/ping"]))))
(testing "middleware-handler"
(let [mw (fn [handler value]
@ -173,11 +173,11 @@
api-mw #(mw % :api)
handler #(conj % :ok)
router (middleware/router
[["/ping" handler]
["/api" {:middleware [api-mw]}
["/ping" handler]
["/admin" {:middleware [[mw :admin]]}
["/ping" handler]]]])
[["/ping" handler]
["/api" {:middleware [api-mw]}
["/ping" handler]
["/admin" {:middleware [[mw :admin]]}
["/ping" handler]]]])
app (create-app router)]
(testing "not found"
@ -197,9 +197,9 @@
mw2 {:name ::mw2, :compile (constantly nil)}
mw3 {:name ::mw3, :wrap #(mw % ::mw3)}
router (middleware/router
["/api" {:name ::api
:middleware [mw1 mw2 mw3 mw2]
:handler handler}])
["/api" {:name ::api
:middleware [mw1 mw2 mw3 mw2]
:handler handler}])
app (create-app router)]
(is (= [::mw1 ::mw3 :ok ::mw3 ::mw1] (app "/api")))
@ -241,12 +241,12 @@
debug-mw {:name ::debug, :wrap #(wrap % ::debug)}
create (fn [options]
(create-app
(middleware/router
["/ping" {:middleware [{:name ::olipa, :wrap #(wrap % ::olipa)}
{:name ::kerran, :wrap #(wrap % ::kerran)}
{:name ::avaruus, :wrap #(wrap % ::avaruus)}]
:handler #(conj % :ok)}]
options)))]
(middleware/router
["/ping" {:middleware [{:name ::olipa, :wrap #(wrap % ::olipa)}
{:name ::kerran, :wrap #(wrap % ::kerran)}
{:name ::avaruus, :wrap #(wrap % ::avaruus)}]
:handler #(conj % :ok)}]
options)))]
(testing "by default, all middleware are applied in order"
(let [app (create nil)]

View file

@ -77,19 +77,19 @@
(deftest spec-coercion-test
(let [create (fn [middleware]
(ring/ring-handler
(ring/router
["/api"
["/plus/:e"
{:get {:parameters {:query {(ds/opt :a) int?}
:body {:b int?}
:form {:c int?}
:header {:d int?}
:path {:e int?}}
:responses {200 {:body {:total pos-int?}}
500 {:description "fail"}}
:handler handler}}]]
{:data {:middleware middleware
:coercion spec/coercion}})))]
(ring/router
["/api"
["/plus/:e"
{:get {:parameters {:query {(ds/opt :a) int?}
:body {:b int?}
:form {:c int?}
:header {:d int?}
:path {:e int?}}
:responses {200 {:body {:total pos-int?}}
500 {:description "fail"}}
:handler handler}}]]
{:data {:middleware middleware
:coercion spec/coercion}})))]
(testing "without exception handling"
(let [app (create [rrc/coerce-request-middleware
@ -111,15 +111,15 @@
(testing "invalid request"
(is (thrown-with-msg?
ExceptionInfo
#"Request coercion failed"
(app invalid-request1))))
ExceptionInfo
#"Request coercion failed"
(app invalid-request1))))
(testing "invalid response"
(is (thrown-with-msg?
ExceptionInfo
#"Response coercion failed"
(app invalid-request2))))))
ExceptionInfo
#"Response coercion failed"
(app invalid-request2))))))
(testing "with exception handling"
(let [app (create [rrc/coerce-exceptions-middleware
@ -144,19 +144,19 @@
(deftest schema-coercion-test
(let [create (fn [middleware]
(ring/ring-handler
(ring/router
["/api"
["/plus/:e"
{:get {:parameters {:query {(s/optional-key :a) s/Int}
:body {:b s/Int}
:form {:c s/Int}
:header {:d s/Int}
:path {:e s/Int}}
:responses {200 {:body {:total (s/constrained s/Int pos? 'positive)}}
500 {:description "fail"}}
:handler handler}}]]
{:data {:middleware middleware
:coercion schema/coercion}})))]
(ring/router
["/api"
["/plus/:e"
{:get {:parameters {:query {(s/optional-key :a) s/Int}
:body {:b s/Int}
:form {:c s/Int}
:header {:d s/Int}
:path {:e s/Int}}
:responses {200 {:body {:total (s/constrained s/Int pos? 'positive)}}
500 {:description "fail"}}
:handler handler}}]]
{:data {:middleware middleware
:coercion schema/coercion}})))]
(testing "withut exception handling"
(let [app (create [rrc/coerce-request-middleware
@ -175,19 +175,19 @@
(testing "invalid request"
(is (thrown-with-msg?
ExceptionInfo
#"Request coercion failed"
(app invalid-request1)))
ExceptionInfo
#"Request coercion failed"
(app invalid-request1)))
(is (thrown-with-msg?
ExceptionInfo
#"Request coercion failed"
(app valid-request3))))
ExceptionInfo
#"Request coercion failed"
(app valid-request3))))
(testing "invalid response"
(is (thrown-with-msg?
ExceptionInfo
#"Response coercion failed"
(app invalid-request2))))))
ExceptionInfo
#"Response coercion failed"
(app invalid-request2))))))
(testing "with exception handling"
(let [app (create [rrc/coerce-exceptions-middleware
@ -210,50 +210,50 @@
(deftest malli-coercion-test
(let [create (fn [middleware]
(ring/ring-handler
(ring/router
["/api"
(ring/router
["/api"
["/validate" {:summary "just validation"
:coercion (reitit.coercion.malli/create {:transformers {}})
:post {:parameters {:body [:map [:x int?]]}
:responses {200 {:body [:map [:x int?]]}}
:handler (fn [req]
{:status 200
:body (-> req :parameters :body)})}}]
["/validate" {:summary "just validation"
:coercion (reitit.coercion.malli/create {:transformers {}})
:post {:parameters {:body [:map [:x int?]]}
:responses {200 {:body [:map [:x int?]]}}
:handler (fn [req]
{:status 200
:body (-> req :parameters :body)})}}]
["/no-op" {:summary "no-operation"
:coercion (reitit.coercion.malli/create {:transformers {}, :validate false})
:post {:parameters {:body [:map [:x int?]]}
:responses {200 {:body [:map [:x int?]]}}
:handler (fn [req]
{:status 200
:body (-> req :parameters :body)})}}]
["/no-op" {:summary "no-operation"
:coercion (reitit.coercion.malli/create {:transformers {}, :validate false})
:post {:parameters {:body [:map [:x int?]]}
:responses {200 {:body [:map [:x int?]]}}
:handler (fn [req]
{:status 200
:body (-> req :parameters :body)})}}]
["/skip" {:summary "skip"
:coercion (reitit.coercion.malli/create {:enabled false})
:post {:parameters {:body [:map [:x int?]]}
:responses {200 {:body [:map [:x int?]]}}
:handler (fn [req]
{:status 200
:body (-> req :parameters :body)})}}]
["/or" {:post {:summary "accepts either of two map schemas"
:parameters {:body [:or [:map [:x int?]] [:map [:y int?]]]}
:responses {200 {:body [:map [:msg string?]]}}
:handler (fn [{{{:keys [x]} :body} :parameters}]
["/skip" {:summary "skip"
:coercion (reitit.coercion.malli/create {:enabled false})
:post {:parameters {:body [:map [:x int?]]}
:responses {200 {:body [:map [:x int?]]}}
:handler (fn [req]
{:status 200
:body {:msg (if x "you sent x" "you sent y")}})}}]
:body (-> req :parameters :body)})}}]
["/plus/:e" {:get {:parameters {:query [:map [:a {:optional true} int?]]
:body [:map [:b int?]]
:form [:map [:c [int? {:default 3}]]]
:header [:map [:d int?]]
:path [:map [:e int?]]}
:responses {200 {:body [:map [:total pos-int?]]}
500 {:description "fail"}}
:handler handler}}]]
{:data {:middleware middleware
:coercion malli/coercion}})))]
["/or" {:post {:summary "accepts either of two map schemas"
:parameters {:body [:or [:map [:x int?]] [:map [:y int?]]]}
:responses {200 {:body [:map [:msg string?]]}}
:handler (fn [{{{:keys [x]} :body} :parameters}]
{:status 200
:body {:msg (if x "you sent x" "you sent y")}})}}]
["/plus/:e" {:get {:parameters {:query [:map [:a {:optional true} int?]]
:body [:map [:b int?]]
:form [:map [:c [int? {:default 3}]]]
:header [:map [:d int?]]
:path [:map [:e int?]]}
:responses {200 {:body [:map [:total pos-int?]]}
500 {:description "fail"}}
:handler handler}}]]
{:data {:middleware middleware
:coercion malli/coercion}})))]
(testing "without exception handling"
(let [app (create [rrc/coerce-request-middleware
@ -279,15 +279,15 @@
(testing "invalid request"
(is (thrown-with-msg?
ExceptionInfo
#"Request coercion failed"
(app invalid-request1))))
ExceptionInfo
#"Request coercion failed"
(app invalid-request1))))
(testing "invalid response"
(is (thrown-with-msg?
ExceptionInfo
#"Response coercion failed"
(app invalid-request2))))))
ExceptionInfo
#"Response coercion failed"
(app invalid-request2))))))
(testing "with exception handling"
(let [app (create [rrc/coerce-exceptions-middleware
@ -350,15 +350,15 @@
{:status 200, :body (assoc body :response true)})}})
->app (fn [options]
(ring/ring-handler
(ring/router
["/api"
["/default" (endpoint [:map [:x int?]])]
["/closed" (endpoint [:map {:closed true} [:x int?]])]
["/open" (endpoint [:map {:closed false} [:x int?]])]]
{:data {:middleware [rrc/coerce-exceptions-middleware
rrc/coerce-request-middleware
rrc/coerce-response-middleware]
:coercion (malli/create options)}})))
(ring/router
["/api"
["/default" (endpoint [:map [:x int?]])]
["/closed" (endpoint [:map {:closed true} [:x int?]])]
["/open" (endpoint [:map {:closed false} [:x int?]])]]
{:data {:middleware [rrc/coerce-exceptions-middleware
rrc/coerce-request-middleware
rrc/coerce-response-middleware]
:coercion (malli/create options)}})))
->request (fn [uri] {:uri (str "/api/" uri)
:request-method :get
:muuntaja/request {:format "application/json"}
@ -414,22 +414,22 @@
(testing "sequence schemas"
(let [app (ring/ring-handler
(ring/router
["/ping" {:get {:parameters {:body [:vector [:map [:message string?]]]}
:responses {200 {:body [:vector [:map [:pong string?]]]}
501 {:body [:vector [:map [:error string?]]]}}
:handler (fn [{{[{:keys [message]}] :body} :parameters :as req}]
(condp = message
"ping" {:status 200
:body [{:pong message}]}
"fail" {:status 501
:body [{:error "fail"}]}
{:status 200
:body {:invalid "response"}}))}}]
{:data {:middleware [rrc/coerce-exceptions-middleware
rrc/coerce-request-middleware
rrc/coerce-response-middleware]
:coercion malli/coercion}}))
(ring/router
["/ping" {:get {:parameters {:body [:vector [:map [:message string?]]]}
:responses {200 {:body [:vector [:map [:pong string?]]]}
501 {:body [:vector [:map [:error string?]]]}}
:handler (fn [{{[{:keys [message]}] :body} :parameters :as req}]
(condp = message
"ping" {:status 200
:body [{:pong message}]}
"fail" {:status 501
:body [{:error "fail"}]}
{:status 200
:body {:invalid "response"}}))}}]
{:data {:middleware [rrc/coerce-exceptions-middleware
rrc/coerce-request-middleware
rrc/coerce-response-middleware]
:coercion malli/coercion}}))
->request (fn [body]
{:uri "/ping"
:request-method :get
@ -454,15 +454,15 @@
(testing "encoding responses"
(let [->app (fn [total-schema]
(ring/ring-handler
(ring/router
["/total" {:get {:parameters {:query [:map [:x :int]]}
:responses {200 {:body [:map [:total total-schema]]}}
:handler (fn [{{{:keys [x]} :query} :parameters}]
{:status 200
:body {:total (* x x)}})}}]
{:data {:middleware [rrc/coerce-request-middleware
rrc/coerce-response-middleware]
:coercion malli/coercion}})))
(ring/router
["/total" {:get {:parameters {:query [:map [:x :int]]}
:responses {200 {:body [:map [:total total-schema]]}}
:handler (fn [{{{:keys [x]} :query} :parameters}]
{:status 200
:body {:total (* x x)}})}}]
{:data {:middleware [rrc/coerce-request-middleware
rrc/coerce-response-middleware]
:coercion malli/coercion}})))
call (fn [accept total-schema]
((->app total-schema) {:uri "/total"
:request-method :get
@ -482,18 +482,18 @@
#?(:clj
(deftest muuntaja-test
(let [app (ring/ring-handler
(ring/router
["/api"
["/plus"
{:post {:parameters {:body {:int int?, :keyword keyword?}}
:responses {200 {:body {:int int?, :keyword keyword?}}}
:handler (fn [{{:keys [body]} :parameters}]
{:status 200
:body body})}}]]
{:data {:middleware [muuntaja.middleware/wrap-format
rrc/coerce-request-middleware
rrc/coerce-response-middleware]
:coercion spec/coercion}}))
(ring/router
["/api"
["/plus"
{:post {:parameters {:body {:int int?, :keyword keyword?}}
:responses {200 {:body {:int int?, :keyword keyword?}}}
:handler (fn [{{:keys [body]} :parameters}]
{:status 200
:body body})}}]]
{:data {:middleware [muuntaja.middleware/wrap-format
rrc/coerce-request-middleware
rrc/coerce-response-middleware]
:coercion spec/coercion}}))
request (fn [content-type body]
(-> {:request-method :post
:headers {"content-type" content-type, "accept" content-type}

View file

@ -15,69 +15,69 @@
(deftest route-data-validation-test
(testing "validation is turned off by default"
(is (r/router?
(r/router
["/api" {:handler "identity"}]))))
(r/router
["/api" {:handler "identity"}]))))
(testing "with default spec validates :name, :handler and :middleware"
(is (thrown-with-msg?
ExceptionInfo
#"Invalid route data"
(ring/router
["/api" {:handler "identity"}]
{:validate rrs/validate})))
ExceptionInfo
#"Invalid route data"
(ring/router
["/api" {:handler "identity"}]
{:validate rrs/validate})))
(is (thrown-with-msg?
ExceptionInfo
#"Invalid route data"
(ring/router
["/api" {:handler identity
:name "kikka"}]
{:validate rrs/validate}))))
ExceptionInfo
#"Invalid route data"
(ring/router
["/api" {:handler identity
:name "kikka"}]
{:validate rrs/validate}))))
(testing "all endpoints are validated"
(is (thrown-with-msg?
ExceptionInfo
#"Invalid route data"
(ring/router
["/api" {:patch {:handler "identity"}}]
{:validate rrs/validate}))))
ExceptionInfo
#"Invalid route data"
(ring/router
["/api" {:patch {:handler "identity"}}]
{:validate rrs/validate}))))
(testing "spec can be overridden"
(is (r/router?
(ring/router
["/api" {:handler "identity"}]
{:spec (s/spec any?)
:validate rrs/validate})))
(ring/router
["/api" {:handler "identity"}]
{:spec (s/spec any?)
:validate rrs/validate})))
(testing "predicates are not allowed"
(is (thrown-with-msg?
ExceptionInfo
#":reitit.ring.spec/invalid-specs"
(ring/router
["/api" {:handler "identity"}]
{:spec any?
:validate rrs/validate})))))
ExceptionInfo
#":reitit.ring.spec/invalid-specs"
(ring/router
["/api" {:handler "identity"}]
{:spec any?
:validate rrs/validate})))))
(testing "middleware can contribute to specs"
(is (r/router?
(ring/router
["/api" {:get {:handler identity
:roles #{:admin}}}]
{:validate rrs/validate
:data {:middleware [{:spec (s/keys :opt-un [::roles])
:wrap (fn [handler]
(fn [request]
(handler request)))}]}})))
(ring/router
["/api" {:get {:handler identity
:roles #{:admin}}}]
{:validate rrs/validate
:data {:middleware [{:spec (s/keys :opt-un [::roles])
:wrap (fn [handler]
(fn [request]
(handler request)))}]}})))
(is (thrown-with-msg?
ExceptionInfo
#"Invalid route data"
(ring/router
["/api" {:get {:handler identity
:roles #{:adminz}}}]
{:validate rrs/validate
:data {:middleware [{:spec (s/keys :opt-un [::roles])
:wrap (fn [handler]
(fn [request]
(handler request)))}]}}))))
ExceptionInfo
#"Invalid route data"
(ring/router
["/api" {:get {:handler identity
:roles #{:adminz}}}]
{:validate rrs/validate
:data {:middleware [{:spec (s/keys :opt-un [::roles])
:wrap (fn [handler]
(fn [request]
(handler request)))}]}}))))
(testing "middleware cannot be a list"
(is (thrown-with-msg?
ExceptionInfo
@ -89,46 +89,46 @@
(deftest coercion-spec-test
(is (r/router?
(ring/router
["/api"
["/plus/:e"
{:get {:parameters {:query {:a string?}
:body {:b string?}
:form {:c string?}
:header {:d string?}
:path {:e string?}}
:responses {200 {:body {:total pos-int?}}
400 {:description "fail"}
500 {}}
:handler identity}}]]
{:data {:middleware [rrc/coerce-exceptions-middleware
rrc/coerce-request-middleware
rrc/coerce-response-middleware]
:coercion reitit.coercion.spec/coercion}
:validate rrs/validate})))
(ring/router
["/api"
["/plus/:e"
{:get {:parameters {:query {:a string?}
:body {:b string?}
:form {:c string?}
:header {:d string?}
:path {:e string?}}
:responses {200 {:body {:total pos-int?}}
400 {:description "fail"}
500 {}}
:handler identity}}]]
{:data {:middleware [rrc/coerce-exceptions-middleware
rrc/coerce-request-middleware
rrc/coerce-response-middleware]
:coercion reitit.coercion.spec/coercion}
:validate rrs/validate})))
(is (r/router?
(ring/router
["/api"
["/plus/:e"
{:get {:parameters {:query (s/keys)}
:handler identity}}]]
{:data {:middleware [rrc/coerce-exceptions-middleware
rrc/coerce-request-middleware
rrc/coerce-response-middleware]
:coercion reitit.coercion.spec/coercion}
:validate rrs/validate})))
(ring/router
["/api"
["/plus/:e"
{:get {:parameters {:query (s/keys)}
:handler identity}}]]
{:data {:middleware [rrc/coerce-exceptions-middleware
rrc/coerce-request-middleware
rrc/coerce-response-middleware]
:coercion reitit.coercion.spec/coercion}
:validate rrs/validate})))
(is (thrown-with-msg?
ExceptionInfo
#"Invalid route data"
(ring/router
["/api"
["/plus/:e"
{:get {:responses {"200" {}}
:handler identity}}]]
{:data {:middleware [rrc/coerce-exceptions-middleware
rrc/coerce-request-middleware
rrc/coerce-response-middleware]
:coercion reitit.coercion.spec/coercion}
:validate rrs/validate}))))
ExceptionInfo
#"Invalid route data"
(ring/router
["/api"
["/plus/:e"
{:get {:responses {"200" {}}
:handler identity}}]]
{:data {:middleware [rrc/coerce-exceptions-middleware
rrc/coerce-request-middleware
rrc/coerce-response-middleware]
:coercion reitit.coercion.spec/coercion}
:validate rrs/validate}))))

View file

@ -28,35 +28,35 @@
(testing "nils are removed"
(is (= 123
((ring/routes
(constantly nil)
nil
(constantly 123))
(constantly nil)
nil
(constantly 123))
::irrelevant))))
(testing "can return nil"
(is (= nil
(ring/routes
nil
nil)))))
nil
nil)))))
(deftest ring-router-test
(testing "all paths should have a handler"
(is (thrown-with-msg?
ExceptionInfo
#"path \"/ping\" doesn't have a :handler defined for :get"
(ring/router ["/ping" {:get {}}]))))
ExceptionInfo
#"path \"/ping\" doesn't have a :handler defined for :get"
(ring/router ["/ping" {:get {}}]))))
(testing "ring-handler"
(let [api-mw #(mw % :api)
router (ring/router
["/api" {:middleware [api-mw]}
["/all" handler]
["/get" {:get handler}]
["/users" {:middleware [[mw :users]]
:get handler
:post {:handler handler
:middleware [[mw :post]]}
:handler handler}]])
["/api" {:middleware [api-mw]}
["/all" handler]
["/get" {:get handler}]
["/users" {:middleware [[mw :users]]
:get handler
:post {:handler handler
:middleware [[mw :post]]}
:handler handler}]])
app (ring/ring-handler router)]
(testing "router can be extracted"
@ -95,8 +95,8 @@
(testing "with top-level middleware"
(let [router (ring/router
["/api" {:middleware [[mw :api]]}
["/get" {:get handler}]])
["/api" {:middleware [[mw :api]]}
["/get" {:get handler}]])
app (ring/ring-handler router nil {:middleware [[mw :top]]})]
(testing "router can be extracted"
@ -111,14 +111,14 @@
(testing "named routes"
(let [router (ring/router
[["/api"
["/all" {:handler handler :name ::all}]
["/get" {:get {:handler handler :name ::HIDDEN}
:name ::get}]
["/users" {:get handler
:post handler
:handler handler
:name ::users}]]])
[["/api"
["/all" {:handler handler :name ::all}]
["/get" {:get {:handler handler :name ::HIDDEN}
:name ::get}]
["/users" {:get handler
:post handler
:handler handler
:name ::users}]]])
app (ring/ring-handler router)]
(testing "router can be extracted"
@ -141,21 +141,21 @@
(deftest mw-variadic-test
(let [app (ring/ring-handler
(ring/router
["/" {:middleware [[mw-variadic "kikka" "kakka" "kukka"]]
:handler handler}]))]
(ring/router
["/" {:middleware [[mw-variadic "kikka" "kakka" "kukka"]]
:handler handler}]))]
(is (= {:status 200, :body [:kikka_kakka_kukka :ok]}
(app {:request-method :get, :uri "/"})))))
(deftest enforcing-data-rules-at-runtime-test
(let [handler (constantly {:status 200, :body "ok"})
app (ring/ring-handler
(ring/router
[["/api"
["/ping" handler]
["/admin" {::roles #{:admin}}
["/ping" handler]]]]
{:data {:middleware [wrap-enforce-roles]}}))]
(ring/router
[["/api"
["/ping" handler]
["/admin" {::roles #{:admin}}
["/ping" handler]]]]
{:data {:middleware [wrap-enforce-roles]}}))]
(testing "public handler"
(is (= {:status 200, :body "ok"}
@ -175,8 +175,8 @@
(deftest default-handler-test
(let [response {:status 200, :body "ok"}
router (ring/router
[["/ping" {:get (constantly response)}]
["/pong" (constantly nil)]])
[["/ping" {:get (constantly response)}]
["/pong" (constantly nil)]])
app (ring/ring-handler router)]
(testing "match"
@ -202,9 +202,9 @@
(testing "with custom http responses"
(let [app (ring/ring-handler router (ring/create-default-handler
{:not-found (constantly {:status -404})
:method-not-allowed (constantly {:status -405})
:not-acceptable (constantly {:status -406})}))]
{:not-found (constantly {:status -404})
:method-not-allowed (constantly {:status -405})
:not-acceptable (constantly {:status -406})}))]
(testing "route doesn't match"
(is (= -404 (:status (app {:request-method :get, :uri "/"})))))
(testing "method doesn't match"
@ -214,7 +214,7 @@
(testing "with some custom http responses"
(let [app (ring/ring-handler router (ring/create-default-handler
{:not-found (constantly {:status -404})}))]
{:not-found (constantly {:status -404})}))]
(testing "route doesn't match"
(is (= 405 (:status (app {:request-method :post, :uri "/ping"}))))))))))
@ -227,11 +227,11 @@
(testing "with defaults"
(let [app (ring/ring-handler
(ring/router
[["/get" {:get (constantly response)
:post (constantly response)}]
["/options" {:options (constantly response)}]
["/any" (constantly response)]]))]
(ring/router
[["/get" {:get (constantly response)
:post (constantly response)}]
["/options" {:options (constantly response)}]
["/any" (constantly response)]]))]
(testing "endpoint with a non-options handler"
(let [request {:request-method :options, :uri "/get"}]
@ -258,10 +258,10 @@
(constantly {:status 200, :body "ok"})]]
(let [response {:status 200, :body "ok"}
app (ring/ring-handler
(ring/router
["/get" {:get (constantly response)
:post (constantly response)}]
{::ring/default-options-endpoint endpoint}))]
(ring/router
["/get" {:get (constantly response)
:post (constantly response)}]
{::ring/default-options-endpoint endpoint}))]
(testing "endpoint with a non-options handler"
(let [request {:request-method :options, :uri "/get"}]
@ -270,11 +270,11 @@
(testing "disabled via options"
(let [app (ring/ring-handler
(ring/router
[["/get" {:get (constantly response)}]
["/options" {:options (constantly response)}]
["/any" (constantly response)]]
{::ring/default-options-endpoint nil}))]
(ring/router
[["/get" {:get (constantly response)}]
["/options" {:options (constantly response)}]
["/any" (constantly response)]]
{::ring/default-options-endpoint nil}))]
(testing "endpoint with a non-options handler"
(is (= response (app {:request-method :get, :uri "/get"})))
@ -297,8 +297,8 @@
:post (constantly ok)}]]]
(testing "using :method :add"
(let [app (ring/ring-handler
(ring/router routes)
(ring/redirect-trailing-slash-handler {:method :add}))]
(ring/router routes)
(ring/redirect-trailing-slash-handler {:method :add}))]
(testing "exact matches work"
(is (= ok (app {:request-method :get, :uri "/slash-less"})))
@ -316,8 +316,8 @@
(testing "using :method :strip"
(let [app (ring/ring-handler
(ring/router routes)
(ring/redirect-trailing-slash-handler {:method :strip}))]
(ring/router routes)
(ring/redirect-trailing-slash-handler {:method :strip}))]
(testing "stripping to empty string doesn't match"
(is (= nil (:status (app {:request-method :get, :uri "/"})))))
@ -342,8 +342,8 @@
(testing "without option (equivalent to using :method :both)"
(let [app (ring/ring-handler
(ring/router routes)
(ring/redirect-trailing-slash-handler))]
(ring/router routes)
(ring/redirect-trailing-slash-handler))]
(testing "exact matches work"
(is (= ok (app {:request-method :get, :uri "/slash-less"})))
@ -370,10 +370,10 @@
([x] (reset! value x))))
response {:status 200, :body "ok"}
router (ring/router
[["/ping" {:get (fn [_ respond _]
(respond response))}]
["/pong" (fn [_ respond _]
(respond nil))]])
[["/ping" {:get (fn [_ respond _]
(respond response))}]
["/pong" (fn [_ respond _]
(respond nil))]])
app (ring/ring-handler router)]
(testing "match"
@ -436,12 +436,12 @@
request {:uri "/api/avaruus" :request-method :get}
create (fn [options]
(ring/ring-handler
(ring/router
["/api" {:middleware [(middleware :olipa)]}
["/avaruus" {:middleware [(middleware :kerran)]
:get {:handler handler
:middleware [(middleware :avaruus)]}}]]
options)))]
(ring/router
["/api" {:middleware [(middleware :olipa)]}
["/avaruus" {:middleware [(middleware :kerran)]
:get {:handler handler
:middleware [(middleware :avaruus)]}}]]
options)))]
(testing "by default, all middleware are applied in order"
(let [app (create nil)]
@ -510,9 +510,9 @@
(testing "from path"
(let [app (ring/ring-handler
(ring/router
["/files/*" (create nil)])
(ring/create-default-handler))
(ring/router
["/files/*" (create nil)])
(ring/create-default-handler))
request #(request (str "/files" %))
redirect #(redirect (str "/files" %))]
(testing "different file-types"
@ -553,10 +553,10 @@
(testing "from root"
(let [app (ring/ring-handler
(ring/router [])
(ring/routes
(create {:path "/" :not-found-handler (fn [x] {:status 404 :body "resource-handler"})})
(ring/create-default-handler)))]
(ring/router [])
(ring/routes
(create {:path "/" :not-found-handler (fn [x] {:status 404 :body "resource-handler"})})
(ring/create-default-handler)))]
(testing "different file-types"
(let [response (app (request "/hello.json"))]
(is (= "application/json" (get-in response [:headers "Content-Type"])))
@ -594,10 +594,10 @@
(testing "from path"
(let [app (ring/ring-handler
(ring/router [])
(ring/routes
(create {:path "/files" :not-found-handler (fn [x] {:status 404 :body "resource-handler"})})
(ring/create-default-handler)))
(ring/router [])
(ring/routes
(create {:path "/files" :not-found-handler (fn [x] {:status 404 :body "resource-handler"})})
(ring/create-default-handler)))
request #(request (str "/files" %))
redirect #(redirect (str "/files" %))]
(testing "different file-types"
@ -638,12 +638,11 @@
(is (get-in @result [:headers "Last-Modified"]))
(is (= "<xml><hello>file</hello></xml>\n" (slurp (:body @result))))))))))))))
#?(:clj
(deftest file-resource-handler-not-found-test
(let [redirect (fn [uri] {:status 302, :body "", :headers {"Location" uri}})
request (fn [uri] {:uri uri, :request-method :get})
not-found-handler (fn [_] {:status 404, :body "not-found-handler"})]
(deftest file-resource-handler-not-found-test
(let [redirect (fn [uri] {:status 302, :body "", :headers {"Location" uri}})
request (fn [uri] {:uri uri, :request-method :get})
not-found-handler (fn [_] {:status 404, :body "not-found-handler"})]
(doseq [[name create] [["resource-handler" ring/create-resource-handler]
["file-handler" #(ring/create-file-handler (assoc % :root "dev-resources/public"))]]]
@ -681,15 +680,15 @@
(deftest router-available-in-default-branch
(testing "1-arity"
((ring/ring-handler
(ring/router [])
(fn [{::r/keys [router]}]
(is router)))
(ring/router [])
(fn [{::r/keys [router]}]
(is router)))
{}))
(testing "3-arity"
((ring/ring-handler
(ring/router [])
(fn [{::r/keys [router]} _ _]
(is router)))
(ring/router [])
(fn [{::r/keys [router]} _ _]
(is router)))
{} ::respond ::raise)))
#?(:clj
@ -697,11 +696,11 @@
(testing "in enough concurrent system, path-parameters can bleed"
(doseq [compiler [trie/java-trie-compiler trie/clojure-trie-compiler]]
(let [app (ring/ring-handler
(ring/router
["/:id" (fn [request]
{:status 200
:body (-> request :path-params :id)})])
{::trie/trie-compiler compiler})]
(ring/router
["/:id" (fn [request]
{:status 200
:body (-> request :path-params :id)})])
{::trie/trie-compiler compiler})]
(dotimes [_ 10]
(future
(dotimes [n 100000]

View file

@ -38,10 +38,10 @@
(testing "with invalid routes"
(are [data]
(is (thrown-with-msg?
ExceptionInfo
#"Call to #'reitit.core/router did not conform to spec"
(r/router
data)))
ExceptionInfo
#"Call to #'reitit.core/router did not conform to spec"
(r/router
data)))
;; path
[:invalid {}]
@ -68,10 +68,10 @@
(are [opts]
(is (thrown-with-msg?
ExceptionInfo
#"Call to #'reitit.core/router did not conform to spec"
(r/router
["/api"] opts)))
ExceptionInfo
#"Call to #'reitit.core/router did not conform to spec"
(r/router
["/api"] opts)))
{:path :api}
{:path nil}
@ -85,52 +85,52 @@
(deftest route-data-validation-test
(testing "validation is turned off by default"
(is (r/router? (r/router
["/api" {:handler "identity"}]))))
["/api" {:handler "identity"}]))))
(testing "with default spec validates :name and :handler"
(is (thrown-with-msg?
ExceptionInfo
#"Invalid route data"
(r/router
["/api" {:handler "identity"}]
{:validate rs/validate})))
ExceptionInfo
#"Invalid route data"
(r/router
["/api" {:handler "identity"}]
{:validate rs/validate})))
(is (thrown-with-msg?
ExceptionInfo
#"Invalid route data"
(r/router
["/api" {:name "kikka"}]
{:validate rs/validate}))))
ExceptionInfo
#"Invalid route data"
(r/router
["/api" {:name "kikka"}]
{:validate rs/validate}))))
(testing "spec can be overridden"
(is (r/router? (r/router
["/api" {:handler "identity"}]
{:spec any?
:validate rs/validate})))))
["/api" {:handler "identity"}]
{:spec any?
:validate rs/validate})))))
(deftest parameters-test
(is (s/valid?
::rs/parameters
{:parameters {:query {:a string?}
:body {:b string?}
:form {:c string?}
:header {:d string?}
:path {:e string?}}}))
::rs/parameters
{:parameters {:query {:a string?}
:body {:b string?}
:form {:c string?}
:header {:d string?}
:path {:e string?}}}))
(is (s/valid?
::rs/parameters
{:parameters {:header (s/keys)}}))
::rs/parameters
{:parameters {:header (s/keys)}}))
(is (s/valid?
::rs/responses
{:responses {200 {:description "ok", :body string?}
400 {:description "fail"}
500 {:body string?}
:default {}}}))
::rs/responses
{:responses {200 {:description "ok", :body string?}
400 {:description "fail"}
500 {:body string?}
:default {}}}))
(is (not (s/valid?
::rs/responses
{:responses {"200" {:description "ok", :body string?}}})))
::rs/responses
{:responses {"200" {:description "ok", :body string?}}})))
(is (not (s/valid?
::rs/responses
{:responses {200 {:description :ok, :body string?}}}))))
::rs/responses
{:responses {200 {:description :ok, :body string?}}}))))

View file

@ -13,93 +13,93 @@
(def app
(ring/ring-handler
(ring/router
["/api"
{:swagger {:id ::math}}
(ring/router
["/api"
{:swagger {:id ::math}}
["/swagger.json"
{:get {:no-doc true
:swagger {:info {:title "my-api"}}
:handler (swagger/create-swagger-handler)}}]
["/swagger.json"
{:get {:no-doc true
:swagger {:info {:title "my-api"}}
:handler (swagger/create-swagger-handler)}}]
["/spec" {:coercion spec/coercion}
["/plus/:z"
{:patch {:summary "patch"
["/spec" {:coercion spec/coercion}
["/plus/:z"
{:patch {:summary "patch"
:handler (constantly {:status 200})}
:options {:summary "options"
:middleware [{:data {:swagger {:responses {200 {:description "200"}}}}}]
:handler (constantly {:status 200})}
:options {:summary "options"
:middleware [{:data {:swagger {:responses {200 {:description "200"}}}}}]
:handler (constantly {:status 200})}
:get {:summary "plus"
:parameters {:query {:x int?, :y int?}
:path {:z int?}}
:swagger {:responses {400 {:schema {:type "string"}
:description "kosh"}}}
:responses {200 {:body {:total int?}}
500 {:description "fail"}}
:handler (fn [{{{:keys [x y]} :query
{:keys [z]} :path} :parameters}]
{:status 200, :body {:total (+ x y z)}})}
:post {:summary "plus with body"
:parameters {:body (ds/maybe [int?])
:path {:z int?}}
:swagger {:responses {400 {:schema {:type "string"}
:description "kosh"}}}
:responses {200 {:body {:total int?}}
500 {:description "fail"}}
:handler (fn [{{{:keys [z]} :path
xs :body} :parameters}]
{:status 200, :body {:total (+ (reduce + xs) z)}})}}]]
:get {:summary "plus"
:parameters {:query {:x int?, :y int?}
:path {:z int?}}
:swagger {:responses {400 {:schema {:type "string"}
:description "kosh"}}}
:responses {200 {:body {:total int?}}
500 {:description "fail"}}
:handler (fn [{{{:keys [x y]} :query
{:keys [z]} :path} :parameters}]
{:status 200, :body {:total (+ x y z)}})}
:post {:summary "plus with body"
:parameters {:body (ds/maybe [int?])
:path {:z int?}}
:swagger {:responses {400 {:schema {:type "string"}
:description "kosh"}}}
:responses {200 {:body {:total int?}}
500 {:description "fail"}}
:handler (fn [{{{:keys [z]} :path
xs :body} :parameters}]
{:status 200, :body {:total (+ (reduce + xs) z)}})}}]]
["/malli" {:coercion malli/coercion}
["/plus/*z"
{:get {:summary "plus"
:parameters {:query [:map [:x int?] [:y int?]]
:path [:map [:z int?]]}
:swagger {:responses {400 {:schema {:type "string"}
:description "kosh"}}}
:responses {200 {:body [:map [:total int?]]}
500 {:description "fail"}}
:handler (fn [{{{:keys [x y]} :query
{:keys [z]} :path} :parameters}]
{:status 200, :body {:total (+ x y z)}})}
:post {:summary "plus with body"
:parameters {:body [:maybe [:vector int?]]
:path [:map [:z int?]]}
:swagger {:responses {400 {:schema {:type "string"}
:description "kosh"}}}
:responses {200 {:body [:map [:total int?]]}
500 {:description "fail"}}
:handler (fn [{{{:keys [z]} :path
xs :body} :parameters}]
{:status 200, :body {:total (+ (reduce + xs) z)}})}}]]
["/malli" {:coercion malli/coercion}
["/plus/*z"
{:get {:summary "plus"
:parameters {:query [:map [:x int?] [:y int?]]
:path [:map [:z int?]]}
:swagger {:responses {400 {:schema {:type "string"}
:description "kosh"}}}
:responses {200 {:body [:map [:total int?]]}
500 {:description "fail"}}
:handler (fn [{{{:keys [x y]} :query
{:keys [z]} :path} :parameters}]
{:status 200, :body {:total (+ x y z)}})}
:post {:summary "plus with body"
:parameters {:body [:maybe [:vector int?]]
:path [:map [:z int?]]}
:swagger {:responses {400 {:schema {:type "string"}
:description "kosh"}}}
:responses {200 {:body [:map [:total int?]]}
500 {:description "fail"}}
:handler (fn [{{{:keys [z]} :path
xs :body} :parameters}]
{:status 200, :body {:total (+ (reduce + xs) z)}})}}]]
["/schema" {:coercion schema/coercion}
["/plus/*z"
{:get {:summary "plus"
:parameters {:query {:x s/Int, :y s/Int}
:path {:z s/Int}}
:swagger {:responses {400 {:schema {:type "string"}
:description "kosh"}}}
:responses {200 {:body {:total s/Int}}
500 {:description "fail"}}
:handler (fn [{{{:keys [x y]} :query
{:keys [z]} :path} :parameters}]
{:status 200, :body {:total (+ x y z)}})}
:post {:summary "plus with body"
:parameters {:body (s/maybe [s/Int])
:path {:z s/Int}}
:swagger {:responses {400 {:schema {:type "string"}
:description "kosh"}}}
:responses {200 {:body {:total s/Int}}
500 {:description "fail"}}
:handler (fn [{{{:keys [z]} :path
xs :body} :parameters}]
{:status 200, :body {:total (+ (reduce + xs) z)}})}}]]]
["/schema" {:coercion schema/coercion}
["/plus/*z"
{:get {:summary "plus"
:parameters {:query {:x s/Int, :y s/Int}
:path {:z s/Int}}
:swagger {:responses {400 {:schema {:type "string"}
:description "kosh"}}}
:responses {200 {:body {:total s/Int}}
500 {:description "fail"}}
:handler (fn [{{{:keys [x y]} :query
{:keys [z]} :path} :parameters}]
{:status 200, :body {:total (+ x y z)}})}
:post {:summary "plus with body"
:parameters {:body (s/maybe [s/Int])
:path {:z s/Int}}
:swagger {:responses {400 {:schema {:type "string"}
:description "kosh"}}}
:responses {200 {:body {:total s/Int}}
500 {:description "fail"}}
:handler (fn [{{{:keys [z]} :path
xs :body} :parameters}]
{:status 200, :body {:total (+ (reduce + xs) z)}})}}]]]
{:data {:middleware [swagger/swagger-feature
rrc/coerce-exceptions-middleware
rrc/coerce-request-middleware
rrc/coerce-response-middleware]}})))
{:data {:middleware [swagger/swagger-feature
rrc/coerce-exceptions-middleware
rrc/coerce-request-middleware
rrc/coerce-response-middleware]}})))
(require '[fipp.edn])
(deftest swagger-test
@ -300,21 +300,21 @@
{:get {:no-doc true
:handler (swagger/create-swagger-handler)}}]
app (ring/ring-handler
(ring/router
[["/common" {:swagger {:id #{::one ::two}}}
ping-route]
(ring/router
[["/common" {:swagger {:id #{::one ::two}}}
ping-route]
["/one" {:swagger {:id ::one}}
ping-route
spec-route]
["/one" {:swagger {:id ::one}}
ping-route
spec-route]
["/two" {:swagger {:id ::two}}
ping-route
spec-route
["/deep" {:swagger {:id ::one}}
ping-route]]
["/one-two" {:swagger {:id #{::one ::two}}}
spec-route]]))]
["/two" {:swagger {:id ::two}}
ping-route
spec-route
["/deep" {:swagger {:id ::one}}
ping-route]]
["/one-two" {:swagger {:id #{::one ::two}}}
spec-route]]))]
(is (= ["/common/ping" "/one/ping" "/two/deep/ping"]
(spec-paths app "/one/swagger.json")))
(is (= ["/common/ping" "/two/ping"]
@ -324,8 +324,8 @@
(deftest swagger-ui-config-test
(let [app (swagger-ui/create-swagger-ui-handler
{:path "/"
:config {:jsonEditor true}})]
{:path "/"
:config {:jsonEditor true}})]
(is (= 302 (:status (app {:request-method :get, :uri "/"}))))
(is (= 200 (:status (app {:request-method :get, :uri "/index.html"}))))
(is (= {:jsonEditor true, :url "/swagger.json"}
@ -334,12 +334,12 @@
(deftest without-swagger-id-test
(let [app (ring/ring-handler
(ring/router
[["/ping"
{:get (constantly "ping")}]
["/swagger.json"
{:get {:no-doc true
:handler (swagger/create-swagger-handler)}}]]))]
(ring/router
[["/ping"
{:get (constantly "ping")}]
["/swagger.json"
{:get {:no-doc true
:handler (swagger/create-swagger-handler)}}]]))]
(is (= ["/ping"] (spec-paths app "/swagger.json")))
(is (= #{::swagger/default}
(-> {:request-method :get :uri "/swagger.json"}
@ -347,14 +347,14 @@
(deftest with-options-endpoint-test
(let [app (ring/ring-handler
(ring/router
[["/ping"
{:options (constantly "options")}]
["/pong"
(constantly "options")]
["/swagger.json"
{:get {:no-doc true
:handler (swagger/create-swagger-handler)}}]]))]
(ring/router
[["/ping"
{:options (constantly "options")}]
["/pong"
(constantly "options")]
["/swagger.json"
{:get {:no-doc true
:handler (swagger/create-swagger-handler)}}]]))]
(is (= ["/ping" "/pong"] (spec-paths app "/swagger.json")))
(is (= #{::swagger/default}
(-> {:request-method :get :uri "/swagger.json"}
@ -362,18 +362,18 @@
(deftest all-parameter-types-test
(let [app (ring/ring-handler
(ring/router
[["/parameters"
{:post {:coercion spec/coercion
:parameters {:query {:q string?}
:body {:b string?}
:form {:f string?}
:header {:h string?}
:path {:p string?}}
:handler identity}}]
["/swagger.json"
{:get {:no-doc true
:handler (swagger/create-swagger-handler)}}]]))
(ring/router
[["/parameters"
{:post {:coercion spec/coercion
:parameters {:query {:q string?}
:body {:b string?}
:form {:f string?}
:header {:h string?}
:path {:p string?}}
:handler identity}}]
["/swagger.json"
{:get {:no-doc true
:handler (swagger/create-swagger-handler)}}]]))
spec (:body (app {:request-method :get, :uri "/swagger.json"}))]
(is (= ["query" "body" "formData" "header" "path"]
(map :in (get-in spec [:paths "/parameters" :post :parameters]))))))