Merge branch 'master' into reloading-ring-handler

This commit is contained in:
Tommi Reiman 2023-01-22 14:30:06 +02:00 committed by GitHub
commit d1bb44a88f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 288 additions and 264 deletions

View file

@ -12,6 +12,29 @@ We use [Break Versioning][breakver]. The version numbers follow a `<major>.<mino
[breakver]: https://github.com/ptaoussanis/encore/blob/master/BREAK-VERSIONING.md [breakver]: https://github.com/ptaoussanis/encore/blob/master/BREAK-VERSIONING.md
## UNRELEASED
* Remove redundant s/and [#552](https://github.com/metosin/reitit/pull/552)
* FIX: redirect-trailing-slash-handler strips query-params [#565](https://github.com/metosin/reitit/issues/565)
* **BREAKING**: Drop tests for Clojure 1.9, run tests with 1.10 & 1.11
* NEW option `:meta-merge` on a router for custom merge strategy on route data
* Swagger: support operationId in generated swagger json [#452](https://github.com/metosin/reitit/pull/452) & [#569](https://github.com/metosin/reitit/pull/569)
* Update documentation and link to the startrek project [#578](https://github.com/metosin/reitit/pull/578)
* Upgrade jackson for CVE-2022-42003 and CVE-2022-42004 [#577](https://github.com/metosin/reitit/pull/577)
* Improved coercion errors perf [#576](https://github.com/metosin/reitit/pull/576)
* Add example for Reitit + Pedestal + Malli coercion [#572](https://github.com/metosin/reitit/pull/572)
* Handle empty seq as empty string in query-string [#566](https://github.com/metosin/reitit/pull/566)
* Polish pedestal chains when printing context diffs [#557](https://github.com/metosin/reitit/pull/557)
* Updated dependencies:
```clojure
[metosin/ring-swagger-ui "4.15.5"] is available but we use "4.3.0"
[metosin/jsonista "0.3.7"] is available but we use "0.3.5"
[metosin/malli "0.10.1"] is available but we use "0.8.2"
[fipp "0.6.26"] is available but we use "0.6.25"
[ring/ring-core "1.9.6"] is available but we use "1.9.5"
```
## 0.5.18 (2022-04-05) ## 0.5.18 (2022-04-05)
* FIX [#334](https://github.com/metosin/reitit/pull/334) - Frontend: there is no way to catch the exception if coercion fails (via [#549](https://github.com/metosin/reitit/pull/549)) * FIX [#334](https://github.com/metosin/reitit/pull/334) - Frontend: there is no way to catch the exception if coercion fails (via [#549](https://github.com/metosin/reitit/pull/549))

View file

@ -184,6 +184,6 @@ Roadmap is mostly written in [issues](https://github.com/metosin/reitit/issues).
## License ## License
Copyright © 2017-2021 [Metosin Oy](http://www.metosin.fi) Copyright © 2017-2023 [Metosin Oy](http://www.metosin.fi)
Distributed under the Eclipse Public License, the same as Clojure. Distributed under the Eclipse Public License, the same as Clojure.

View file

@ -2,18 +2,18 @@
Routers can be configured via options. The following options are available for the `reitit.core/router`: Routers can be configured via options. The following options are available for the `reitit.core/router`:
| key | description | key | description
|--------------|------------- |---------------|-------------
| `:path` | Base-path for routes | `:path` | Base-path for routes
| `:routes` | Initial resolved routes (default `[]`) | `:routes` | Initial resolved routes (default `[]`)
| `:data` | Initial route data (default `{}`) | `:data` | Initial route data (default `{}`)
| `:spec` | clojure.spec definition for a route data, see `reitit.spec` on how to use this | `:spec` | clojure.spec definition for a route data, see `reitit.spec` on how to use this
| `:syntax` | Path-parameter syntax as keyword or set of keywords (default #{:bracket :colon}) | `:syntax` | Path-parameter syntax as keyword or set of keywords (default #{:bracket :colon})
| `:expand` | Function of `arg opts => data` to expand route arg to route data (default `reitit.core/expand`) | `:expand` | Function of `arg opts => data` to expand route arg to route data (default `reitit.core/expand`)
| `:coerce` | Function of `route opts => route` to coerce resolved route, can throw or return `nil` | `:coerce` | Function of `route opts => route` to coerce resolved route, can throw or return `nil`
| `:meta-merge-fn` | Function which follows the signature of `meta-merge.core/meta-merge`, useful for when you want to have more control over the meta merging | `:meta-merge` | Function which follows the signature of `meta-merge.core/meta-merge`, useful for when you want to have more control over the meta merging
| `:compile` | Function of `route opts => result` to compile a route handler | `:compile` | Function of `route opts => result` to compile a route handler
| `:validate` | Function of `routes opts => ()` to validate route (data) via side-effects | `:validate` | Function of `routes opts => ()` to validate route (data) via side-effects
| `:conflicts` | Function of `{route #{route}} => ()` to handle conflicting routes | `:conflicts` | Function of `{route #{route}} => ()` to handle conflicting routes
| `:exception` | Function of `Exception => Exception ` to handle creation time exceptions (default `reitit.exception/exception`) | `:exception` | Function of `Exception => Exception ` to handle creation time exceptions (default `reitit.exception/exception`)
| `:router` | Function of `routes opts => router` to override the actual router implementation | `:router` | Function of `routes opts => router` to override the actual router implementation

View file

@ -60,11 +60,14 @@
(defn map-data [f routes] (defn map-data [f routes]
(mapv (fn [[p ds]] [p (f p ds)]) routes)) (mapv (fn [[p ds]] [p (f p ds)]) routes))
(defn merge-data [{:keys [meta-merge-fn] :as g} p x] (defn meta-merge [left right opts]
((or (:meta-merge opts) mm/meta-merge) left right))
(defn merge-data [opts p x]
(reduce (reduce
(fn [acc [k v]] (fn [acc [k v]]
(try (try
((or meta-merge-fn mm/meta-merge) acc {k v}) (meta-merge acc {k v} opts)
(catch #?(:clj Exception, :cljs js/Error) e (catch #?(:clj Exception, :cljs js/Error) e
(ex/fail! ::merge-data {:path p, :left acc, :right {k v}, :exception e})))) (ex/fail! ::merge-data {:path p, :left acc, :right {k v}, :exception e}))))
{} x)) {} x))

View file

@ -1,6 +1,5 @@
(ns reitit.interceptor (ns reitit.interceptor
(:require [clojure.pprint :as pprint] (:require [clojure.pprint :as pprint]
[meta-merge.core :refer [meta-merge]]
[reitit.core :as r] [reitit.core :as r]
[reitit.exception :as exception] [reitit.exception :as exception]
[reitit.impl :as impl])) [reitit.impl :as impl]))
@ -155,14 +154,14 @@
:handler get-user}]])" :handler get-user}]])"
([data] ([data]
(router data nil)) (router data nil))
([data {:keys [meta-merge-fn] :as opts}] ([data opts]
(let [opts ((or meta-merge-fn meta-merge) {:compile compile-result} opts)] (let [opts (impl/meta-merge {:compile compile-result} opts opts)]
(r/router data opts)))) (r/router data opts))))
(defn interceptor-handler [router] (defn interceptor-handler [router]
(with-meta (with-meta
(fn [path] (fn [path]
(some->> (r/match-by-path router path) (some->> (r/match-by-path router path)
:result :result
:interceptors)) :interceptors))
{::router router})) {::router router}))

View file

@ -1,6 +1,5 @@
(ns reitit.middleware (ns reitit.middleware
(:require [clojure.pprint :as pprint] (:require [clojure.pprint :as pprint]
[meta-merge.core :refer [meta-merge]]
[reitit.core :as r] [reitit.core :as r]
[reitit.exception :as exception] [reitit.exception :as exception]
[reitit.impl :as impl])) [reitit.impl :as impl]))
@ -138,15 +137,15 @@
:handler get-user}]])" :handler get-user}]])"
([data] ([data]
(router data nil)) (router data nil))
([data {:keys [meta-merge-fn] :as opts}] ([data opts]
(let [opts ((or meta-merge-fn meta-merge) {:compile compile-result} opts)] (let [opts (impl/meta-merge {:compile compile-result} opts opts)]
(r/router data opts)))) (r/router data opts))))
(defn middleware-handler [router] (defn middleware-handler [router]
(with-meta (with-meta
(fn [path] (fn [path]
(some->> path (some->> path
(r/match-by-path router) (r/match-by-path router)
:result :result
:handler)) :handler))
{::router router})) {::router router}))

View file

@ -176,10 +176,10 @@
(fn [_ [p n]] (fn [_ [p n]]
(if-let [cp (common-prefix p path)] (if-let [cp (common-prefix p path)]
(if (= cp p) (if (= cp p)
;; insert into child node ;; insert into child node
(let [n' (-insert n (conj ps (subs path (count p))) fp params data)] (let [n' (-insert n (conj ps (subs path (count p))) fp params data)]
(reduced (assoc-in node [:children p] n'))) (reduced (assoc-in node [:children p] n')))
;; split child node ;; split child node
(let [rp (subs p (count cp)) (let [rp (subs p (count cp))
rp' (subs path (count cp)) rp' (subs path (count cp))
n' (-insert (-node {}) ps fp params data) n' (-insert (-node {}) ps fp params data)
@ -189,7 +189,7 @@
(dissoc p) (dissoc p)
(assoc cp n''))))))))) (assoc cp n'')))))))))
nil (:children node)) nil (:children node))
;; new child 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 ""])] (if-let [child (get-in node' [:children ""])]
;; optimize by removing empty paths ;; optimize by removing empty paths
@ -385,62 +385,62 @@
;; ;;
(comment (comment
(-> (->
[["/v2/whoami" 1] [["/v2/whoami" 1]
["/v2/users/:user-id/datasets" 2] ["/v2/users/:user-id/datasets" 2]
["/v2/public/projects/:project-id/datasets" 3] ["/v2/public/projects/:project-id/datasets" 3]
["/v1/public/topics/:topic" 4] ["/v1/public/topics/:topic" 4]
["/v1/users/:user-id/orgs/:org-id" 5] ["/v1/users/:user-id/orgs/:org-id" 5]
["/v1/search/topics/:term" 6] ["/v1/search/topics/:term" 6]
["/v1/users/:user-id/invitations" 7] ["/v1/users/:user-id/invitations" 7]
["/v1/users/:user-id/topics" 9] ["/v1/users/:user-id/topics" 9]
["/v1/users/:user-id/bookmarks/followers" 10] ["/v1/users/:user-id/bookmarks/followers" 10]
["/v2/datasets/:dataset-id" 11] ["/v2/datasets/:dataset-id" 11]
["/v1/orgs/:org-id/usage-stats" 12] ["/v1/orgs/:org-id/usage-stats" 12]
["/v1/orgs/:org-id/devices/:client-id" 13] ["/v1/orgs/:org-id/devices/:client-id" 13]
["/v1/messages/user/:user-id" 14] ["/v1/messages/user/:user-id" 14]
["/v1/users/:user-id/devices" 15] ["/v1/users/:user-id/devices" 15]
["/v1/public/users/:user-id" 16] ["/v1/public/users/:user-id" 16]
["/v1/orgs/:org-id/errors" 17] ["/v1/orgs/:org-id/errors" 17]
["/v1/public/orgs/:org-id" 18] ["/v1/public/orgs/:org-id" 18]
["/v1/orgs/:org-id/invitations" 19] ["/v1/orgs/:org-id/invitations" 19]
["/v1/users/:user-id/device-errors" 22] ["/v1/users/:user-id/device-errors" 22]
["/v2/login" 23] ["/v2/login" 23]
["/v1/users/:user-id/usage-stats" 24] ["/v1/users/:user-id/usage-stats" 24]
["/v2/users/:user-id/devices" 25] ["/v2/users/:user-id/devices" 25]
["/v1/users/:user-id/claim-device/:client-id" 26] ["/v1/users/:user-id/claim-device/:client-id" 26]
["/v2/public/projects/:project-id" 27] ["/v2/public/projects/:project-id" 27]
["/v2/public/datasets/:dataset-id" 28] ["/v2/public/datasets/:dataset-id" 28]
["/v2/users/:user-id/topics/bulk" 29] ["/v2/users/:user-id/topics/bulk" 29]
["/v1/messages/device/:client-id" 30] ["/v1/messages/device/:client-id" 30]
["/v1/users/:user-id/owned-orgs" 31] ["/v1/users/:user-id/owned-orgs" 31]
["/v1/topics/:topic" 32] ["/v1/topics/:topic" 32]
["/v1/users/:user-id/bookmark/:topic" 33] ["/v1/users/:user-id/bookmark/:topic" 33]
["/v1/orgs/:org-id/members/:user-id" 34] ["/v1/orgs/:org-id/members/:user-id" 34]
["/v1/users/:user-id/devices/:client-id" 35] ["/v1/users/:user-id/devices/:client-id" 35]
["/v1/users/:user-id" 36] ["/v1/users/:user-id" 36]
["/v1/orgs/:org-id/devices" 37] ["/v1/orgs/:org-id/devices" 37]
["/v1/orgs/:org-id/members" 38] ["/v1/orgs/:org-id/members" 38]
["/v2/orgs/:org-id/topics" 40] ["/v2/orgs/:org-id/topics" 40]
["/v1/whoami" 41] ["/v1/whoami" 41]
["/v1/orgs/:org-id" 42] ["/v1/orgs/:org-id" 42]
["/v1/users/:user-id/api-key" 43] ["/v1/users/:user-id/api-key" 43]
["/v2/schemas" 44] ["/v2/schemas" 44]
["/v2/users/:user-id/topics" 45] ["/v2/users/:user-id/topics" 45]
["/v1/orgs/:org-id/confirm-membership/:token" 46] ["/v1/orgs/:org-id/confirm-membership/:token" 46]
["/v2/topics/:topic" 47] ["/v2/topics/:topic" 47]
["/v1/messages/topic/:topic" 48] ["/v1/messages/topic/:topic" 48]
["/v1/users/:user-id/devices/:client-id/reset-password" 49] ["/v1/users/:user-id/devices/:client-id/reset-password" 49]
["/v2/topics" 50] ["/v2/topics" 50]
["/v1/login" 51] ["/v1/login" 51]
["/v1/users/:user-id/orgs" 52] ["/v1/users/:user-id/orgs" 52]
["/v2/public/messages/dataset/:dataset-id" 53] ["/v2/public/messages/dataset/:dataset-id" 53]
["/v1/topics" 54] ["/v1/topics" 54]
["/v1/orgs" 55] ["/v1/orgs" 55]
["/v1/users/:user-id/bookmarks" 56] ["/v1/users/:user-id/bookmarks" 56]
["/v1/orgs/:org-id/topics" 57] ["/v1/orgs/:org-id/topics" 57]
["/command1 {arg1} {arg2}" ::cmd1] ["/command1 {arg1} {arg2}" ::cmd1]
["/command2 {arg1} {arg2} {arg3}" ::cmd2]] ["/command2 {arg1} {arg2} {arg3}" ::cmd2]]
(insert) (insert)
(compile) (compile)
(pretty))) (pretty)))

View file

@ -9,8 +9,7 @@
[fipp.engine] [fipp.engine]
[fipp.visit] [fipp.visit]
[reitit.exception :as exception] [reitit.exception :as exception]
[spell-spec.expound] ;; expound [spell-spec.expound])) ;; expound
))
;; ;;
;; colors ;; colors
@ -46,17 +45,17 @@
:error 196}) :error 196})
(comment (comment
(defn- -color [color & text] (defn- -color [color & text]
(str "\033[38;5;" (colors color color) "m" (apply str text) "\u001B[0m")) (str "\033[38;5;" (colors color color) "m" (apply str text) "\u001B[0m"))
(doseq [c (range 0 255)] (doseq [c (range 0 255)]
(println (-color c "kikka") "->" c)) (println (-color c "kikka") "->" c))
(doseq [[n c] colors] (doseq [[n c] colors]
(println (-color c "kikka") "->" c n)) (println (-color c "kikka") "->" c n))
(doseq [[k v] expound.ansi/sgr-code] (doseq [[k v] expound.ansi/sgr-code]
(println (expound.ansi/sgr "kikka" k) "->" k))) (println (expound.ansi/sgr "kikka" k) "->" k)))
(defn- -start [x] (str "\033[38;5;" x "m")) (defn- -start [x] (str "\033[38;5;" x "m"))
(defn- -end [] "\u001B[0m") (defn- -end [] "\u001B[0m")
@ -220,10 +219,10 @@
(defn exception [e] (defn exception [e]
(let [data (-> e ex-data :data) (let [data (-> e ex-data :data)
message (format-exception (-> e ex-data :type) #?(:clj (.getMessage ^Exception e) :cljs (ex-message e)) data) message (format-exception (-> e ex-data :type) #?(:clj (.getMessage ^Exception e) :cljs (ex-message e)) data)
source #?(:clj (->> e Throwable->map :trace source #?(:clj (->> e Throwable->map :trace
(drop-while #(not= (name (first %)) "reitit.core$router")) (drop-while #(not= (name (first %)) "reitit.core$router"))
(drop-while #(= (name (first %)) "reitit.core$router")) (drop-while #(= (name (first %)) "reitit.core$router"))
next first source-str) next first source-str)
:cljs "unknown")] :cljs "unknown")]
(ex-info (exception-str message source (printer)) (assoc (or data {}) ::exception/cause e)))) (ex-info (exception-str message source (printer)) (assoc (or data {}) ::exception/cause e))))

View file

@ -1,7 +1,7 @@
(ns reitit.http (ns reitit.http
(:require [meta-merge.core :refer [meta-merge]] (:require [reitit.core :as r]
[reitit.core :as r]
[reitit.exception :as ex] [reitit.exception :as ex]
[reitit.impl :as impl]
[reitit.interceptor :as interceptor] [reitit.interceptor :as interceptor]
[reitit.ring :as ring])) [reitit.ring :as ring]))
@ -14,7 +14,7 @@
(update acc method expand opts) (update acc method expand opts)
acc)) data ring/http-methods)]) acc)) data ring/http-methods)])
(defn compile-result [[path data] {:keys [::default-options-endpoint expand meta-merge-fn] :as opts}] (defn compile-result [[path data] {:keys [::default-options-endpoint expand] :as opts}]
(let [[top childs] (ring/group-keys data) (let [[top childs] (ring/group-keys data)
childs (cond-> childs childs (cond-> childs
(and (not (:options childs)) (not (:handler top)) default-options-endpoint) (and (not (:options childs)) (not (:handler top)) default-options-endpoint)
@ -38,7 +38,7 @@
(->methods true top) (->methods true top)
(reduce-kv (reduce-kv
(fn [acc method data] (fn [acc method data]
(let [data ((or meta-merge-fn meta-merge) top data)] (let [data (impl/meta-merge top data opts)]
(assoc acc method (->endpoint path data method method)))) (assoc acc method (->endpoint path data method method))))
(->methods (:handler top) data) (->methods (:handler top) data)
childs)))) childs))))
@ -138,35 +138,35 @@
enrich-request (ring/create-enrich-request inject-match? inject-router?) enrich-request (ring/create-enrich-request inject-match? inject-router?)
enrich-default-request (ring/create-enrich-default-request inject-router?)] enrich-default-request (ring/create-enrich-default-request inject-router?)]
(with-meta (with-meta
(fn (fn
([request] ([request]
(if-let [match (r/match-by-path router (:uri request))] (if-let [match (r/match-by-path router (:uri request))]
(let [method (:request-method request) (let [method (:request-method request)
path-params (:path-params match) path-params (:path-params match)
endpoint (-> match :result method) endpoint (-> match :result method)
interceptors (or (:queue endpoint) (:interceptors endpoint)) interceptors (or (:queue endpoint) (:interceptors endpoint))
request (enrich-request request path-params match router)] request (enrich-request request path-params match router)]
(or (interceptor/execute executor interceptors request) (or (interceptor/execute executor interceptors request)
(interceptor/execute executor default-queue request))) (interceptor/execute executor default-queue request)))
(interceptor/execute executor default-queue (enrich-default-request request router)))) (interceptor/execute executor default-queue (enrich-default-request request router))))
([request respond raise] ([request respond raise]
(let [default #(interceptor/execute executor default-queue % respond raise)] (let [default #(interceptor/execute executor default-queue % respond raise)]
(if-let [match (r/match-by-path router (:uri request))] (if-let [match (r/match-by-path router (:uri request))]
(let [method (:request-method request) (let [method (:request-method request)
path-params (:path-params match) path-params (:path-params match)
endpoint (-> match :result method) endpoint (-> match :result method)
interceptors (or (:queue endpoint) (:interceptors endpoint)) interceptors (or (:queue endpoint) (:interceptors endpoint))
request (enrich-request request path-params match router) request (enrich-request request path-params match router)
respond' (fn [response] respond' (fn [response]
(if response (if response
(respond response) (respond response)
(default request)))] (default request)))]
(if interceptors (if interceptors
(interceptor/execute executor interceptors request respond' raise) (interceptor/execute executor interceptors request respond' raise)
(default request))) (default request)))
(default (enrich-default-request request router)))) (default (enrich-default-request request router))))
nil)) nil))
{::r/router router})))) {::r/router router}))))
(defn get-router [handler] (defn get-router [handler]
(-> handler meta ::r/router)) (-> handler meta ::r/router))

View file

@ -140,7 +140,7 @@
show? (fn [key] (contains? error-keys key)) show? (fn [key] (contains? error-keys key))
transformers (walk/prewalk #(if (satisfies? TransformationProvider %) (-transformer % opts) %) transformers) transformers (walk/prewalk #(if (satisfies? TransformationProvider %) (-transformer % opts) %) transformers)
compile (if lite (fn [schema options] (compile (binding [l/*options* options] (l/schema schema)) options)) compile (if lite (fn [schema options] (compile (binding [l/*options* options] (l/schema schema)) options))
compile)] compile)]
^{:type ::coercion/coercion} ^{:type ::coercion/coercion}
(reify coercion/Coercion (reify coercion/Coercion
(-get-name [_] :malli) (-get-name [_] :malli)

View file

@ -1,8 +1,7 @@
(ns reitit.ring (ns reitit.ring
(:require [clojure.string :as str] (:require [clojure.string :as str]
[meta-merge.core :refer [meta-merge]]
#?@(:clj [[ring.util.mime-type :as mime-type] #?@(:clj [[ring.util.mime-type :as mime-type]
[ring.util.response :as response]]) [ring.util.response :as response]])
[reitit.core :as r] [reitit.core :as r]
[reitit.exception :as ex] [reitit.exception :as ex]
[reitit.impl :as impl] [reitit.impl :as impl]
@ -29,7 +28,7 @@
(update acc method expand opts) (update acc method expand opts)
acc)) data http-methods)]) acc)) data http-methods)])
(defn compile-result [[path data] {:keys [::default-options-endpoint expand meta-merge-fn] :as opts}] (defn compile-result [[path data] {:keys [::default-options-endpoint expand] :as opts}]
(let [[top childs] (group-keys data) (let [[top childs] (group-keys data)
childs (cond-> childs childs (cond-> childs
(and (not (:options childs)) (not (:handler top)) default-options-endpoint) (and (not (:options childs)) (not (:handler top)) default-options-endpoint)
@ -50,7 +49,7 @@
(->methods true top) (->methods true top)
(reduce-kv (reduce-kv
(fn [acc method data] (fn [acc method data]
(let [data ((or meta-merge-fn meta-merge) top data)] (let [data (impl/meta-merge top data opts)]
(assoc acc method (->endpoint path data method method)))) (assoc acc method (->endpoint path data method method))))
(->methods (:handler top) data) (->methods (:handler top) data)
childs)))) childs))))
@ -317,28 +316,28 @@
enrich-request (create-enrich-request inject-match? inject-router?) enrich-request (create-enrich-request inject-match? inject-router?)
enrich-default-request (create-enrich-default-request inject-router?)] enrich-default-request (create-enrich-default-request inject-router?)]
(with-meta (with-meta
(wrap (wrap
(fn (fn
([request] ([request]
(if-let [match (r/match-by-path router (:uri request))] (if-let [match (r/match-by-path router (:uri request))]
(let [method (:request-method request) (let [method (:request-method request)
path-params (:path-params match) path-params (:path-params match)
result (:result match) result (:result match)
handler (-> result method :handler (or default-handler)) handler (-> result method :handler (or default-handler))
request (enrich-request request path-params match router)] request (enrich-request request path-params match router)]
(or (handler request) (default-handler request))) (or (handler request) (default-handler request)))
(default-handler (enrich-default-request request router)))) (default-handler (enrich-default-request request router))))
([request respond raise] ([request respond raise]
(if-let [match (r/match-by-path router (:uri request))] (if-let [match (r/match-by-path router (:uri request))]
(let [method (:request-method request) (let [method (:request-method request)
path-params (:path-params match) path-params (:path-params match)
result (:result match) result (:result match)
handler (-> result method :handler (or default-handler)) handler (-> result method :handler (or default-handler))
request (enrich-request request path-params match router)] request (enrich-request request path-params match router)]
((routes handler default-handler) request respond raise)) ((routes handler default-handler) request respond raise))
(default-handler (enrich-default-request request router) respond raise)) (default-handler (enrich-default-request request router) respond raise))
nil))) nil)))
{::r/router router})))) {::r/router router}))))
(defn reloading-ring-handler (defn reloading-ring-handler
"Returns a ring-handler that recreates the actual ring-handler for each request. "Returns a ring-handler that recreates the actual ring-handler for each request.

View file

@ -47,7 +47,7 @@
(-get-name [_] :schema) (-get-name [_] :schema)
(-get-options [_] opts) (-get-options [_] opts)
(-get-apidocs [this specification {:keys [parameters responses]}] (-get-apidocs [this specification {:keys [parameters responses]}]
;; TODO: this looks identical to spec, refactor when schema is done. ;; TODO: this looks identical to spec, refactor when schema is done.
(case specification (case specification
:swagger (swagger/swagger-spec :swagger (swagger/swagger-spec
(merge (merge

View file

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

View file

@ -27,24 +27,24 @@
[metosin/reitit-frontend "0.5.18"] [metosin/reitit-frontend "0.5.18"]
[metosin/reitit-sieppari "0.5.18"] [metosin/reitit-sieppari "0.5.18"]
[metosin/reitit-pedestal "0.5.18"] [metosin/reitit-pedestal "0.5.18"]
[metosin/ring-swagger-ui "4.3.0"] [metosin/ring-swagger-ui "4.15.5"]
[metosin/spec-tools "0.10.5"] [metosin/spec-tools "0.10.5"]
[metosin/schema-tools "0.12.3"] [metosin/schema-tools "0.12.3"]
[metosin/muuntaja "0.6.8"] [metosin/muuntaja "0.6.8"]
[metosin/jsonista "0.3.5"] [metosin/jsonista "0.3.7"]
[metosin/sieppari "0.0.0-alpha13"] [metosin/sieppari "0.0.0-alpha13"]
[metosin/malli "0.8.2"] [metosin/malli "0.10.1"]
;; https://clojureverse.org/t/depending-on-the-right-versions-of-jackson-libraries/5111 ;; https://clojureverse.org/t/depending-on-the-right-versions-of-jackson-libraries/5111
[com.fasterxml.jackson.core/jackson-core "2.14.1"] [com.fasterxml.jackson.core/jackson-core "2.14.1"]
[com.fasterxml.jackson.core/jackson-databind "2.14.1"] [com.fasterxml.jackson.core/jackson-databind "2.14.1"]
[meta-merge "1.0.0"] [meta-merge "1.0.0"]
[fipp "0.6.25" :exclusions [org.clojure/core.rrb-vector]] [fipp "0.6.26" :exclusions [org.clojure/core.rrb-vector]]
[expound "0.9.0"] [expound "0.9.0"]
[lambdaisland/deep-diff "0.0-47"] [lambdaisland/deep-diff "0.0-47"]
[com.bhauman/spell-spec "0.1.2"] [com.bhauman/spell-spec "0.1.2"]
[ring/ring-core "1.9.5"] [ring/ring-core "1.9.6"]
[io.pedestal/pedestal.service "0.5.10"]] [io.pedestal/pedestal.service "0.5.10"]]
@ -77,7 +77,7 @@
:java-source-paths ["modules/reitit-core/java-src"] :java-source-paths ["modules/reitit-core/java-src"]
:dependencies [[org.clojure/clojure "1.10.2"] :dependencies [[org.clojure/clojure "1.11.1"]
[org.clojure/clojurescript "1.10.773"] [org.clojure/clojurescript "1.10.773"]
;; modules dependencies ;; modules dependencies
@ -85,55 +85,55 @@
[metosin/spec-tools "0.10.5"] [metosin/spec-tools "0.10.5"]
[metosin/muuntaja "0.6.8"] [metosin/muuntaja "0.6.8"]
[metosin/sieppari "0.0.0-alpha13"] [metosin/sieppari "0.0.0-alpha13"]
[metosin/jsonista "0.3.5"] [metosin/jsonista "0.3.7"]
[metosin/malli "0.8.2"] [metosin/malli "0.10.1"]
[lambdaisland/deep-diff "0.0-47"] [lambdaisland/deep-diff "0.0-47"]
[meta-merge "1.0.0"] [meta-merge "1.0.0"]
[com.bhauman/spell-spec "0.1.2"] [com.bhauman/spell-spec "0.1.2"]
[expound "0.9.0"] [expound "0.9.0"]
[fipp "0.6.25"] [fipp "0.6.26"]
[orchestra "2021.01.01-1"] [orchestra "2021.01.01-1"]
[ring "1.9.5"] [ring "1.9.6"]
[ikitommi/immutant-web "3.0.0-alpha1"] [ikitommi/immutant-web "3.0.0-alpha1"]
[metosin/ring-http-response "0.9.3"] [metosin/ring-http-response "0.9.3"]
[metosin/ring-swagger-ui "4.3.0"] [metosin/ring-swagger-ui "4.15.5"]
[criterium "0.4.6"] [criterium "0.4.6"]
[org.clojure/test.check "1.1.1"] [org.clojure/test.check "1.1.1"]
[org.clojure/tools.namespace "1.2.0"] [org.clojure/tools.namespace "1.3.0"]
[com.gfredericks/test.chuck "0.2.13"] [com.gfredericks/test.chuck "0.2.13"]
[io.pedestal/pedestal.service "0.5.10"] [io.pedestal/pedestal.service "0.5.10"]
[org.clojure/core.async "1.5.648"] [org.clojure/core.async "1.6.673"]
[manifold "0.2.3"] [manifold "0.3.0"]
[funcool/promesa "6.1.434"] [funcool/promesa "10.0.594"]
[com.clojure-goes-fast/clj-async-profiler "0.5.1"] [com.clojure-goes-fast/clj-async-profiler "1.0.3"]
[ring-cors "0.1.13"] [ring-cors "0.1.13"]
[com.bhauman/rebel-readline "0.1.4"]]} [com.bhauman/rebel-readline "0.1.4"]]}
:1.9 {:dependencies [[org.clojure/clojure "1.9.0"]]} :1.10 {:dependencies [[org.clojure/clojure "1.10.3"]]}
:perf {:jvm-opts ^:replace ["-server" :perf {:jvm-opts ^:replace ["-server"
"-Xmx4096m" "-Xmx4096m"
"-Dclojure.compiler.direct-linking=true"] "-Dclojure.compiler.direct-linking=true"]
:test-paths ["perf-test/clj"] :test-paths ["perf-test/clj"]
:dependencies [[compojure "1.6.2"] :dependencies [[compojure "1.7.0"]
[ring/ring-defaults "0.3.3"] [ring/ring-defaults "0.3.4"]
[ikitommi/immutant-web "3.0.0-alpha1"] [ikitommi/immutant-web "3.0.0-alpha1"]
[io.pedestal/pedestal.service "0.5.10"] [io.pedestal/pedestal.service "0.5.10"]
[io.pedestal/pedestal.jetty "0.5.10"] [io.pedestal/pedestal.jetty "0.5.10"]
[calfpath "0.8.1"] [calfpath "0.8.1"]
[org.clojure/core.async "1.5.648"] [org.clojure/core.async "1.6.673"]
[manifold "0.2.3"] [manifold "0.3.0"]
[funcool/promesa "6.1.434"] [funcool/promesa "10.0.594"]
[metosin/sieppari] [metosin/sieppari]
[yada "1.2.16"] [yada "1.2.16"]
[aleph "0.4.6"] [aleph "0.6.0"]
[ring/ring-defaults "0.3.3"] [ring/ring-defaults "0.3.4"]
[ataraxy "0.4.2"] [ataraxy "0.4.3"]
[bidi "2.1.6"] [bidi "2.1.6"]
[janus "1.3.2"]]} [janus "1.3.2"]]}
:analyze {:jvm-opts ^:replace ["-server" :analyze {:jvm-opts ^:replace ["-server"
@ -141,7 +141,7 @@
"-XX:+PrintCompilation" "-XX:+PrintCompilation"
"-XX:+UnlockDiagnosticVMOptions" "-XX:+UnlockDiagnosticVMOptions"
"-XX:+PrintInlining"]}} "-XX:+PrintInlining"]}}
:aliases {"all" ["with-profile" "dev,default:dev,default,1.9"] :aliases {"all" ["with-profile" "dev,default:dev,default,1.10"]
"perf" ["with-profile" "default,dev,perf"] "perf" ["with-profile" "default,dev,perf"]
"test-clj" ["all" "do" ["bat-test"] ["check"]] "test-clj" ["all" "do" ["bat-test"] ["check"]]
"test-browser" ["doo" "chrome-headless" "test"] "test-browser" ["doo" "chrome-headless" "test"]

View file

@ -30,4 +30,4 @@
(spit "doc/cljdoc.edn" (with-out-str (pprint/pprint data))))) (spit "doc/cljdoc.edn" (with-out-str (pprint/pprint data)))))
(comment (comment
(reap!)) (reap!))

View file

@ -1,6 +1,6 @@
(ns reitit.exception-test (ns reitit.exception-test
(:require [clojure.spec.alpha :as s] (:require [clojure.spec.alpha :as s]
[clojure.test :refer [are deftest is testing]] [clojure.test :refer [are deftest is]]
[reitit.core :as r] [reitit.core :as r]
[reitit.dev.pretty :as pretty] [reitit.dev.pretty :as pretty]
[reitit.exception :as exception] [reitit.exception :as exception]

View file

@ -1,5 +1,5 @@
(ns reitit.impl-test (ns reitit.impl-test
(:require [clojure.test :refer [are deftest is testing]] (:require [clojure.test :refer [are deftest is]]
[reitit.impl :as impl])) [reitit.impl :as impl]))
(deftest strip-nils-test (deftest strip-nils-test

View file

@ -1,5 +1,5 @@
(ns reitit.interceptor-test (ns reitit.interceptor-test
(:require [clojure.test :refer [are deftest is testing]] (:require [clojure.test :refer [deftest is testing]]
[reitit.core :as r] [reitit.core :as r]
[reitit.interceptor :as interceptor]) [reitit.interceptor :as interceptor])
#?(:clj #?(:clj

View file

@ -1,5 +1,5 @@
(ns reitit.middleware-test (ns reitit.middleware-test
(:require [clojure.test :refer [are deftest is testing]] (:require [clojure.test :refer [deftest is testing]]
[reitit.core :as r] [reitit.core :as r]
[reitit.middleware :as middleware]) [reitit.middleware :as middleware])
#?(:clj #?(:clj

View file

@ -2,7 +2,7 @@
(:require [clojure.test :refer [deftest is testing]] (:require [clojure.test :refer [deftest is testing]]
[malli.experimental.lite :as l] [malli.experimental.lite :as l]
#?@(:clj [[muuntaja.middleware] #?@(:clj [[muuntaja.middleware]
[jsonista.core :as j]]) [jsonista.core :as j]])
[malli.core :as m] [malli.core :as m]
[malli.util :as mu] [malli.util :as mu]
[meta-merge.core :refer [meta-merge]] [meta-merge.core :refer [meta-merge]]
@ -562,7 +562,7 @@
(is (= {:status 200, :body {:total +4}} (call "application/edn" [:int {:encode/json -}])))))) (is (= {:status 200, :body {:total +4}} (call "application/edn" [:int {:encode/json -}]))))))
(testing "using custom meta-merge function" (testing "using custom meta-merge function"
(let [->app (fn [schema-fn meta-merge-fn] (let [->app (fn [schema-fn meta-merge]
(ring/ring-handler (ring/ring-handler
(ring/router (ring/router
["/merging-params/:foo" {:parameters {:path (schema-fn [:map [:foo :string]])}} ["/merging-params/:foo" {:parameters {:path (schema-fn [:map [:foo :string]])}}
@ -574,10 +574,10 @@
{:data {:middleware [rrc/coerce-request-middleware {:data {:middleware [rrc/coerce-request-middleware
rrc/coerce-response-middleware] rrc/coerce-response-middleware]
:coercion malli/coercion} :coercion malli/coercion}
:meta-merge-fn meta-merge-fn}))) :meta-merge meta-merge})))
call (fn [schema-fn meta-merge-fn] call (fn [schema-fn meta-merge]
((->app schema-fn meta-merge-fn) {:uri "/merging-params/this/that" ((->app schema-fn meta-merge) {:uri "/merging-params/this/that"
:request-method :get}))] :request-method :get}))]
(is (= {:status 200, :body {:total "FOO: this, BAR: that"}} (call m/schema custom-meta-merge-checking-schema))) (is (= {:status 200, :body {:total "FOO: this, BAR: that"}} (call m/schema custom-meta-merge-checking-schema)))
(is (= {:status 200, :body {:total "FOO: this, BAR: that"}} (call identity custom-meta-merge-checking-parameters))))))) (is (= {:status 200, :body {:total "FOO: this, BAR: that"}} (call identity custom-meta-merge-checking-parameters)))))))

View file

@ -83,7 +83,7 @@
ExceptionInfo ExceptionInfo
#"Invalid route data" #"Invalid route data"
(ring/router (ring/router
["/api" {:handler identity ["/api" {:handler identity
:middleware '()}] :middleware '()}]
{:validate rrs/validate}))))) {:validate rrs/validate})))))

View file

@ -39,7 +39,7 @@
(are [data] (are [data]
(is (thrown-with-msg? (is (thrown-with-msg?
ExceptionInfo ExceptionInfo
#"Call to #'reitit.core/router did not conform to spec" #"Call to (#')*reitit.core/router did not conform to spec"
(r/router (r/router
data))) data)))
@ -69,7 +69,7 @@
(are [opts] (are [opts]
(is (thrown-with-msg? (is (thrown-with-msg?
ExceptionInfo ExceptionInfo
#"Call to #'reitit.core/router did not conform to spec" #"Call to (#')*reitit.core/router did not conform to spec"
(r/router (r/router
["/api"] opts))) ["/api"] opts)))

View file

@ -22,35 +22,35 @@
:swagger {:info {:title "my-api"}} :swagger {:info {:title "my-api"}}
:handler (swagger/create-swagger-handler)}}] :handler (swagger/create-swagger-handler)}}]
["/spec" {:coercion spec/coercion} ["/spec" {:coercion spec/coercion}
["/plus/:z" ["/plus/:z"
{:patch {:summary "patch" {:patch {:summary "patch"
:operationId "Patch" :operationId "Patch"
:handler (constantly {:status 200})}
:options {:summary "options"
:middleware [{:data {:swagger {:responses {200 {:description "200"}}}}}]
:handler (constantly {:status 200})} :handler (constantly {:status 200})}
:options {:summary "options" :get {:summary "plus"
:middleware [{:data {:swagger {:responses {200 {:description "200"}}}}}] :operationId "GetPlus"
:handler (constantly {:status 200})} :parameters {:query {:x int?, :y int?}
:get {:summary "plus" :path {:z int?}}
:operationId "GetPlus" :swagger {:responses {400 {:schema {:type "string"}
:parameters {:query {:x int?, :y int?} :description "kosh"}}}
:path {:z int?}} :responses {200 {:body {:total int?}}
:swagger {:responses {400 {:schema {:type "string"} 500 {:description "fail"}}
:description "kosh"}}} :handler (fn [{{{:keys [x y]} :query
:responses {200 {:body {:total int?}} {:keys [z]} :path} :parameters}]
500 {:description "fail"}} {:status 200, :body {:total (+ x y z)}})}
:handler (fn [{{{:keys [x y]} :query :post {:summary "plus with body"
{:keys [z]} :path} :parameters}] :parameters {:body (ds/maybe [int?])
{:status 200, :body {:total (+ x y z)}})} :path {:z int?}}
:post {:summary "plus with body" :swagger {:responses {400 {:schema {:type "string"}
:parameters {:body (ds/maybe [int?]) :description "kosh"}}}
:path {:z int?}} :responses {200 {:body {:total int?}}
:swagger {:responses {400 {:schema {:type "string"} 500 {:description "fail"}}
:description "kosh"}}} :handler (fn [{{{:keys [z]} :path
:responses {200 {:body {:total int?}} xs :body} :parameters}]
500 {:description "fail"}} {:status 200, :body {:total (+ (reduce + xs) z)}})}}]]
:handler (fn [{{{:keys [z]} :path
xs :body} :parameters}]
{:status 200, :body {:total (+ (reduce + xs) z)}})}}]]
["/malli" {:coercion malli/coercion} ["/malli" {:coercion malli/coercion}
["/plus/*z" ["/plus/*z"
@ -206,6 +206,7 @@
:responses {200 {:schema {:type "object" :responses {200 {:schema {:type "object"
:properties {:total {:format "int64" :properties {:total {:format "int64"
:type "integer"}} :type "integer"}}
:additionalProperties false
:required [:total]} :required [:total]}
:description ""} :description ""}
400 {:schema {:type "string"} 400 {:schema {:type "string"}
@ -229,6 +230,7 @@
:responses {200 {:description "" :responses {200 {:description ""
:schema {:properties {:total {:format "int64" :schema {:properties {:total {:format "int64"
:type "integer"}} :type "integer"}}
:additionalProperties false
:required [:total] :required [:total]
:type "object"}} :type "object"}}
400 {:schema {:type "string"} 400 {:schema {:type "string"}

View file

@ -10,11 +10,11 @@
(let [log (atom []) (let [log (atom [])
controller-state (atom []) controller-state (atom [])
controller-1 {:start (fn [_] (swap! log conj :start-1)) controller-1 {:start (fn [_] (swap! log conj :start-1))
:stop (fn [_] (swap! log conj :stop-1))} :stop (fn [_] (swap! log conj :stop-1))}
controller-2 {:start (fn [_] (swap! log conj :start-2)) controller-2 {:start (fn [_] (swap! log conj :start-2))
:stop (fn [_] (swap! log conj :stop-2))} :stop (fn [_] (swap! log conj :stop-2))}
controller-3 {:start (fn [{:keys [foo]}] (swap! log conj [:start-3 foo])) controller-3 {:start (fn [{:keys [foo]}] (swap! log conj [:start-3 foo]))
:stop (fn [{:keys [foo]}] (swap! log conj [:stop-3 foo])) :stop (fn [{:keys [foo]}] (swap! log conj [:stop-3 foo]))
:identity (fn [match] :identity (fn [match]
{:foo (-> match :parameters :path :foo)})}] {:foo (-> match :parameters :path :foo)})}]
@ -70,9 +70,9 @@
(let [log (atom []) (let [log (atom [])
controller-state (atom []) controller-state (atom [])
static {:start (fn [params] (swap! log conj [:start-static])) static {:start (fn [params] (swap! log conj [:start-static]))
:stop (fn [params] (swap! log conj [:stop-static]))} :stop (fn [params] (swap! log conj [:stop-static]))}
controller {:start (fn [params] (swap! log conj [:start params])) controller {:start (fn [params] (swap! log conj [:start params]))
:stop (fn [params] (swap! log conj [:stop params])) :stop (fn [params] (swap! log conj [:stop params]))
:parameters {:path [:foo]}}] :parameters {:path [:foo]}}]
(testing "init" (testing "init"

View file

@ -56,9 +56,9 @@
(is (= [{:type :warn (is (= [{:type :warn
:message ["missing route" ::asd]}] :message ["missing route" ::asd]}]
(:messages (:messages
(capture-console (capture-console
(fn [] (fn []
(rf/match-by-name! router ::asd))))))))) (rf/match-by-name! router ::asd)))))))))
(testing "schema coercion" (testing "schema coercion"
(let [router (r/router ["/" (let [router (r/router ["/"
@ -124,6 +124,6 @@
:required #{:id} :required #{:id}
:path-params {}}]}] :path-params {}}]}]
(:messages (:messages
(capture-console (capture-console
(fn [] (fn []
(rf/match-by-name! router ::foo {})))))))))) (rf/match-by-name! router ::foo {}))))))))))

View file

@ -27,8 +27,8 @@
(is (= "#/bar/5?q=x" (is (= "#/bar/5?q=x"
(rfh/href history ::bar {:id 5} {:q "x"}))) (rfh/href history ::bar {:id 5} {:q "x"})))
(let [{:keys [value messages]} (capture-console (let [{:keys [value messages]} (capture-console
(fn [] (fn []
(rfh/href history ::asd)))] (rfh/href history ::asd)))]
(is (= nil value)) (is (= nil value))
(is (= [{:type :warn (is (= [{:type :warn
:message ["missing route" ::asd]}] :message ["missing route" ::asd]}]
@ -84,8 +84,8 @@
(is (= "/bar/5?q=x" (is (= "/bar/5?q=x"
(rfh/href history ::bar {:id 5} {:q "x"}))) (rfh/href history ::bar {:id 5} {:q "x"})))
(let [{:keys [value messages]} (capture-console (let [{:keys [value messages]} (capture-console
(fn [] (fn []
(rfh/href history ::asd)))] (rfh/href history ::asd)))]
(is (= nil value)) (is (= nil value))
(is (= [{:type :warn (is (= [{:type :warn
:message ["missing route" ::asd]}] :message ["missing route" ::asd]}]
@ -153,7 +153,7 @@
(done))))) (done)))))
{:use-fragment false}) {:use-fragment false})
create-link #(doto create-link #(doto
(js/document.createElement "A") (js/document.createElement "A")
(.setAttribute "href" (rfh/href history ::foo))) (.setAttribute "href" (rfh/href history ::foo)))
document-link (create-link) document-link (create-link)
shadow-link (create-link)] shadow-link (create-link)]

View file

@ -10,6 +10,6 @@
(set! js/console.warn (partial log :warn)) (set! js/console.warn (partial log :warn))
(f) (f)
(finally (finally
(set! js/console.warn original-console-warn)))] (set! js/console.warn original-console-warn)))]
{:value value {:value value
:messages @messages})) :messages @messages}))