From e095cd2efa54ad270ca0831a9d16aa5e1299ab51 Mon Sep 17 00:00:00 2001 From: Timo Kramer Date: Fri, 13 Nov 2020 11:22:07 +0100 Subject: [PATCH 01/33] Support operationId in reitit-swagger OpenAPI Specification allows the operationId to be added to the "Operation Object" alongside e.g. summary and description. This commit introduces the support of this element in the reitit-swagger module and extends the tests. One test shows the correct use of operationId where both are distinct and one shows the failing of the swagger creation when the IDs are not distinct. - Spec: https://swagger.io/specification/#operation-object - Adds the support for operationId - Adds operationId in two places of the swagger test - Adds a test that checks exception on duplicate IDs - Closes #451 --- .../reitit-swagger/src/reitit/swagger.cljc | 17 +++++++-- test/cljc/reitit/swagger_test.clj | 38 ++++++++++++++++++- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/modules/reitit-swagger/src/reitit/swagger.cljc b/modules/reitit-swagger/src/reitit/swagger.cljc index 998457ac..cc6f4e8e 100644 --- a/modules/reitit-swagger/src/reitit/swagger.cljc +++ b/modules/reitit-swagger/src/reitit/swagger.cljc @@ -12,9 +12,11 @@ (s/def ::tags (s/coll-of (s/or :keyword keyword? :string string?) :kind #{})) (s/def ::summary string?) (s/def ::description string?) +(s/def ::operationId string?) +(s/def ::operationIds (s/coll-of ::operationId :distinct true)) (s/def ::swagger (s/keys :opt-un [::id])) -(s/def ::spec (s/keys :opt-un [::swagger ::no-doc ::tags ::summary ::description])) +(s/def ::spec (s/keys :opt-un [::swagger ::no-doc ::tags ::summary ::description ::operationId])) (def swagger-feature "Feature for handling swagger-documentation for routes. @@ -75,13 +77,14 @@ (let [{:keys [id] :or {id ::default} :as swagger} (-> match :result request-method :data :swagger) ids (trie/into-set id) strip-top-level-keys #(dissoc % :id :info :host :basePath :definitions :securityDefinitions) - strip-endpoint-keys #(dissoc % :id :parameters :responses :summary :description) + strip-endpoint-keys #(dissoc % :id :parameters :responses :summary :description :operationId) swagger (->> (strip-endpoint-keys swagger) (merge {:swagger "2.0" :x-id ids})) accept-route (fn [route] (-> route second :swagger :id (or ::default) (trie/into-set) (set/intersection ids) seq)) base-swagger-spec {:responses ^:displace {:default {:description ""}}} + oid-acc (atom []) transform-endpoint (fn [[method {{:keys [coercion no-doc swagger] :as data} :data middleware :middleware interceptors :interceptors}]] @@ -94,12 +97,20 @@ (if coercion (coercion/get-apidocs coercion :swagger data)) (select-keys data [:tags :summary :description]) + (let [oid (select-keys data [:operationId]) + oid-val (:operationId oid) + _ (when (not (nil? oid-val)) + (reset! oid-acc (conj @oid-acc oid-val)))] + oid) (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])) map-in-order #(->> % (apply concat) (apply array-map)) - paths (->> router (r/compiled-routes) (filter accept-route) (map transform-path) map-in-order)] + paths (->> router (r/compiled-routes) (filter accept-route) (map transform-path) map-in-order) + _ (when (not (s/valid? ::operationIds @oid-acc)) + (throw (ex-info (s/explain-str ::operationIds @oid-acc) {:operation-ids @oid-acc + :error "operationIds are not distinct"})))] {:status 200 :body (meta-merge swagger {:paths paths})})) ([req res raise] diff --git a/test/cljc/reitit/swagger_test.clj b/test/cljc/reitit/swagger_test.clj index b87c695c..4adafdc9 100644 --- a/test/cljc/reitit/swagger_test.clj +++ b/test/cljc/reitit/swagger_test.clj @@ -1,5 +1,5 @@ (ns reitit.swagger-test - (:require [clojure.test :refer [deftest is testing]] + (:require [clojure.test :refer :all] [reitit.ring :as ring] [reitit.swagger :as swagger] [reitit.swagger-ui :as swagger-ui] @@ -25,11 +25,13 @@ ["/spec" {:coercion spec/coercion} ["/plus/:z" {:patch {:summary "patch" + :operationId "Patch" :handler (constantly {:status 200})} :options {:summary "options" :middleware [{:data {:swagger {:responses {200 {:description "200"}}}}}] :handler (constantly {:status 200})} :get {:summary "plus" + :operationId "GetPlus" :parameters {:query {:x int?, :y int?} :path {:z int?}} :swagger {:responses {400 {:schema {:type "string"} @@ -101,6 +103,32 @@ rrc/coerce-request-middleware rrc/coerce-response-middleware]}}))) +(def failing-app + (ring/ring-handler + (ring/router + ["/api" + {:swagger {:id ::math}} + + ["/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" + :operationId "Patch" + :handler (constantly {:status 200})} + :options {:summary "options" + :operationId "Patch" + :middleware [{:data {:swagger {:responses {200 {:description "200"}}}}}] + :handler (constantly {:status 200})}}]]] + + {:data {:middleware [swagger/swagger-feature + rrc/coerce-exceptions-middleware + rrc/coerce-request-middleware + rrc/coerce-response-middleware]}}))) + (require '[fipp.edn]) (deftest swagger-test (testing "endpoints work" @@ -118,6 +146,10 @@ (app {:request-method :get :uri "/api/schema/plus/3" :query-params {:x "2", :y "1"}}))))) + + (testing "failing swagger-spec" + (is (thrown? clojure.lang.ExceptionInfo (:body (failing-app {:request-method :get + :uri "/api/swagger.json"}))))) (testing "swagger-spec" (let [spec (:body (app {:request-method :get :uri "/api/swagger.json"})) @@ -126,6 +158,7 @@ :info {:title "my-api"} :paths {"/api/spec/plus/{z}" {:patch {:parameters [] :summary "patch" + :operationId "Patch" :responses {:default {:description ""}}} :options {:parameters [] :summary "options" @@ -156,7 +189,8 @@ 400 {:schema {:type "string"} :description "kosh"} 500 {:description "fail"}} - :summary "plus"} + :summary "plus" + :operationId "GetPlus"} :post {:parameters [{:in "body", :name "body", :description "", From 1b583c1cc2f87ce958dd32cf60f6dbc3ee11c888 Mon Sep 17 00:00:00 2001 From: Miikka Koskinen Date: Fri, 23 Apr 2021 17:02:17 +0300 Subject: [PATCH 02/33] Remove operation-id uniqueness check Let's leave that for other tools for now. --- .../reitit-swagger/src/reitit/swagger.cljc | 14 ++------- test/cljc/reitit/swagger_test.clj | 29 ------------------- 2 files changed, 2 insertions(+), 41 deletions(-) diff --git a/modules/reitit-swagger/src/reitit/swagger.cljc b/modules/reitit-swagger/src/reitit/swagger.cljc index cc6f4e8e..4a2a5319 100644 --- a/modules/reitit-swagger/src/reitit/swagger.cljc +++ b/modules/reitit-swagger/src/reitit/swagger.cljc @@ -13,7 +13,6 @@ (s/def ::summary string?) (s/def ::description string?) (s/def ::operationId string?) -(s/def ::operationIds (s/coll-of ::operationId :distinct true)) (s/def ::swagger (s/keys :opt-un [::id])) (s/def ::spec (s/keys :opt-un [::swagger ::no-doc ::tags ::summary ::description ::operationId])) @@ -84,7 +83,6 @@ accept-route (fn [route] (-> route second :swagger :id (or ::default) (trie/into-set) (set/intersection ids) seq)) base-swagger-spec {:responses ^:displace {:default {:description ""}}} - oid-acc (atom []) transform-endpoint (fn [[method {{:keys [coercion no-doc swagger] :as data} :data middleware :middleware interceptors :interceptors}]] @@ -96,21 +94,13 @@ (apply meta-merge (keep (comp :swagger :data) interceptors)) (if coercion (coercion/get-apidocs coercion :swagger data)) - (select-keys data [:tags :summary :description]) - (let [oid (select-keys data [:operationId]) - oid-val (:operationId oid) - _ (when (not (nil? oid-val)) - (reset! oid-acc (conj @oid-acc oid-val)))] - oid) + (select-keys data [:tags :summary :description :operationId]) (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])) map-in-order #(->> % (apply concat) (apply array-map)) - paths (->> router (r/compiled-routes) (filter accept-route) (map transform-path) map-in-order) - _ (when (not (s/valid? ::operationIds @oid-acc)) - (throw (ex-info (s/explain-str ::operationIds @oid-acc) {:operation-ids @oid-acc - :error "operationIds are not distinct"})))] + paths (->> router (r/compiled-routes) (filter accept-route) (map transform-path) map-in-order)] {:status 200 :body (meta-merge swagger {:paths paths})})) ([req res raise] diff --git a/test/cljc/reitit/swagger_test.clj b/test/cljc/reitit/swagger_test.clj index 4adafdc9..9c1ef38a 100644 --- a/test/cljc/reitit/swagger_test.clj +++ b/test/cljc/reitit/swagger_test.clj @@ -103,32 +103,6 @@ rrc/coerce-request-middleware rrc/coerce-response-middleware]}}))) -(def failing-app - (ring/ring-handler - (ring/router - ["/api" - {:swagger {:id ::math}} - - ["/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" - :operationId "Patch" - :handler (constantly {:status 200})} - :options {:summary "options" - :operationId "Patch" - :middleware [{:data {:swagger {:responses {200 {:description "200"}}}}}] - :handler (constantly {:status 200})}}]]] - - {:data {:middleware [swagger/swagger-feature - rrc/coerce-exceptions-middleware - rrc/coerce-request-middleware - rrc/coerce-response-middleware]}}))) - (require '[fipp.edn]) (deftest swagger-test (testing "endpoints work" @@ -147,9 +121,6 @@ :uri "/api/schema/plus/3" :query-params {:x "2", :y "1"}}))))) - (testing "failing swagger-spec" - (is (thrown? clojure.lang.ExceptionInfo (:body (failing-app {:request-method :get - :uri "/api/swagger.json"}))))) (testing "swagger-spec" (let [spec (:body (app {:request-method :get :uri "/api/swagger.json"})) From 1b74373911fb2e2b314f6e4128431e7a4e6b29c3 Mon Sep 17 00:00:00 2001 From: Miikka Koskinen Date: Fri, 23 Apr 2021 17:15:35 +0300 Subject: [PATCH 03/33] Use explicit :refers --- test/cljc/reitit/swagger_test.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cljc/reitit/swagger_test.clj b/test/cljc/reitit/swagger_test.clj index 9c1ef38a..c141a489 100644 --- a/test/cljc/reitit/swagger_test.clj +++ b/test/cljc/reitit/swagger_test.clj @@ -1,5 +1,5 @@ (ns reitit.swagger-test - (:require [clojure.test :refer :all] + (:require [clojure.test :refer [deftest is testing]] [reitit.ring :as ring] [reitit.swagger :as swagger] [reitit.swagger-ui :as swagger-ui] From 38ec679207081d49df7d6995b2e3b1bf3a85c1af Mon Sep 17 00:00:00 2001 From: Timo Kramer Date: Sat, 29 May 2021 11:48:57 +0200 Subject: [PATCH 04/33] Extend the docs to mention the operationID --- doc/ring/swagger.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/ring/swagger.md b/doc/ring/swagger.md index 69006a23..d5767445 100644 --- a/doc/ring/swagger.md +++ b/doc/ring/swagger.md @@ -23,6 +23,7 @@ The following route data keys contribute to the generated swagger specification: | :tags | optional set of string or keyword tags for an endpoint api docs | :summary | optional short string summary of an endpoint | :description | optional long description of an endpoint. Supports http://spec.commonmark.org/ +| :operationId | optional string specifying the unique ID of an Operation Coercion keys also contribute to the docs: From aeab5b96a6d1fe187e6e9edb74e16c123fb19ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Vaeng=20R=C3=B8tnes?= Date: Mon, 9 May 2022 10:36:40 +0000 Subject: [PATCH 05/33] Remove redundant s/and --- modules/reitit-core/src/reitit/spec.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/reitit-core/src/reitit/spec.cljc b/modules/reitit-core/src/reitit/spec.cljc index 6715eae3..62595b2b 100644 --- a/modules/reitit-core/src/reitit/spec.cljc +++ b/modules/reitit-core/src/reitit/spec.cljc @@ -18,7 +18,7 @@ (s/nilable (s/cat :path ::path :arg (s/? ::arg) - :childs (s/* (s/and (s/nilable ::raw-routes)))))) + :childs (s/* (s/nilable ::raw-routes))))) (s/def ::raw-routes (s/or :route ::raw-route From 25a051b00319fc175e4fb2dd13b9f001714a1467 Mon Sep 17 00:00:00 2001 From: Ilshat Sultanov Date: Wed, 15 Jun 2022 22:49:13 +0500 Subject: [PATCH 06/33] Add support for fragment parameters in the reitit-frontend module We have to process the fragment parameters due to the fact that the authorization server returns a callback in the following format: `https://example.com/oauth/google/callback#access_token=foo&refresh_token=bar&provider_token=baz&token_type=bearer&expires_in=3600` Links: - https://www.rfc-editor.org/rfc/rfc6749#section-4.2 - https://www.rfc-editor.org/rfc/rfc6749#section-4.2.2 --- modules/reitit-core/src/reitit/coercion.cljc | 3 +- .../reitit-frontend/src/reitit/frontend.cljs | 20 ++++++++- test/cljs/reitit/frontend/core_test.cljs | 43 +++++++++++++++---- 3 files changed, 54 insertions(+), 12 deletions(-) diff --git a/modules/reitit-core/src/reitit/coercion.cljc b/modules/reitit-core/src/reitit/coercion.cljc index 0fd5d234..f7a83a87 100644 --- a/modules/reitit-core/src/reitit/coercion.cljc +++ b/modules/reitit-core/src/reitit/coercion.cljc @@ -39,7 +39,8 @@ :body (->ParameterCoercion :body-params :body false false) :form (->ParameterCoercion :form-params :string true true) :header (->ParameterCoercion :headers :string true true) - :path (->ParameterCoercion :path-params :string true true)}) + :path (->ParameterCoercion :path-params :string true true) + :fragment (->ParameterCoercion :fragment-params :string true true)}) (defn ^:no-doc request-coercion-failed! [result coercion value in request] (throw diff --git a/modules/reitit-frontend/src/reitit/frontend.cljs b/modules/reitit-frontend/src/reitit/frontend.cljs index d1e9c50a..922b5a8b 100644 --- a/modules/reitit-frontend/src/reitit/frontend.cljs +++ b/modules/reitit-frontend/src/reitit/frontend.cljs @@ -1,5 +1,6 @@ (ns reitit.frontend (:require [clojure.set :as set] + [clojure.string :as str] [reitit.coercion :as coercion] [reitit.core :as r]) (:import goog.Uri @@ -20,6 +21,19 @@ (map (juxt keyword #(query-param q %))) (into {})))) +(defn fragment-params + "Given goog.Uri, read fragment parameters into Clojure map." + [^Uri uri] + (let [fp (.getFragment uri)] + (if-not (seq fp) + {} + (into {} + (comp + (map #(str/split % #"=")) + (map (fn [[k v]] + [(keyword k) v]))) + (str/split fp #"&"))))) + (defn match-by-path "Given routing tree and current path, return match with possibly coerced parameters. Return nil if no match found. @@ -37,12 +51,14 @@ coercion/coerce!)] (if-let [match (r/match-by-path router (.getPath uri))] (let [q (query-params uri) - match (assoc match :query-params q) + fp (fragment-params uri) + match (assoc match :query-params q :fragment-params fp) ;; Return uncoerced values if coercion is not enabled - so ;; that tha parameters are always accessible from same property. parameters (or (coerce! match) {:path (:path-params match) - :query q})] + :query q + :fragment fp})] (assoc match :parameters parameters)))))) (defn match-by-name diff --git a/test/cljs/reitit/frontend/core_test.cljs b/test/cljs/reitit/frontend/core_test.cljs index e5c1d13a..61c938d8 100644 --- a/test/cljs/reitit/frontend/core_test.cljs +++ b/test/cljs/reitit/frontend/core_test.cljs @@ -21,9 +21,11 @@ :data {:name ::frontpage} :path-params {} :query-params {} + :fragment-params {} :path "/" :parameters {:query {} - :path {}}}) + :path {} + :fragment {}}}) (rf/match-by-path router "/"))) (is (= "/" @@ -34,9 +36,11 @@ :data {:name ::foo} :path-params {} :query-params {} + :fragment-params {} :path "/foo" :parameters {:query {} - :path {}}}) + :path {} + :fragment {}}}) (rf/match-by-path router "/foo"))) (is (= (r/map->Match @@ -44,9 +48,11 @@ :data {:name ::foo} :path-params {} :query-params {:mode ["foo", "bar"]} + :fragment-params {} :path "/foo" :parameters {:query {:mode ["foo", "bar"]} - :path {}}}) + :path {} + :fragment {}}}) (rf/match-by-path router "/foo?mode=foo&mode=bar"))) (is (= "/foo" @@ -64,7 +70,12 @@ (let [router (r/router ["/" [":id" {:name ::foo :parameters {:path {:id s/Int} - :query {(s/optional-key :mode) s/Keyword}}}]] + :query {(s/optional-key :mode) s/Keyword} + :fragment {(s/optional-key :access_token) s/Str + (s/optional-key :refresh_token) s/Str + (s/optional-key :expires_in) s/Int + (s/optional-key :provider_token) s/Str + (s/optional-key :token_type) s/Str}}}]] {:compile rc/compile-request-coercers :data {:coercion rsc/coercion}})] @@ -72,9 +83,11 @@ {:template "/:id" :path-params {:id "5"} :query-params {} + :fragment-params {} :path "/5" :parameters {:query {} - :path {:id 5}}}) + :path {:id 5} + :fragment {}}}) (m (rf/match-by-path router "/5")))) (is (= "/5" @@ -98,23 +111,35 @@ {:template "/:id" :path-params {:id "5"} :query-params {:mode "foo"} + :fragment-params {} :path "/5" :parameters {:path {:id 5} - :query {:mode :foo}}}) + :query {:mode :foo} + :fragment {}}}) (m (rf/match-by-path router "/5?mode=foo")))) (is (= "/5?mode=foo" (r/match->path (rf/match-by-name router ::foo {:id 5}) {:mode :foo})))) - (testing "fragment is ignored" + (testing "fragment is read" (is (= (r/map->Match {:template "/:id" :path-params {:id "5"} :query-params {:mode "foo"} + :fragment-params {:access_token "foo" + :refresh_token "bar" + :provider_token "baz" + :token_type "bearer" + :expires_in "3600"} :path "/5" :parameters {:path {:id 5} - :query {:mode :foo}}}) - (m (rf/match-by-path router "/5?mode=foo#fragment"))))) + :query {:mode :foo} + :fragment {:access_token "foo" + :refresh_token "bar" + :provider_token "baz" + :token_type "bearer" + :expires_in 3600}}}) + (m (rf/match-by-path router "/5?mode=foo#access_token=foo&refresh_token=bar&provider_token=baz&token_type=bearer&expires_in=3600"))))) (testing "console warning about missing params" (is (= [{:type :warn From 4e14b1f05e70f315bb05ee9b56c5f74c6f65eff2 Mon Sep 17 00:00:00 2001 From: Peder Refsnes Date: Tue, 19 Jul 2022 22:25:51 +0200 Subject: [PATCH 07/33] Polish pedestal chains when printing context diffs --- .../reitit-interceptors/src/reitit/http/interceptors/dev.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/reitit-interceptors/src/reitit/http/interceptors/dev.clj b/modules/reitit-interceptors/src/reitit/http/interceptors/dev.clj index 896f99fc..54173d57 100644 --- a/modules/reitit-interceptors/src/reitit/http/interceptors/dev.clj +++ b/modules/reitit-interceptors/src/reitit/http/interceptors/dev.clj @@ -20,7 +20,9 @@ (defn- polish [ctx] (-> ctx - (dissoc ::original ::previous :stack :queue) + (dissoc ::original ::previous :stack :queue + :io.pedestal.interceptor.chain/stack + :io.pedestal.interceptor.chain/queue) (update :request dissoc ::r/match ::r/router))) (defn- handle [name stage] From fc73d02e01685bb5582e1b0c2779deb3eb4bb999 Mon Sep 17 00:00:00 2001 From: Paulo Feodrippe Date: Wed, 10 Aug 2022 21:20:35 -0400 Subject: [PATCH 08/33] add `:meta-merge-fn` option --- doc/advanced/configuring_routers.md | 25 ++++---- modules/reitit-core/src/reitit/impl.cljc | 6 +- .../reitit-core/src/reitit/interceptor.cljc | 4 +- .../reitit-core/src/reitit/middleware.cljc | 4 +- modules/reitit-http/src/reitit/http.cljc | 4 +- modules/reitit-ring/src/reitit/ring.cljc | 4 +- test/cljc/reitit/ring_coercion_test.cljc | 58 ++++++++++++++++++- 7 files changed, 81 insertions(+), 24 deletions(-) diff --git a/doc/advanced/configuring_routers.md b/doc/advanced/configuring_routers.md index 4cc30be9..35601c94 100644 --- a/doc/advanced/configuring_routers.md +++ b/doc/advanced/configuring_routers.md @@ -4,15 +4,16 @@ Routers can be configured via options. The following options are available for t | key | description |--------------|------------- -| `:path` | Base-path for routes -| `:routes` | Initial resolved routes (default `[]`) -| `:data` | Initial route data (default `{}`) -| `:spec` | clojure.spec definition for a route data, see `reitit.spec` on how to use this -| `:syntax` | Path-parameter syntax as keyword or set of keywords (default #{:bracket :colon}) -| `:expand` | Function of `arg opts => data` to expand route arg to route data (default `reitit.core/expand`) -| `:coerce` | Function of `route opts => route` to coerce resolved route, can throw or return `nil` -| `:compile` | Function of `route opts => result` to compile a route handler -| `:validate` | Function of `routes opts => ()` to validate route (data) via side-effects -| `:conflicts` | Function of `{route #{route}} => ()` to handle conflicting routes -| `:exception` | Function of `Exception => Exception ` to handle creation time exceptions (default `reitit.exception/exception`) -| `:router` | Function of `routes opts => router` to override the actual router implementation +| `:path` | Base-path for routes +| `:routes` | Initial resolved routes (default `[]`) +| `:data` | Initial route data (default `{}`) +| `:spec` | clojure.spec definition for a route data, see `reitit.spec` on how to use this +| `:syntax` | Path-parameter syntax as keyword or set of keywords (default #{:bracket :colon}) +| `:expand` | Function of `arg opts => data` to expand route arg to route data (default `reitit.core/expand`) +| `:coerce` | Function of `route opts => route` to coerce resolved route, can throw or return `nil` +| `:meta-merge-fn` | Function which follows the signature of `meta-merge.core/meta-merge`, useful for when you want to have more control over the meta merging +| `:compile` | Function of `route opts => result` to compile a route handler +| `:validate` | Function of `routes opts => ()` to validate route (data) via side-effects +| `:conflicts` | Function of `{route #{route}} => ()` to handle conflicting routes +| `:exception` | Function of `Exception => Exception ` to handle creation time exceptions (default `reitit.exception/exception`) +| `:router` | Function of `routes opts => router` to override the actual router implementation diff --git a/modules/reitit-core/src/reitit/impl.cljc b/modules/reitit-core/src/reitit/impl.cljc index 03c55e72..0805f8ff 100644 --- a/modules/reitit-core/src/reitit/impl.cljc +++ b/modules/reitit-core/src/reitit/impl.cljc @@ -60,17 +60,17 @@ (defn map-data [f routes] (mapv (fn [[p ds]] [p (f p ds)]) routes)) -(defn merge-data [p x] +(defn merge-data [{:keys [meta-merge-fn] :as g} p x] (reduce (fn [acc [k v]] (try - (mm/meta-merge acc {k v}) + ((or meta-merge-fn mm/meta-merge) acc {k v}) (catch #?(:clj Exception, :cljs js/Error) e (ex/fail! ::merge-data {:path p, :left acc, :right {k v}, :exception e})))) {} x)) (defn resolve-routes [raw-routes {:keys [coerce] :as opts}] - (cond->> (->> (walk raw-routes opts) (map-data merge-data)) + (cond->> (->> (walk raw-routes opts) (map-data #(merge-data opts %1 %2))) coerce (into [] (keep #(coerce % opts))))) (defn path-conflicting-routes [routes opts] diff --git a/modules/reitit-core/src/reitit/interceptor.cljc b/modules/reitit-core/src/reitit/interceptor.cljc index 8d90c2e4..9b544638 100644 --- a/modules/reitit-core/src/reitit/interceptor.cljc +++ b/modules/reitit-core/src/reitit/interceptor.cljc @@ -155,8 +155,8 @@ :handler get-user}]])" ([data] (router data nil)) - ([data opts] - (let [opts (meta-merge {:compile compile-result} opts)] + ([data {:keys [meta-merge-fn] :as opts}] + (let [opts ((or meta-merge-fn meta-merge) {:compile compile-result} opts)] (r/router data opts)))) (defn interceptor-handler [router] diff --git a/modules/reitit-core/src/reitit/middleware.cljc b/modules/reitit-core/src/reitit/middleware.cljc index 49381b68..58c13f67 100644 --- a/modules/reitit-core/src/reitit/middleware.cljc +++ b/modules/reitit-core/src/reitit/middleware.cljc @@ -138,8 +138,8 @@ :handler get-user}]])" ([data] (router data nil)) - ([data opts] - (let [opts (meta-merge {:compile compile-result} opts)] + ([data {:keys [meta-merge-fn] :as opts}] + (let [opts ((or meta-merge-fn meta-merge) {:compile compile-result} opts)] (r/router data opts)))) (defn middleware-handler [router] diff --git a/modules/reitit-http/src/reitit/http.cljc b/modules/reitit-http/src/reitit/http.cljc index c85817f8..36e96504 100644 --- a/modules/reitit-http/src/reitit/http.cljc +++ b/modules/reitit-http/src/reitit/http.cljc @@ -14,7 +14,7 @@ (update acc method expand opts) acc)) data ring/http-methods)]) -(defn compile-result [[path data] {:keys [::default-options-endpoint expand] :as opts}] +(defn compile-result [[path data] {:keys [::default-options-endpoint expand meta-merge-fn] :as opts}] (let [[top childs] (ring/group-keys data) childs (cond-> childs (and (not (:options childs)) (not (:handler top)) default-options-endpoint) @@ -38,7 +38,7 @@ (->methods true top) (reduce-kv (fn [acc method data] - (let [data (meta-merge top data)] + (let [data ((or meta-merge-fn meta-merge) top data)] (assoc acc method (->endpoint path data method method)))) (->methods (:handler top) data) childs)))) diff --git a/modules/reitit-ring/src/reitit/ring.cljc b/modules/reitit-ring/src/reitit/ring.cljc index 4aceaaf1..0439ab86 100644 --- a/modules/reitit-ring/src/reitit/ring.cljc +++ b/modules/reitit-ring/src/reitit/ring.cljc @@ -29,7 +29,7 @@ (update acc method expand opts) acc)) data http-methods)]) -(defn compile-result [[path data] {:keys [::default-options-endpoint expand] :as opts}] +(defn compile-result [[path data] {:keys [::default-options-endpoint expand meta-merge-fn] :as opts}] (let [[top childs] (group-keys data) childs (cond-> childs (and (not (:options childs)) (not (:handler top)) default-options-endpoint) @@ -50,7 +50,7 @@ (->methods true top) (reduce-kv (fn [acc method data] - (let [data (meta-merge top data)] + (let [data ((or meta-merge-fn meta-merge) top data)] (assoc acc method (->endpoint path data method method)))) (->methods (:handler top) data) childs)))) diff --git a/test/cljc/reitit/ring_coercion_test.cljc b/test/cljc/reitit/ring_coercion_test.cljc index cf4c9092..fb9879bc 100644 --- a/test/cljc/reitit/ring_coercion_test.cljc +++ b/test/cljc/reitit/ring_coercion_test.cljc @@ -3,6 +3,9 @@ [malli.experimental.lite :as l] #?@(:clj [[muuntaja.middleware] [jsonista.core :as j]]) + [malli.core :as m] + [malli.util :as mu] + [meta-merge.core :refer [meta-merge]] [reitit.coercion.malli :as malli] [reitit.coercion.schema :as schema] [reitit.coercion.spec :as spec] @@ -208,6 +211,38 @@ (let [{:keys [status]} (app invalid-request2)] (is (= 500 status)))))))) +(defn- custom-meta-merge-checking-schema + ([] {}) + ([left] left) + ([left right] + (cond + (and (map? left) (map? right)) + (merge-with custom-meta-merge-checking-schema left right) + + (and (m/schema? left) + (m/schema? right)) + (mu/merge left right) + + :else + (meta-merge left right))) + ([left right & more] + (reduce custom-meta-merge-checking-schema left (cons right more)))) + +(defn- custom-meta-merge-checking-parameters + ([] {}) + ([left] left) + ([left right] + (if (and (map? left) (map? right) + (contains? left :parameters) + (contains? right :parameters)) + (-> (merge-with custom-meta-merge-checking-parameters left right) + (assoc :parameters (merge-with mu/merge + (:parameters left) + (:parameters right)))) + (meta-merge left right))) + ([left right & more] + (reduce custom-meta-merge-checking-parameters left (cons right more)))) + (deftest malli-coercion-test (let [create (fn [middleware routes] (ring/ring-handler @@ -524,7 +559,28 @@ (is (= {:status 200, :body {:total -4}} (call "application/json" [:int {:encode/json -}])))) (testing "edn encoding (nada)" - (is (= {:status 200, :body {:total +4}} (call "application/edn" [:int {:encode/json -}])))))))) + (is (= {:status 200, :body {:total +4}} (call "application/edn" [:int {:encode/json -}])))))) + + (testing "using custom meta-merge function" + (let [->app (fn [schema-fn meta-merge-fn] + (ring/ring-handler + (ring/router + ["/merging-params/:foo" {:parameters {:path (schema-fn [:map [:foo :string]])}} + ["/:bar" {:parameters {:path (schema-fn [:map [:bar :string]])} + :get {:handler (fn [{{{:keys [foo bar]} :path} :parameters}] + {:status 200 + :body {:total (str "FOO: " foo ", " + "BAR: " bar)}})}}]] + {:data {:middleware [rrc/coerce-request-middleware + rrc/coerce-response-middleware] + :coercion malli/coercion} + :meta-merge-fn meta-merge-fn}))) + call (fn [schema-fn meta-merge-fn] + ((->app schema-fn meta-merge-fn) {:uri "/merging-params/this/that" + :request-method :get}))] + + (is (= {:status 200, :body {:total "FOO: this, BAR: that"}} (call m/schema custom-meta-merge-checking-schema))) + (is (= {:status 200, :body {:total "FOO: this, BAR: that"}} (call identity custom-meta-merge-checking-parameters))))))) #?(:clj (deftest muuntaja-test From c69b4cde3a3516571ab4c9246cc8b8489b149e5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toni=20V=C3=A4is=C3=A4nen?= Date: Wed, 12 Oct 2022 13:19:10 +0300 Subject: [PATCH 09/33] Handle empty seq as empty string in `query-string` example: instead of ```clojure (query-string {:nil nil :vec [] :seq-empty '()}) ;; => "nil=&&" ``` now ```clojure (query-string {:nil nil :vec [] :seq-empty '()}) ;; => "nil=&vec=&seq-empty=" ``` --- modules/reitit-core/src/reitit/impl.cljc | 6 +++++- test/cljc/reitit/impl_test.cljc | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/reitit-core/src/reitit/impl.cljc b/modules/reitit-core/src/reitit/impl.cljc index 03c55e72..4bce736e 100644 --- a/modules/reitit-core/src/reitit/impl.cljc +++ b/modules/reitit-core/src/reitit/impl.cljc @@ -249,6 +249,10 @@ (->> params (map (fn [[k v]] (if (or (sequential? v) (set? v)) - (str/join "&" (map query-parameter (repeat k) v)) + (if (seq v) + (str/join "&" (map query-parameter (repeat k) v)) + ;; Empty seq results in single & character in the query string. + ;; Handle as empty string to behave similarly as when the value is nil. + (query-parameter k "")) (query-parameter k v)))) (str/join "&"))) diff --git a/test/cljc/reitit/impl_test.cljc b/test/cljc/reitit/impl_test.cljc index 526c140c..119a5fad 100644 --- a/test/cljc/reitit/impl_test.cljc +++ b/test/cljc/reitit/impl_test.cljc @@ -50,6 +50,8 @@ {"a" "b"} "a=b" {:a 1} "a=1" {:a nil} "a=" + {:a []} "a=" + {:a '()} "a=" {:a :b :c "d"} "a=b&c=d" {:a "b c"} "a=b+c" {:a ["b" "c"]} "a=b&a=c" From 148fa2167b52fed582c9de9b7b3c615653696097 Mon Sep 17 00:00:00 2001 From: Jesse Dowell Date: Sun, 13 Nov 2022 18:54:43 -0500 Subject: [PATCH 10/33] Swagger: support operationId in generated swagger json --- modules/reitit-swagger/src/reitit/swagger.cljc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/reitit-swagger/src/reitit/swagger.cljc b/modules/reitit-swagger/src/reitit/swagger.cljc index 2c71a6d3..daffaff4 100644 --- a/modules/reitit-swagger/src/reitit/swagger.cljc +++ b/modules/reitit-swagger/src/reitit/swagger.cljc @@ -10,11 +10,12 @@ (s/def ::id (s/or :keyword keyword? :set (s/coll-of keyword? :into #{}))) (s/def ::no-doc boolean?) (s/def ::tags (s/coll-of (s/or :keyword keyword? :string string?) :kind #{})) +(s/def ::operationId string?) (s/def ::summary string?) (s/def ::description string?) (s/def ::swagger (s/keys :opt-un [::id])) -(s/def ::spec (s/keys :opt-un [::swagger ::no-doc ::tags ::summary ::description])) +(s/def ::spec (s/keys :opt-un [::swagger ::no-doc ::tags ::operationId ::summary ::description])) (def swagger-feature "Feature for handling swagger-documentation for routes. @@ -52,6 +53,7 @@ [\"/plus\" {:get {:swagger {:tags \"math\"} + :operationId \"addTwoNumbers\" :summary \"adds numbers together\" :description \"takes `x` and `y` query-params and adds them together\" :parameters {:query {:x int?, :y int?}} @@ -75,7 +77,7 @@ (let [{:keys [id] :or {id ::default} :as swagger} (-> match :result request-method :data :swagger) ids (trie/into-set id) strip-top-level-keys #(dissoc % :id :info :host :basePath :definitions :securityDefinitions) - strip-endpoint-keys #(dissoc % :id :parameters :responses :summary :description) + strip-endpoint-keys #(dissoc % :id :operationId :parameters :responses :summary :description) swagger (->> (strip-endpoint-keys swagger) (merge {:swagger "2.0" :x-id ids})) @@ -93,7 +95,7 @@ (apply meta-merge (keep (comp :swagger :data) interceptors)) (if coercion (coercion/get-apidocs coercion :swagger data)) - (select-keys data [:tags :summary :description]) + (select-keys data [:tags :operationId :summary :description]) (strip-top-level-keys swagger))])) transform-path (fn [[p _ c]] (if-let [endpoint (some->> c (keep transform-endpoint) (seq) (into {}))] From eebc2a3df07c831ef71fc0672c6efff6f95cef5f Mon Sep 17 00:00:00 2001 From: Ian Fernandez Date: Sun, 4 Dec 2022 00:07:05 +0000 Subject: [PATCH 11/33] Add example for Reitit + Pedestal + Malli coercion --- examples/pedestal-malli-swagger/.gitignore | 11 ++ examples/pedestal-malli-swagger/project.clj | 9 + .../pedestal-malli-swagger/src/server.clj | 164 ++++++++++++++++++ 3 files changed, 184 insertions(+) create mode 100644 examples/pedestal-malli-swagger/.gitignore create mode 100644 examples/pedestal-malli-swagger/project.clj create mode 100644 examples/pedestal-malli-swagger/src/server.clj diff --git a/examples/pedestal-malli-swagger/.gitignore b/examples/pedestal-malli-swagger/.gitignore new file mode 100644 index 00000000..708f60f6 --- /dev/null +++ b/examples/pedestal-malli-swagger/.gitignore @@ -0,0 +1,11 @@ +/target +/classes +/checkouts +pom.xml +pom.xml.asc +*.jar +*.class +/.lein-* +/.nrepl-port +.hgignore +.hg/ \ No newline at end of file diff --git a/examples/pedestal-malli-swagger/project.clj b/examples/pedestal-malli-swagger/project.clj new file mode 100644 index 00000000..695bd0d3 --- /dev/null +++ b/examples/pedestal-malli-swagger/project.clj @@ -0,0 +1,9 @@ +(defproject pedestal-malli-swagger-example "0.1.0-SNAPSHOT" + :description "Reitit-http with pedestal" + :dependencies [[org.clojure/clojure "1.10.0"] + [io.pedestal/pedestal.service "0.5.5"] + [io.pedestal/pedestal.jetty "0.5.5"] + [metosin/reitit-malli "0.5.18"] + [metosin/reitit-pedestal "0.5.18"] + [metosin/reitit "0.5.18"]] + :repl-options {:init-ns example.server}) diff --git a/examples/pedestal-malli-swagger/src/server.clj b/examples/pedestal-malli-swagger/src/server.clj new file mode 100644 index 00000000..72c93967 --- /dev/null +++ b/examples/pedestal-malli-swagger/src/server.clj @@ -0,0 +1,164 @@ +(ns example.server + (:require [clojure.java.io :as io] + [io.pedestal.http.route] + [reitit.interceptor] + [reitit.dev.pretty :as pretty] + [reitit.coercion.malli] + [io.pedestal.http] + [reitit.ring] + [reitit.ring.malli] + [reitit.http] + [reitit.pedestal] + [reitit.swagger :as swagger] + [reitit.swagger-ui :as swagger-ui] + [reitit.http.coercion :as coercion] + [reitit.http.interceptors.parameters :as parameters] + [reitit.http.interceptors.muuntaja :as muuntaja] + [reitit.http.interceptors.multipart :as multipart] + [muuntaja.core] + [malli.util :as mu])) + +(defn reitit-routes + [_config] + [["/swagger.json" {:get {:no-doc true + :swagger {:info {:title "my-api" + :description "with [malli](https://github.com/metosin/malli) and reitit-ring"} + :tags [{:name "files", + :description "file api"} + {:name "math", + :description "math api"}]} + :handler (swagger/create-swagger-handler)}}] + ["/files" {:swagger {:tags ["files"]}} + ["/upload" + {:post {:summary "upload a file" + :parameters {:multipart [:map [:file reitit.ring.malli/temp-file-part]]} + :responses {200 {:body [:map + [:name string?] + [:size int?]]}} + :handler (fn [{{{{:keys [filename + size]} :file} + :multipart} + :parameters}] + {:status 200 + :body {:name filename + :size size}})}}] + ["/download" {:get {:summary "downloads a file" + :swagger {:produces ["image/png"]} + :handler (fn [_] + {:status 200 + :headers {"Content-Type" "image/png"} + :body (-> "reitit.png" + (io/resource) + (io/input-stream))})}}]] + ["/math" {:swagger {:tags ["math"]}} + ["/plus" + {:get {:summary "plus with malli query parameters" + :parameters {:query [:map + [:x + {:title "X parameter" + :description "Description for X parameter" + :json-schema/default 42} + int?] + [:y int?]]} + :responses {200 {:body [:map [:total int?]]}} + :handler (fn [{{{:keys [x + y]} + :query} + :parameters}] + {:status 200 + :body {:total (+ x y)}})} + :post {:summary "plus with malli body parameters" + :parameters {:body [:map + [:x + {:title "X parameter" + :description "Description for X parameter" + :json-schema/default 42} + int?] + [:y int?]]} + :responses {200 {:body [:map [:total int?]]}} + :handler (fn [{{{:keys [x + y]} + :body} + :parameters}] + {:status 200 + :body {:total (+ x y)}})}}]]]) + +(defn reitit-ring-routes + [_config] + [(swagger-ui/create-swagger-ui-handler + {:path "/" + :config {:validatorUrl nil + :operationsSorter "alpha"}}) + (reitit.ring/create-resource-handler) + (reitit.ring/create-default-handler)]) + + +(defn reitit-router-config + [_config] + {:exception pretty/exception + :data {:coercion (reitit.coercion.malli/create + {:error-keys #{:coercion + :in + :schema + :value + :errors + :humanized} + :compile mu/closed-schema + :strip-extra-keys true + :default-values true + :options nil}) + :muuntaja muuntaja.core/instance + :interceptors [swagger/swagger-feature + (parameters/parameters-interceptor) + (muuntaja/format-negotiate-interceptor) + (muuntaja/format-response-interceptor) + (muuntaja/format-request-interceptor) + (coercion/coerce-response-interceptor) + (coercion/coerce-request-interceptor) + (multipart/multipart-interceptor)]}}) + +(def config + {:env :dev + :io.pedestal.http/routes [] + :io.pedestal.http/type :jetty + :io.pedestal.http/port 3000 + :io.pedestal.http/join? false + :io.pedestal.http/secure-headers {:content-security-policy-settings + {:default-src "'self'" + :style-src "'self' 'unsafe-inline'" + :script-src "'self' 'unsafe-inline'"}} + ::reitit-routes reitit-routes + ::reitit-ring-routes reitit-ring-routes + ::reitit-router-config reitit-router-config}) + +(defn reitit-http-router + [{::keys [reitit-routes + reitit-ring-routes + reitit-router-config] + :as config}] + (reitit.pedestal/routing-interceptor + (reitit.http/router + (reitit-routes config) + (reitit-router-config config)) + (->> config + reitit-ring-routes + (apply reitit.ring/routes)))) + +(defonce server (atom nil)) + +(defn start + [server + config] + (when @server + (io.pedestal.http/stop @server) + (println "server stopped")) + (-> config + io.pedestal.http/default-interceptors + (reitit.pedestal/replace-last-interceptor (reitit-http-router config)) + io.pedestal.http/dev-interceptors + io.pedestal.http/create-server + io.pedestal.http/start + (->> (reset! server))) + (println "server running in port 3000")) + +#_(start server config) \ No newline at end of file From edee97a5506740b9bf64d067abaf089bfb796cb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Vaeng=20R=C3=B8tnes?= Date: Mon, 5 Dec 2022 13:06:27 +0000 Subject: [PATCH 12/33] Rename variable handle -> handler --- modules/reitit-ring/src/reitit/ring.cljc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/reitit-ring/src/reitit/ring.cljc b/modules/reitit-ring/src/reitit/ring.cljc index 4aceaaf1..7eb3b73b 100644 --- a/modules/reitit-ring/src/reitit/ring.cljc +++ b/modules/reitit-ring/src/reitit/ring.cljc @@ -56,15 +56,15 @@ childs)))) (def default-options-handler - (let [handle (fn [request] - (let [methods (->> request get-match :result (keep (fn [[k v]] (if v k)))) - allow (->> methods (map (comp str/upper-case name)) (str/join ","))] - {:status 200, :body "", :headers {"Allow" allow}}))] + (let [handler (fn [request] + (let [methods (->> request get-match :result (keep (fn [[k v]] (if v k)))) + allow (->> methods (map (comp str/upper-case name)) (str/join ","))] + {:status 200, :body "", :headers {"Allow" allow}}))] (fn ([request] - (handle request)) + (handler request)) ([request respond _] - (respond (handle request)))))) + (respond (handler request)))))) (def default-options-endpoint {:no-doc true From 544e264ffba9dea9c6b1c0eda8ffb6765386ef92 Mon Sep 17 00:00:00 2001 From: Ian Fernandez Date: Mon, 5 Dec 2022 14:56:56 +0000 Subject: [PATCH 13/33] fix example in project.clj --- examples/pedestal-malli-swagger/project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/pedestal-malli-swagger/project.clj b/examples/pedestal-malli-swagger/project.clj index 695bd0d3..694d3beb 100644 --- a/examples/pedestal-malli-swagger/project.clj +++ b/examples/pedestal-malli-swagger/project.clj @@ -6,4 +6,4 @@ [metosin/reitit-malli "0.5.18"] [metosin/reitit-pedestal "0.5.18"] [metosin/reitit "0.5.18"]] - :repl-options {:init-ns example.server}) + :repl-options {:init-ns server}) From bc4443a935836b315cf2a7971b24f37dbca74db9 Mon Sep 17 00:00:00 2001 From: Ben Sless Date: Tue, 6 Dec 2022 21:20:48 +0200 Subject: [PATCH 14/33] Remove schema and errors from default malli coercion error keys --- modules/reitit-malli/src/reitit/coercion/malli.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/reitit-malli/src/reitit/coercion/malli.cljc b/modules/reitit-malli/src/reitit/coercion/malli.cljc index a57d2641..e7b2607c 100644 --- a/modules/reitit-malli/src/reitit/coercion/malli.cljc +++ b/modules/reitit-malli/src/reitit/coercion/malli.cljc @@ -114,7 +114,7 @@ :response {:default default-transformer-provider :formats {"application/json" json-transformer-provider}}} ;; set of keys to include in error messages - :error-keys #{:type :coercion :in :schema :value :errors :humanized #_:transformed} + :error-keys #{:type :coercion :in #_:schema :value #_:errors :humanized #_:transformed} ;; support lite syntax? :lite true ;; schema identity function (default: close all map schemas) From 8398c985956a59197736b5625110d2bf54c13fdc Mon Sep 17 00:00:00 2001 From: Ben Sless Date: Tue, 6 Dec 2022 21:34:13 +0200 Subject: [PATCH 15/33] Add serialize-failed-result coercion option False by default, if true will serialize the failed coercion result in the error message --- modules/reitit-core/src/reitit/coercion.cljc | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/modules/reitit-core/src/reitit/coercion.cljc b/modules/reitit-core/src/reitit/coercion.cljc index 0fd5d234..27fe6159 100644 --- a/modules/reitit-core/src/reitit/coercion.cljc +++ b/modules/reitit-core/src/reitit/coercion.cljc @@ -41,10 +41,12 @@ :header (->ParameterCoercion :headers :string true true) :path (->ParameterCoercion :path-params :string true true)}) -(defn ^:no-doc request-coercion-failed! [result coercion value in request] +(defn ^:no-doc request-coercion-failed! [result coercion value in request serialize-failed-result] (throw (ex-info - (str "Request coercion failed: " (pr-str result)) + (if serialize-failed-result + (str "Request coercion failed: " (pr-str result)) + "Request coercion failed") (merge (into {} result) {:type ::request-coercion @@ -53,10 +55,12 @@ :in [:request in] :request request})))) -(defn ^:no-doc response-coercion-failed! [result coercion value request response] +(defn ^:no-doc response-coercion-failed! [result coercion value request response serialize-failed-result] (throw (ex-info - (str "Response coercion failed: " (pr-str result)) + (if serialize-failed-result + (str "Response coercion failed: " (pr-str result)) + "Response coercion failed") (merge (into {} result) {:type ::response-coercion @@ -70,7 +74,7 @@ (-> request :muuntaja/request :format)) ;; TODO: support faster key walking, walk/keywordize-keys is quite slow... -(defn request-coercer [coercion type model {::keys [extract-request-format parameter-coercion] +(defn request-coercer [coercion type model {::keys [extract-request-format parameter-coercion serialize-failed-result] :or {extract-request-format extract-request-format-default parameter-coercion default-parameter-coercion}}] (if coercion @@ -83,13 +87,13 @@ format (extract-request-format request) result (coercer value format)] (if (error? result) - (request-coercion-failed! result coercion value in request) + (request-coercion-failed! result coercion value in request serialize-failed-result) result)))))))) (defn extract-response-format-default [request _] (-> request :muuntaja/response :format)) -(defn response-coercer [coercion body {:keys [extract-response-format] +(defn response-coercer [coercion body {:keys [extract-response-format serialize-failed-result] :or {extract-response-format extract-response-format-default}}] (if coercion (if-let [coercer (-response-coercer coercion body)] @@ -98,7 +102,7 @@ value (:body response) result (coercer value format)] (if (error? result) - (response-coercion-failed! result coercion value request response) + (response-coercion-failed! result coercion value request response serialize-failed-result) result)))))) (defn encode-error [data] From 24f38e0dfa24998c8f6dce98db886c010b307d47 Mon Sep 17 00:00:00 2001 From: Ben Sless Date: Tue, 6 Dec 2022 21:35:26 +0200 Subject: [PATCH 16/33] Unroll merge and hash-map coercion --- modules/reitit-core/src/reitit/coercion.cljc | 34 +++++++++++--------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/modules/reitit-core/src/reitit/coercion.cljc b/modules/reitit-core/src/reitit/coercion.cljc index 27fe6159..4436214c 100644 --- a/modules/reitit-core/src/reitit/coercion.cljc +++ b/modules/reitit-core/src/reitit/coercion.cljc @@ -47,13 +47,15 @@ (if serialize-failed-result (str "Request coercion failed: " (pr-str result)) "Request coercion failed") - (merge - (into {} result) - {:type ::request-coercion - :coercion coercion - :value value - :in [:request in] - :request request})))) + (-> {} + transient + (as-> $ (reduce conj! $ result)) + (assoc! :type ::request-coercion) + (assoc! :coercion coercion) + (assoc! :value value) + (assoc! :in [:request in]) + (assoc! :request request) + persistent!)))) (defn ^:no-doc response-coercion-failed! [result coercion value request response serialize-failed-result] (throw @@ -61,14 +63,16 @@ (if serialize-failed-result (str "Response coercion failed: " (pr-str result)) "Response coercion failed") - (merge - (into {} result) - {:type ::response-coercion - :coercion coercion - :value value - :in [:response :body] - :request request - :response response})))) + (-> {} + transient + (as-> $ (reduce conj! $ result)) + (assoc! :type ::response-coercion) + (assoc! :coercion coercion) + (assoc! :value value) + (assoc! :in [:response :body]) + (assoc! :request request) + (assoc! :response response) + persistent!)))) (defn extract-request-format-default [request] (-> request :muuntaja/request :format)) From 7e05f4931e48539a80250ad68d03737d18890937 Mon Sep 17 00:00:00 2001 From: Devin Walters Date: Sat, 17 Dec 2022 16:00:04 -0600 Subject: [PATCH 17/33] Upgrade jackson for CVE-2022-42003 and CVE-2022-42004 --- project.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project.clj b/project.clj index 8afdf260..edfe3e1b 100644 --- a/project.clj +++ b/project.clj @@ -36,8 +36,8 @@ [metosin/malli "0.8.2"] ;; https://clojureverse.org/t/depending-on-the-right-versions-of-jackson-libraries/5111 - [com.fasterxml.jackson.core/jackson-core "2.13.2"] - [com.fasterxml.jackson.core/jackson-databind "2.13.2.2"] + [com.fasterxml.jackson.core/jackson-core "2.14.1"] + [com.fasterxml.jackson.core/jackson-databind "2.14.1"] [meta-merge "1.0.0"] [fipp "0.6.25" :exclusions [org.clojure/core.rrb-vector]] From ffe8846de106dd2b6bbdb892bdecb99eb0c228c2 Mon Sep 17 00:00:00 2001 From: David Harrigan Date: Sun, 18 Dec 2022 07:09:30 +0000 Subject: [PATCH 18/33] Update documentation and link to the startrek project -=david=- --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 933b7be2..ce40a746 100644 --- a/README.md +++ b/README.md @@ -153,9 +153,13 @@ All examples are in https://github.com/metosin/reitit/tree/master/examples ## External resources * Simple web application using Ring/Reitit and Integrant: https://github.com/PrestanceDesign/usermanager-reitit-integrant-example -* A simple [ClojureScript](https://clojurescript.org/) frontend and Clojure backend using Reitit, [JUXT Clip](https://github.com/juxt/clip), [next.jdbc](https://github.com/seancorfield/next-jdbc) and other bits and bobs... - * [startrek](https://git.sr.ht/~dharrigan/startrek) - * [startrek-ui](https://git.sr.ht/~dharrigan/startrek-ui) +* A simple Clojure backend using Reitit to serve up a RESTful API: [startrek](https://github.com/dharrigan/startrek). Technologies include: + * [Donut System](https://github.com/donut-party/system) + * [next-jdbc](https://github.com/seancorfield/next-jdbc) + * [JUXT Clip](https://github.com/juxt/clip) + * [Flyway](https://github.com/flyway/flyway) + * [HoneySQL](https://github.com/seancorfield/honeysql) + * [Babashka](https://babashka.org) * https://www.learnreitit.com/ * Lipas, liikuntapalvelut: https://github.com/lipas-liikuntapaikat/lipas * Implementation of the Todo-Backend API spec, using Clojure, Ring/Reitit and next-jdbc: https://github.com/PrestanceDesign/todo-backend-clojure-reitit From 4d1b00edfab4c50bac22973e80f4de22aeb52ea5 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Wed, 18 Jan 2023 18:38:47 +0200 Subject: [PATCH 19/33] Revert "Merge pull request #554 from just-sultanov/add-support-for-fragment-parameters" This reverts commit c2c267f4d81dbcd1cc19c6b35766ea373aa2ea1b, reversing changes made to 8087522b8224706982f0ce27ce84dea2fd0fc121. --- modules/reitit-core/src/reitit/coercion.cljc | 3 +- .../reitit-frontend/src/reitit/frontend.cljs | 20 +-------- test/cljs/reitit/frontend/core_test.cljs | 43 ++++--------------- 3 files changed, 12 insertions(+), 54 deletions(-) diff --git a/modules/reitit-core/src/reitit/coercion.cljc b/modules/reitit-core/src/reitit/coercion.cljc index 75312c86..4436214c 100644 --- a/modules/reitit-core/src/reitit/coercion.cljc +++ b/modules/reitit-core/src/reitit/coercion.cljc @@ -39,8 +39,7 @@ :body (->ParameterCoercion :body-params :body false false) :form (->ParameterCoercion :form-params :string true true) :header (->ParameterCoercion :headers :string true true) - :path (->ParameterCoercion :path-params :string true true) - :fragment (->ParameterCoercion :fragment-params :string true true)}) + :path (->ParameterCoercion :path-params :string true true)}) (defn ^:no-doc request-coercion-failed! [result coercion value in request serialize-failed-result] (throw diff --git a/modules/reitit-frontend/src/reitit/frontend.cljs b/modules/reitit-frontend/src/reitit/frontend.cljs index 922b5a8b..d1e9c50a 100644 --- a/modules/reitit-frontend/src/reitit/frontend.cljs +++ b/modules/reitit-frontend/src/reitit/frontend.cljs @@ -1,6 +1,5 @@ (ns reitit.frontend (:require [clojure.set :as set] - [clojure.string :as str] [reitit.coercion :as coercion] [reitit.core :as r]) (:import goog.Uri @@ -21,19 +20,6 @@ (map (juxt keyword #(query-param q %))) (into {})))) -(defn fragment-params - "Given goog.Uri, read fragment parameters into Clojure map." - [^Uri uri] - (let [fp (.getFragment uri)] - (if-not (seq fp) - {} - (into {} - (comp - (map #(str/split % #"=")) - (map (fn [[k v]] - [(keyword k) v]))) - (str/split fp #"&"))))) - (defn match-by-path "Given routing tree and current path, return match with possibly coerced parameters. Return nil if no match found. @@ -51,14 +37,12 @@ coercion/coerce!)] (if-let [match (r/match-by-path router (.getPath uri))] (let [q (query-params uri) - fp (fragment-params uri) - match (assoc match :query-params q :fragment-params fp) + match (assoc match :query-params q) ;; Return uncoerced values if coercion is not enabled - so ;; that tha parameters are always accessible from same property. parameters (or (coerce! match) {:path (:path-params match) - :query q - :fragment fp})] + :query q})] (assoc match :parameters parameters)))))) (defn match-by-name diff --git a/test/cljs/reitit/frontend/core_test.cljs b/test/cljs/reitit/frontend/core_test.cljs index 61c938d8..e5c1d13a 100644 --- a/test/cljs/reitit/frontend/core_test.cljs +++ b/test/cljs/reitit/frontend/core_test.cljs @@ -21,11 +21,9 @@ :data {:name ::frontpage} :path-params {} :query-params {} - :fragment-params {} :path "/" :parameters {:query {} - :path {} - :fragment {}}}) + :path {}}}) (rf/match-by-path router "/"))) (is (= "/" @@ -36,11 +34,9 @@ :data {:name ::foo} :path-params {} :query-params {} - :fragment-params {} :path "/foo" :parameters {:query {} - :path {} - :fragment {}}}) + :path {}}}) (rf/match-by-path router "/foo"))) (is (= (r/map->Match @@ -48,11 +44,9 @@ :data {:name ::foo} :path-params {} :query-params {:mode ["foo", "bar"]} - :fragment-params {} :path "/foo" :parameters {:query {:mode ["foo", "bar"]} - :path {} - :fragment {}}}) + :path {}}}) (rf/match-by-path router "/foo?mode=foo&mode=bar"))) (is (= "/foo" @@ -70,12 +64,7 @@ (let [router (r/router ["/" [":id" {:name ::foo :parameters {:path {:id s/Int} - :query {(s/optional-key :mode) s/Keyword} - :fragment {(s/optional-key :access_token) s/Str - (s/optional-key :refresh_token) s/Str - (s/optional-key :expires_in) s/Int - (s/optional-key :provider_token) s/Str - (s/optional-key :token_type) s/Str}}}]] + :query {(s/optional-key :mode) s/Keyword}}}]] {:compile rc/compile-request-coercers :data {:coercion rsc/coercion}})] @@ -83,11 +72,9 @@ {:template "/:id" :path-params {:id "5"} :query-params {} - :fragment-params {} :path "/5" :parameters {:query {} - :path {:id 5} - :fragment {}}}) + :path {:id 5}}}) (m (rf/match-by-path router "/5")))) (is (= "/5" @@ -111,35 +98,23 @@ {:template "/:id" :path-params {:id "5"} :query-params {:mode "foo"} - :fragment-params {} :path "/5" :parameters {:path {:id 5} - :query {:mode :foo} - :fragment {}}}) + :query {:mode :foo}}}) (m (rf/match-by-path router "/5?mode=foo")))) (is (= "/5?mode=foo" (r/match->path (rf/match-by-name router ::foo {:id 5}) {:mode :foo})))) - (testing "fragment is read" + (testing "fragment is ignored" (is (= (r/map->Match {:template "/:id" :path-params {:id "5"} :query-params {:mode "foo"} - :fragment-params {:access_token "foo" - :refresh_token "bar" - :provider_token "baz" - :token_type "bearer" - :expires_in "3600"} :path "/5" :parameters {:path {:id 5} - :query {:mode :foo} - :fragment {:access_token "foo" - :refresh_token "bar" - :provider_token "baz" - :token_type "bearer" - :expires_in 3600}}}) - (m (rf/match-by-path router "/5?mode=foo#access_token=foo&refresh_token=bar&provider_token=baz&token_type=bearer&expires_in=3600"))))) + :query {:mode :foo}}}) + (m (rf/match-by-path router "/5?mode=foo#fragment"))))) (testing "console warning about missing params" (is (= [{:type :warn From 0870b20a05f3835e9022f77acaf1e677316bfc3b Mon Sep 17 00:00:00 2001 From: Ilmo Raunio Date: Fri, 20 Jan 2023 00:06:49 +0200 Subject: [PATCH 20/33] Add query-string to redirect-trailing-slash-handler --- modules/reitit-ring/src/reitit/ring.cljc | 4 +-- test/cljc/reitit/ring_test.cljc | 38 +++++++++++++++++++++--- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/modules/reitit-ring/src/reitit/ring.cljc b/modules/reitit-ring/src/reitit/ring.cljc index c98b2bc8..97fdc9d2 100644 --- a/modules/reitit-ring/src/reitit/ring.cljc +++ b/modules/reitit-ring/src/reitit/ring.cljc @@ -133,10 +133,10 @@ " ([] (redirect-trailing-slash-handler {:method :both})) ([{:keys [method]}] - (letfn [(maybe-redirect [request path] + (letfn [(maybe-redirect [{:keys [query-string] :as request} path] (if (and (seq path) (r/match-by-path (::r/router request) path)) {:status (if (= (:request-method request) :get) 301 308) - :headers {"Location" path} + :headers {"Location" (if query-string (str path "?" query-string) path)} :body ""})) (redirect-handler [request] (let [uri (:uri request)] diff --git a/test/cljc/reitit/ring_test.cljc b/test/cljc/reitit/ring_test.cljc index d7b9f8f4..efd91884 100644 --- a/test/cljc/reitit/ring_test.cljc +++ b/test/cljc/reitit/ring_test.cljc @@ -1,6 +1,6 @@ (ns reitit.ring-test (:require [clojure.set :as set] - [clojure.test :refer [deftest is testing]] + [clojure.test :refer [are deftest is testing]] [reitit.core :as r] [reitit.middleware :as middleware] [reitit.ring :as ring] @@ -312,7 +312,15 @@ (testing "does not strip slashes" (is (= nil (app {:request-method :get, :uri "/slash-less/"}))) - (is (= nil (app {:request-method :post, :uri "/slash-less/"})))))) + (is (= nil (app {:request-method :post, :uri "/slash-less/"})))) + + (testing "retains query-string in location header" + (are [method uri] + (is (= "/with-slash/?kikka=kukka" + (get-in (app {:request-method method :uri uri :query-string "kikka=kukka"}) + [:headers "Location"]))) + :get "/with-slash" + :post "/with-slash")))) (testing "using :method :strip" (let [app (ring/ring-handler @@ -338,7 +346,17 @@ (testing "strips multiple slashes" (is (= 301 (:status (app {:request-method :get, :uri "/slash-less/////"})))) - (is (= 308 (:status (app {:request-method :post, :uri "/slash-less//"}))))))) + (is (= 308 (:status (app {:request-method :post, :uri "/slash-less//"}))))) + + (testing "retains query-string in location header" + (are [method uri] + (is (= "/slash-less?kikka=kukka" + (get-in (app {:request-method method :uri uri :query-string "kikka=kukka"}) + [:headers "Location"]))) + :get "/slash-less/" + :get "/slash-less//" + :post "/slash-less/" + :post "/slash-less//")))) (testing "without option (equivalent to using :method :both)" (let [app (ring/ring-handler @@ -361,7 +379,19 @@ (testing "strips multiple slashes" (is (= 301 (:status (app {:request-method :get, :uri "/slash-less/////"})))) - (is (= 308 (:status (app {:request-method :post, :uri "/slash-less//"}))))))))) + (is (= 308 (:status (app {:request-method :post, :uri "/slash-less//"}))))) + + (testing "retains query-string in location header" + (are [method uri expected-location] + (is (= expected-location + (get-in (app {:request-method method :uri uri :query-string "kikka=kukka"}) + [:headers "Location"]))) + :get "/with-slash" "/with-slash/?kikka=kukka" + :get "/slash-less/" "/slash-less?kikka=kukka" + :get "/slash-less//" "/slash-less?kikka=kukka" + :post "/with-slash" "/with-slash/?kikka=kukka" + :post "/slash-less/" "/slash-less?kikka=kukka" + :post "/slash-less//" "/slash-less?kikka=kukka")))))) (deftest async-ring-test (let [promise #(let [value (atom ::nil)] From 98a4d9b447b307d82c7b71e2a2bb611a64a6b92a Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Mon, 9 Jan 2023 16:57:14 +0200 Subject: [PATCH 21/33] :meta-merge-fn -> :meta-merge --- doc/advanced/configuring_routers.md | 30 +++++++++---------- modules/reitit-core/src/reitit/impl.cljc | 4 +-- .../reitit-core/src/reitit/interceptor.cljc | 6 ++-- .../reitit-core/src/reitit/middleware.cljc | 6 ++-- modules/reitit-http/src/reitit/http.cljc | 6 ++-- modules/reitit-ring/src/reitit/ring.cljc | 6 ++-- test/cljc/reitit/ring_coercion_test.cljc | 10 +++---- 7 files changed, 34 insertions(+), 34 deletions(-) diff --git a/doc/advanced/configuring_routers.md b/doc/advanced/configuring_routers.md index 35601c94..3bd4a982 100644 --- a/doc/advanced/configuring_routers.md +++ b/doc/advanced/configuring_routers.md @@ -2,18 +2,18 @@ Routers can be configured via options. The following options are available for the `reitit.core/router`: -| key | description -|--------------|------------- -| `:path` | Base-path for routes -| `:routes` | Initial resolved routes (default `[]`) -| `:data` | Initial route data (default `{}`) -| `:spec` | clojure.spec definition for a route data, see `reitit.spec` on how to use this -| `:syntax` | Path-parameter syntax as keyword or set of keywords (default #{:bracket :colon}) -| `:expand` | Function of `arg opts => data` to expand route arg to route data (default `reitit.core/expand`) -| `:coerce` | Function of `route opts => route` to coerce resolved route, can throw or return `nil` -| `:meta-merge-fn` | Function which follows the signature of `meta-merge.core/meta-merge`, useful for when you want to have more control over the meta merging -| `:compile` | Function of `route opts => result` to compile a route handler -| `:validate` | Function of `routes opts => ()` to validate route (data) via side-effects -| `:conflicts` | Function of `{route #{route}} => ()` to handle conflicting routes -| `:exception` | Function of `Exception => Exception ` to handle creation time exceptions (default `reitit.exception/exception`) -| `:router` | Function of `routes opts => router` to override the actual router implementation +| key | description +|---------------|------------- +| `:path` | Base-path for routes +| `:routes` | Initial resolved routes (default `[]`) +| `:data` | Initial route data (default `{}`) +| `:spec` | clojure.spec definition for a route data, see `reitit.spec` on how to use this +| `:syntax` | Path-parameter syntax as keyword or set of keywords (default #{:bracket :colon}) +| `:expand` | Function of `arg opts => data` to expand route arg to route data (default `reitit.core/expand`) +| `:coerce` | Function of `route opts => route` to coerce resolved route, can throw or return `nil` +| `: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 +| `:validate` | Function of `routes opts => ()` to validate route (data) via side-effects +| `:conflicts` | Function of `{route #{route}} => ()` to handle conflicting routes +| `:exception` | Function of `Exception => Exception ` to handle creation time exceptions (default `reitit.exception/exception`) +| `:router` | Function of `routes opts => router` to override the actual router implementation diff --git a/modules/reitit-core/src/reitit/impl.cljc b/modules/reitit-core/src/reitit/impl.cljc index 40d02bf9..9b3780f5 100644 --- a/modules/reitit-core/src/reitit/impl.cljc +++ b/modules/reitit-core/src/reitit/impl.cljc @@ -60,11 +60,11 @@ (defn map-data [f routes] (mapv (fn [[p ds]] [p (f p ds)]) routes)) -(defn merge-data [{:keys [meta-merge-fn] :as g} p x] +(defn merge-data [{:keys [meta-merge]} p x] (reduce (fn [acc [k v]] (try - ((or meta-merge-fn mm/meta-merge) acc {k v}) + ((or meta-merge 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)) diff --git a/modules/reitit-core/src/reitit/interceptor.cljc b/modules/reitit-core/src/reitit/interceptor.cljc index 9b544638..7f3dc593 100644 --- a/modules/reitit-core/src/reitit/interceptor.cljc +++ b/modules/reitit-core/src/reitit/interceptor.cljc @@ -1,6 +1,6 @@ (ns reitit.interceptor (:require [clojure.pprint :as pprint] - [meta-merge.core :refer [meta-merge]] + [meta-merge.core :as mm] [reitit.core :as r] [reitit.exception :as exception] [reitit.impl :as impl])) @@ -155,8 +155,8 @@ :handler get-user}]])" ([data] (router data nil)) - ([data {:keys [meta-merge-fn] :as opts}] - (let [opts ((or meta-merge-fn meta-merge) {:compile compile-result} opts)] + ([data {:keys [meta-merge] :as opts}] + (let [opts ((or meta-merge mm/meta-merge) {:compile compile-result} opts)] (r/router data opts)))) (defn interceptor-handler [router] diff --git a/modules/reitit-core/src/reitit/middleware.cljc b/modules/reitit-core/src/reitit/middleware.cljc index 58c13f67..3329d84a 100644 --- a/modules/reitit-core/src/reitit/middleware.cljc +++ b/modules/reitit-core/src/reitit/middleware.cljc @@ -1,6 +1,6 @@ (ns reitit.middleware (:require [clojure.pprint :as pprint] - [meta-merge.core :refer [meta-merge]] + [meta-merge.core :as mm] [reitit.core :as r] [reitit.exception :as exception] [reitit.impl :as impl])) @@ -138,8 +138,8 @@ :handler get-user}]])" ([data] (router data nil)) - ([data {:keys [meta-merge-fn] :as opts}] - (let [opts ((or meta-merge-fn meta-merge) {:compile compile-result} opts)] + ([data {:keys [meta-merge] :as opts}] + (let [opts ((or meta-merge mm/meta-merge) {:compile compile-result} opts)] (r/router data opts)))) (defn middleware-handler [router] diff --git a/modules/reitit-http/src/reitit/http.cljc b/modules/reitit-http/src/reitit/http.cljc index 36e96504..51c8b896 100644 --- a/modules/reitit-http/src/reitit/http.cljc +++ b/modules/reitit-http/src/reitit/http.cljc @@ -1,5 +1,5 @@ (ns reitit.http - (:require [meta-merge.core :refer [meta-merge]] + (:require [meta-merge.core :as mm] [reitit.core :as r] [reitit.exception :as ex] [reitit.interceptor :as interceptor] @@ -14,7 +14,7 @@ (update acc method expand opts) 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 meta-merge] :as opts}] (let [[top childs] (ring/group-keys data) childs (cond-> childs (and (not (:options childs)) (not (:handler top)) default-options-endpoint) @@ -38,7 +38,7 @@ (->methods true top) (reduce-kv (fn [acc method data] - (let [data ((or meta-merge-fn meta-merge) top data)] + (let [data ((or meta-merge mm/meta-merge) top data)] (assoc acc method (->endpoint path data method method)))) (->methods (:handler top) data) childs)))) diff --git a/modules/reitit-ring/src/reitit/ring.cljc b/modules/reitit-ring/src/reitit/ring.cljc index 97fdc9d2..7dd2093d 100644 --- a/modules/reitit-ring/src/reitit/ring.cljc +++ b/modules/reitit-ring/src/reitit/ring.cljc @@ -1,6 +1,6 @@ (ns reitit.ring (:require [clojure.string :as str] - [meta-merge.core :refer [meta-merge]] + [meta-merge.core :as mm] #?@(:clj [[ring.util.mime-type :as mime-type] [ring.util.response :as response]]) [reitit.core :as r] @@ -29,7 +29,7 @@ (update acc method expand opts) 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 meta-merge] :as opts}] (let [[top childs] (group-keys data) childs (cond-> childs (and (not (:options childs)) (not (:handler top)) default-options-endpoint) @@ -50,7 +50,7 @@ (->methods true top) (reduce-kv (fn [acc method data] - (let [data ((or meta-merge-fn meta-merge) top data)] + (let [data ((or meta-merge mm/meta-merge) top data)] (assoc acc method (->endpoint path data method method)))) (->methods (:handler top) data) childs)))) diff --git a/test/cljc/reitit/ring_coercion_test.cljc b/test/cljc/reitit/ring_coercion_test.cljc index fb9879bc..64e906e3 100644 --- a/test/cljc/reitit/ring_coercion_test.cljc +++ b/test/cljc/reitit/ring_coercion_test.cljc @@ -562,7 +562,7 @@ (is (= {:status 200, :body {:total +4}} (call "application/edn" [:int {:encode/json -}])))))) (testing "using custom meta-merge function" - (let [->app (fn [schema-fn meta-merge-fn] + (let [->app (fn [schema-fn meta-merge] (ring/ring-handler (ring/router ["/merging-params/:foo" {:parameters {:path (schema-fn [:map [:foo :string]])}} @@ -574,10 +574,10 @@ {:data {:middleware [rrc/coerce-request-middleware rrc/coerce-response-middleware] :coercion malli/coercion} - :meta-merge-fn meta-merge-fn}))) - call (fn [schema-fn meta-merge-fn] - ((->app schema-fn meta-merge-fn) {:uri "/merging-params/this/that" - :request-method :get}))] + :meta-merge meta-merge}))) + call (fn [schema-fn meta-merge] + ((->app schema-fn meta-merge) {:uri "/merging-params/this/that" + :request-method :get}))] (is (= {:status 200, :body {:total "FOO: this, BAR: that"}} (call m/schema custom-meta-merge-checking-schema))) (is (= {:status 200, :body {:total "FOO: this, BAR: that"}} (call identity custom-meta-merge-checking-parameters))))))) From f27c2fc2aaac9d3c34ab4d2170c5d0b761e3abc2 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Mon, 9 Jan 2023 16:58:51 +0200 Subject: [PATCH 22/33] clean --- modules/reitit-core/src/reitit/impl.cljc | 7 +++++-- modules/reitit-core/src/reitit/interceptor.cljc | 5 ++--- modules/reitit-core/src/reitit/middleware.cljc | 5 ++--- modules/reitit-http/src/reitit/http.cljc | 8 ++++---- modules/reitit-ring/src/reitit/ring.cljc | 5 ++--- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/modules/reitit-core/src/reitit/impl.cljc b/modules/reitit-core/src/reitit/impl.cljc index 9b3780f5..8c6d1cbb 100644 --- a/modules/reitit-core/src/reitit/impl.cljc +++ b/modules/reitit-core/src/reitit/impl.cljc @@ -60,11 +60,14 @@ (defn map-data [f routes] (mapv (fn [[p ds]] [p (f p ds)]) routes)) -(defn merge-data [{:keys [meta-merge]} p x] +(defn meta-merge [left right opts] + ((or (:meta-merge opts) mm/meta-merge) left right)) + +(defn merge-data [opts p x] (reduce (fn [acc [k v]] (try - ((or meta-merge mm/meta-merge) acc {k v}) + (meta-merge acc {k v} opts) (catch #?(:clj Exception, :cljs js/Error) e (ex/fail! ::merge-data {:path p, :left acc, :right {k v}, :exception e})))) {} x)) diff --git a/modules/reitit-core/src/reitit/interceptor.cljc b/modules/reitit-core/src/reitit/interceptor.cljc index 7f3dc593..c01c1668 100644 --- a/modules/reitit-core/src/reitit/interceptor.cljc +++ b/modules/reitit-core/src/reitit/interceptor.cljc @@ -1,6 +1,5 @@ (ns reitit.interceptor (:require [clojure.pprint :as pprint] - [meta-merge.core :as mm] [reitit.core :as r] [reitit.exception :as exception] [reitit.impl :as impl])) @@ -155,8 +154,8 @@ :handler get-user}]])" ([data] (router data nil)) - ([data {:keys [meta-merge] :as opts}] - (let [opts ((or meta-merge mm/meta-merge) {:compile compile-result} opts)] + ([data opts] + (let [opts (impl/meta-merge {:compile compile-result} opts opts)] (r/router data opts)))) (defn interceptor-handler [router] diff --git a/modules/reitit-core/src/reitit/middleware.cljc b/modules/reitit-core/src/reitit/middleware.cljc index 3329d84a..4481b459 100644 --- a/modules/reitit-core/src/reitit/middleware.cljc +++ b/modules/reitit-core/src/reitit/middleware.cljc @@ -1,6 +1,5 @@ (ns reitit.middleware (:require [clojure.pprint :as pprint] - [meta-merge.core :as mm] [reitit.core :as r] [reitit.exception :as exception] [reitit.impl :as impl])) @@ -138,8 +137,8 @@ :handler get-user}]])" ([data] (router data nil)) - ([data {:keys [meta-merge] :as opts}] - (let [opts ((or meta-merge mm/meta-merge) {:compile compile-result} opts)] + ([data opts] + (let [opts (impl/meta-merge {:compile compile-result} opts opts)] (r/router data opts)))) (defn middleware-handler [router] diff --git a/modules/reitit-http/src/reitit/http.cljc b/modules/reitit-http/src/reitit/http.cljc index 51c8b896..4533ebe1 100644 --- a/modules/reitit-http/src/reitit/http.cljc +++ b/modules/reitit-http/src/reitit/http.cljc @@ -1,7 +1,7 @@ (ns reitit.http - (:require [meta-merge.core :as mm] - [reitit.core :as r] + (:require [reitit.core :as r] [reitit.exception :as ex] + [reitit.impl :as impl] [reitit.interceptor :as interceptor] [reitit.ring :as ring])) @@ -14,7 +14,7 @@ (update acc method expand opts) acc)) data ring/http-methods)]) -(defn compile-result [[path data] {:keys [::default-options-endpoint expand meta-merge] :as opts}] +(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) @@ -38,7 +38,7 @@ (->methods true top) (reduce-kv (fn [acc method data] - (let [data ((or meta-merge mm/meta-merge) top data)] + (let [data (impl/meta-merge top data opts)] (assoc acc method (->endpoint path data method method)))) (->methods (:handler top) data) childs)))) diff --git a/modules/reitit-ring/src/reitit/ring.cljc b/modules/reitit-ring/src/reitit/ring.cljc index 7dd2093d..22e62808 100644 --- a/modules/reitit-ring/src/reitit/ring.cljc +++ b/modules/reitit-ring/src/reitit/ring.cljc @@ -1,6 +1,5 @@ (ns reitit.ring (:require [clojure.string :as str] - [meta-merge.core :as mm] #?@(:clj [[ring.util.mime-type :as mime-type] [ring.util.response :as response]]) [reitit.core :as r] @@ -29,7 +28,7 @@ (update acc method expand opts) acc)) data http-methods)]) -(defn compile-result [[path data] {:keys [::default-options-endpoint expand meta-merge] :as opts}] +(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) @@ -50,7 +49,7 @@ (->methods true top) (reduce-kv (fn [acc method data] - (let [data ((or meta-merge mm/meta-merge) top data)] + (let [data (impl/meta-merge top data opts)] (assoc acc method (->endpoint path data method method)))) (->methods (:handler top) data) childs)))) From d1e02fd0a12fb404eab40c3b1789016e2d7fcd9e Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Mon, 9 Jan 2023 17:13:04 +0200 Subject: [PATCH 23/33] Update dependencies --- CHANGELOG.md | 13 +++++++++ project.clj | 44 +++++++++++++++---------------- test/cljc/reitit/swagger_test.clj | 2 ++ 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d77091f8..bda4b055 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,19 @@ We use [Break Versioning][breakver]. The version numbers follow a `. Date: Mon, 9 Jan 2023 17:15:29 +0200 Subject: [PATCH 24/33] CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bda4b055..6b1e7c1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ We use [Break Versioning][breakver]. The version numbers follow a `. Date: Mon, 9 Jan 2023 17:23:57 +0200 Subject: [PATCH 25/33] CHANGELOG --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b1e7c1c..197cfb3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,13 @@ We use [Break Versioning][breakver]. The version numbers follow a `. Date: Mon, 9 Jan 2023 17:30:09 +0200 Subject: [PATCH 26/33] CL --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 197cfb3b..7bf4f8ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ We use [Break Versioning][breakver]. The version numbers follow a `. Date: Mon, 9 Jan 2023 17:34:30 +0200 Subject: [PATCH 27/33] CL --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7bf4f8ed..c55ea7c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ We use [Break Versioning][breakver]. The version numbers follow a `. Date: Tue, 10 Jan 2023 08:05:42 +0200 Subject: [PATCH 28/33] fix spec tests + format --- CHANGELOG.md | 1 + .../reitit-core/src/reitit/interceptor.cljc | 10 +- .../reitit-core/src/reitit/middleware.cljc | 12 +- modules/reitit-core/src/reitit/trie.cljc | 124 +++++++++--------- modules/reitit-dev/src/reitit/dev/pretty.cljc | 27 ++-- modules/reitit-http/src/reitit/http.cljc | 58 ++++---- .../src/reitit/coercion/malli.cljc | 2 +- modules/reitit-ring/src/reitit/ring.cljc | 46 +++---- .../src/reitit/coercion/schema.cljc | 2 +- .../reitit-swagger/src/reitit/swagger.cljc | 14 +- project.clj | 6 +- test/clj/cljdoc/reaper.clj | 2 +- test/cljc/reitit/exception_test.cljc | 2 +- test/cljc/reitit/impl_test.cljc | 2 +- test/cljc/reitit/interceptor_test.cljc | 2 +- test/cljc/reitit/middleware_test.cljc | 2 +- test/cljc/reitit/ring_coercion_test.cljc | 2 +- test/cljc/reitit/ring_spec_test.cljc | 2 +- test/cljc/reitit/ring_test.cljc | 6 +- test/cljc/reitit/spec_test.cljc | 4 +- test/cljc/reitit/swagger_test.clj | 56 ++++---- .../reitit/frontend/controllers_test.cljs | 10 +- test/cljs/reitit/frontend/core_test.cljs | 12 +- test/cljs/reitit/frontend/history_test.cljs | 10 +- test/cljs/reitit/frontend/test_utils.cljs | 2 +- 25 files changed, 208 insertions(+), 208 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c55ea7c0..24965571 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ We use [Break Versioning][breakver]. The version numbers follow a `.> (r/match-by-path router path) - :result - :interceptors)) - {::router router})) + (fn [path] + (some->> (r/match-by-path router path) + :result + :interceptors)) + {::router router})) diff --git a/modules/reitit-core/src/reitit/middleware.cljc b/modules/reitit-core/src/reitit/middleware.cljc index 4481b459..3f1be365 100644 --- a/modules/reitit-core/src/reitit/middleware.cljc +++ b/modules/reitit-core/src/reitit/middleware.cljc @@ -143,9 +143,9 @@ (defn middleware-handler [router] (with-meta - (fn [path] - (some->> path - (r/match-by-path router) - :result - :handler)) - {::router router})) + (fn [path] + (some->> path + (r/match-by-path router) + :result + :handler)) + {::router router})) diff --git a/modules/reitit-core/src/reitit/trie.cljc b/modules/reitit-core/src/reitit/trie.cljc index 22c6356b..061dbeab 100644 --- a/modules/reitit-core/src/reitit/trie.cljc +++ b/modules/reitit-core/src/reitit/trie.cljc @@ -176,10 +176,10 @@ (fn [_ [p n]] (if-let [cp (common-prefix p path)] (if (= cp p) - ;; insert into child node + ;; insert into child node (let [n' (-insert n (conj ps (subs path (count p))) fp params data)] (reduced (assoc-in node [:children p] n'))) - ;; split child node + ;; split child node (let [rp (subs p (count cp)) rp' (subs path (count cp)) n' (-insert (-node {}) ps fp params data) @@ -189,7 +189,7 @@ (dissoc p) (assoc cp n''))))))))) nil (:children node)) - ;; new child node + ;; new child node (assoc-in node [:children path] (-insert (-node {}) ps fp params data))))] (if-let [child (get-in node' [:children ""])] ;; optimize by removing empty paths @@ -385,62 +385,62 @@ ;; (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))) diff --git a/modules/reitit-dev/src/reitit/dev/pretty.cljc b/modules/reitit-dev/src/reitit/dev/pretty.cljc index 158d09e0..78e683a3 100644 --- a/modules/reitit-dev/src/reitit/dev/pretty.cljc +++ b/modules/reitit-dev/src/reitit/dev/pretty.cljc @@ -9,8 +9,7 @@ [fipp.engine] [fipp.visit] [reitit.exception :as exception] - [spell-spec.expound] ;; expound -)) + [spell-spec.expound])) ;; expound ;; ;; colors @@ -46,17 +45,17 @@ :error 196}) (comment - (defn- -color [color & text] - (str "\033[38;5;" (colors color color) "m" (apply str text) "\u001B[0m")) + (defn- -color [color & text] + (str "\033[38;5;" (colors color color) "m" (apply str text) "\u001B[0m")) - (doseq [c (range 0 255)] - (println (-color c "kikka") "->" c)) + (doseq [c (range 0 255)] + (println (-color c "kikka") "->" c)) - (doseq [[n c] colors] - (println (-color c "kikka") "->" c n)) + (doseq [[n c] colors] + (println (-color c "kikka") "->" c n)) - (doseq [[k v] expound.ansi/sgr-code] - (println (expound.ansi/sgr "kikka" k) "->" k))) + (doseq [[k v] expound.ansi/sgr-code] + (println (expound.ansi/sgr "kikka" k) "->" k))) (defn- -start [x] (str "\033[38;5;" x "m")) (defn- -end [] "\u001B[0m") @@ -220,10 +219,10 @@ (defn exception [e] (let [data (-> e ex-data :data) message (format-exception (-> e ex-data :type) #?(:clj (.getMessage ^Exception e) :cljs (ex-message e)) data) - source #?(:clj (->> e Throwable->map :trace - (drop-while #(not= (name (first %)) "reitit.core$router")) - (drop-while #(= (name (first %)) "reitit.core$router")) - next first source-str) + source #?(:clj (->> e Throwable->map :trace + (drop-while #(not= (name (first %)) "reitit.core$router")) + (drop-while #(= (name (first %)) "reitit.core$router")) + next first source-str) :cljs "unknown")] (ex-info (exception-str message source (printer)) (assoc (or data {}) ::exception/cause e)))) diff --git a/modules/reitit-http/src/reitit/http.cljc b/modules/reitit-http/src/reitit/http.cljc index 4533ebe1..bf0f3ae5 100644 --- a/modules/reitit-http/src/reitit/http.cljc +++ b/modules/reitit-http/src/reitit/http.cljc @@ -138,35 +138,35 @@ enrich-request (ring/create-enrich-request inject-match? inject-router?) enrich-default-request (ring/create-enrich-default-request inject-router?)] (with-meta - (fn - ([request] - (if-let [match (r/match-by-path router (:uri request))] - (let [method (:request-method request) - path-params (:path-params match) - endpoint (-> match :result method) - interceptors (or (:queue endpoint) (:interceptors endpoint)) - request (enrich-request request path-params match router)] - (or (interceptor/execute executor interceptors request) - (interceptor/execute executor default-queue request))) - (interceptor/execute executor default-queue (enrich-default-request request router)))) - ([request respond raise] - (let [default #(interceptor/execute executor default-queue % respond raise)] - (if-let [match (r/match-by-path router (:uri request))] - (let [method (:request-method request) - path-params (:path-params match) - endpoint (-> match :result method) - interceptors (or (:queue endpoint) (:interceptors endpoint)) - request (enrich-request request path-params match router) - respond' (fn [response] - (if response - (respond response) - (default request)))] - (if interceptors - (interceptor/execute executor interceptors request respond' raise) - (default request))) - (default (enrich-default-request request router)))) - nil)) - {::r/router router})))) + (fn + ([request] + (if-let [match (r/match-by-path router (:uri request))] + (let [method (:request-method request) + path-params (:path-params match) + endpoint (-> match :result method) + interceptors (or (:queue endpoint) (:interceptors endpoint)) + request (enrich-request request path-params match router)] + (or (interceptor/execute executor interceptors request) + (interceptor/execute executor default-queue request))) + (interceptor/execute executor default-queue (enrich-default-request request router)))) + ([request respond raise] + (let [default #(interceptor/execute executor default-queue % respond raise)] + (if-let [match (r/match-by-path router (:uri request))] + (let [method (:request-method request) + path-params (:path-params match) + endpoint (-> match :result method) + interceptors (or (:queue endpoint) (:interceptors endpoint)) + request (enrich-request request path-params match router) + respond' (fn [response] + (if response + (respond response) + (default request)))] + (if interceptors + (interceptor/execute executor interceptors request respond' raise) + (default request))) + (default (enrich-default-request request router)))) + nil)) + {::r/router router})))) (defn get-router [handler] (-> handler meta ::r/router)) diff --git a/modules/reitit-malli/src/reitit/coercion/malli.cljc b/modules/reitit-malli/src/reitit/coercion/malli.cljc index e7b2607c..87388add 100644 --- a/modules/reitit-malli/src/reitit/coercion/malli.cljc +++ b/modules/reitit-malli/src/reitit/coercion/malli.cljc @@ -140,7 +140,7 @@ show? (fn [key] (contains? error-keys key)) 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)] + compile)] ^{:type ::coercion/coercion} (reify coercion/Coercion (-get-name [_] :malli) diff --git a/modules/reitit-ring/src/reitit/ring.cljc b/modules/reitit-ring/src/reitit/ring.cljc index 22e62808..9bb6d8d6 100644 --- a/modules/reitit-ring/src/reitit/ring.cljc +++ b/modules/reitit-ring/src/reitit/ring.cljc @@ -1,7 +1,7 @@ (ns reitit.ring (:require [clojure.string :as str] #?@(:clj [[ring.util.mime-type :as mime-type] - [ring.util.response :as response]]) + [ring.util.response :as response]]) [reitit.core :as r] [reitit.exception :as ex] [reitit.impl :as impl] @@ -316,28 +316,28 @@ enrich-request (create-enrich-request inject-match? inject-router?) 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))) - {::r/router router})))) + (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))) + {::r/router router})))) (defn get-router [handler] (-> handler meta ::r/router)) diff --git a/modules/reitit-schema/src/reitit/coercion/schema.cljc b/modules/reitit-schema/src/reitit/coercion/schema.cljc index 022f3872..4c29e1c4 100644 --- a/modules/reitit-schema/src/reitit/coercion/schema.cljc +++ b/modules/reitit-schema/src/reitit/coercion/schema.cljc @@ -47,7 +47,7 @@ (-get-name [_] :schema) (-get-options [_] opts) (-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 :swagger (swagger/swagger-spec (merge diff --git a/modules/reitit-swagger/src/reitit/swagger.cljc b/modules/reitit-swagger/src/reitit/swagger.cljc index 0286565a..3c3403cb 100644 --- a/modules/reitit-swagger/src/reitit/swagger.cljc +++ b/modules/reitit-swagger/src/reitit/swagger.cljc @@ -91,13 +91,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 :operationId]) - (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 :operationId]) + (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])) diff --git a/project.clj b/project.clj index 7a163232..e40b6d41 100644 --- a/project.clj +++ b/project.clj @@ -77,7 +77,7 @@ :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"] ;; modules dependencies @@ -115,7 +115,7 @@ [ring-cors "0.1.13"] [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" "-Xmx4096m" "-Dclojure.compiler.direct-linking=true"] @@ -141,7 +141,7 @@ "-XX:+PrintCompilation" "-XX:+UnlockDiagnosticVMOptions" "-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"] "test-clj" ["all" "do" ["bat-test"] ["check"]] "test-browser" ["doo" "chrome-headless" "test"] diff --git a/test/clj/cljdoc/reaper.clj b/test/clj/cljdoc/reaper.clj index bce3e68c..cee3dee6 100644 --- a/test/clj/cljdoc/reaper.clj +++ b/test/clj/cljdoc/reaper.clj @@ -30,4 +30,4 @@ (spit "doc/cljdoc.edn" (with-out-str (pprint/pprint data))))) (comment - (reap!)) + (reap!)) diff --git a/test/cljc/reitit/exception_test.cljc b/test/cljc/reitit/exception_test.cljc index bb77421d..77362c44 100644 --- a/test/cljc/reitit/exception_test.cljc +++ b/test/cljc/reitit/exception_test.cljc @@ -1,6 +1,6 @@ (ns reitit.exception-test (:require [clojure.spec.alpha :as s] - [clojure.test :refer [are deftest is testing]] + [clojure.test :refer [are deftest is]] [reitit.core :as r] [reitit.dev.pretty :as pretty] [reitit.exception :as exception] diff --git a/test/cljc/reitit/impl_test.cljc b/test/cljc/reitit/impl_test.cljc index 119a5fad..0059723b 100644 --- a/test/cljc/reitit/impl_test.cljc +++ b/test/cljc/reitit/impl_test.cljc @@ -1,5 +1,5 @@ (ns reitit.impl-test - (:require [clojure.test :refer [are deftest is testing]] + (:require [clojure.test :refer [are deftest is]] [reitit.impl :as impl])) (deftest strip-nils-test diff --git a/test/cljc/reitit/interceptor_test.cljc b/test/cljc/reitit/interceptor_test.cljc index 185db2db..f8bc8b43 100644 --- a/test/cljc/reitit/interceptor_test.cljc +++ b/test/cljc/reitit/interceptor_test.cljc @@ -1,5 +1,5 @@ (ns reitit.interceptor-test - (:require [clojure.test :refer [are deftest is testing]] + (:require [clojure.test :refer [deftest is testing]] [reitit.core :as r] [reitit.interceptor :as interceptor]) #?(:clj diff --git a/test/cljc/reitit/middleware_test.cljc b/test/cljc/reitit/middleware_test.cljc index 0b7bb05e..23426461 100644 --- a/test/cljc/reitit/middleware_test.cljc +++ b/test/cljc/reitit/middleware_test.cljc @@ -1,5 +1,5 @@ (ns reitit.middleware-test - (:require [clojure.test :refer [are deftest is testing]] + (:require [clojure.test :refer [deftest is testing]] [reitit.core :as r] [reitit.middleware :as middleware]) #?(:clj diff --git a/test/cljc/reitit/ring_coercion_test.cljc b/test/cljc/reitit/ring_coercion_test.cljc index 64e906e3..e8bee456 100644 --- a/test/cljc/reitit/ring_coercion_test.cljc +++ b/test/cljc/reitit/ring_coercion_test.cljc @@ -2,7 +2,7 @@ (:require [clojure.test :refer [deftest is testing]] [malli.experimental.lite :as l] #?@(:clj [[muuntaja.middleware] - [jsonista.core :as j]]) + [jsonista.core :as j]]) [malli.core :as m] [malli.util :as mu] [meta-merge.core :refer [meta-merge]] diff --git a/test/cljc/reitit/ring_spec_test.cljc b/test/cljc/reitit/ring_spec_test.cljc index 1112342b..5d4e4ee7 100644 --- a/test/cljc/reitit/ring_spec_test.cljc +++ b/test/cljc/reitit/ring_spec_test.cljc @@ -83,7 +83,7 @@ ExceptionInfo #"Invalid route data" (ring/router - ["/api" {:handler identity + ["/api" {:handler identity :middleware '()}] {:validate rrs/validate}))))) diff --git a/test/cljc/reitit/ring_test.cljc b/test/cljc/reitit/ring_test.cljc index efd91884..7f0a0af3 100644 --- a/test/cljc/reitit/ring_test.cljc +++ b/test/cljc/reitit/ring_test.cljc @@ -733,6 +733,6 @@ {::trie/trie-compiler compiler})] (dotimes [_ 10] (future - (dotimes [n 100000] - (let [body (:body (app {:request-method :get, :uri (str "/" n)}))] - (is (= body (str n)))))))))))) + (dotimes [n 100000] + (let [body (:body (app {:request-method :get, :uri (str "/" n)}))] + (is (= body (str n)))))))))))) diff --git a/test/cljc/reitit/spec_test.cljc b/test/cljc/reitit/spec_test.cljc index e2717989..740d2df2 100644 --- a/test/cljc/reitit/spec_test.cljc +++ b/test/cljc/reitit/spec_test.cljc @@ -39,7 +39,7 @@ (are [data] (is (thrown-with-msg? ExceptionInfo - #"Call to #'reitit.core/router did not conform to spec" + #"Call to (#')*reitit.core/router did not conform to spec" (r/router data))) @@ -69,7 +69,7 @@ (are [opts] (is (thrown-with-msg? ExceptionInfo - #"Call to #'reitit.core/router did not conform to spec" + #"Call to (#')*reitit.core/router did not conform to spec" (r/router ["/api"] opts))) diff --git a/test/cljc/reitit/swagger_test.clj b/test/cljc/reitit/swagger_test.clj index 1db2aec1..0958b8b1 100644 --- a/test/cljc/reitit/swagger_test.clj +++ b/test/cljc/reitit/swagger_test.clj @@ -22,35 +22,35 @@ :swagger {:info {:title "my-api"}} :handler (swagger/create-swagger-handler)}}] - ["/spec" {:coercion spec/coercion} - ["/plus/:z" - {:patch {:summary "patch" - :operationId "Patch" + ["/spec" {:coercion spec/coercion} + ["/plus/:z" + {:patch {:summary "patch" + :operationId "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" - :operationId "GetPlus" - :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" + :operationId "GetPlus" + :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" diff --git a/test/cljs/reitit/frontend/controllers_test.cljs b/test/cljs/reitit/frontend/controllers_test.cljs index 70fbcd9c..ba7e4410 100644 --- a/test/cljs/reitit/frontend/controllers_test.cljs +++ b/test/cljs/reitit/frontend/controllers_test.cljs @@ -10,11 +10,11 @@ (let [log (atom []) controller-state (atom []) 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)) - :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])) - :stop (fn [{:keys [foo]}] (swap! log conj [:stop-3 foo])) + :stop (fn [{:keys [foo]}] (swap! log conj [:stop-3 foo])) :identity (fn [match] {:foo (-> match :parameters :path :foo)})}] @@ -70,9 +70,9 @@ (let [log (atom []) controller-state (atom []) 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])) - :stop (fn [params] (swap! log conj [:stop params])) + :stop (fn [params] (swap! log conj [:stop params])) :parameters {:path [:foo]}}] (testing "init" diff --git a/test/cljs/reitit/frontend/core_test.cljs b/test/cljs/reitit/frontend/core_test.cljs index e5c1d13a..72bf9428 100644 --- a/test/cljs/reitit/frontend/core_test.cljs +++ b/test/cljs/reitit/frontend/core_test.cljs @@ -56,9 +56,9 @@ (is (= [{:type :warn :message ["missing route" ::asd]}] (:messages - (capture-console - (fn [] - (rf/match-by-name! router ::asd))))))))) + (capture-console + (fn [] + (rf/match-by-name! router ::asd))))))))) (testing "schema coercion" (let [router (r/router ["/" @@ -124,6 +124,6 @@ :required #{:id} :path-params {}}]}] (:messages - (capture-console - (fn [] - (rf/match-by-name! router ::foo {})))))))))) + (capture-console + (fn [] + (rf/match-by-name! router ::foo {})))))))))) diff --git a/test/cljs/reitit/frontend/history_test.cljs b/test/cljs/reitit/frontend/history_test.cljs index cf5341fb..ffab3970 100644 --- a/test/cljs/reitit/frontend/history_test.cljs +++ b/test/cljs/reitit/frontend/history_test.cljs @@ -27,8 +27,8 @@ (is (= "#/bar/5?q=x" (rfh/href history ::bar {:id 5} {:q "x"}))) (let [{:keys [value messages]} (capture-console - (fn [] - (rfh/href history ::asd)))] + (fn [] + (rfh/href history ::asd)))] (is (= nil value)) (is (= [{:type :warn :message ["missing route" ::asd]}] @@ -84,8 +84,8 @@ (is (= "/bar/5?q=x" (rfh/href history ::bar {:id 5} {:q "x"}))) (let [{:keys [value messages]} (capture-console - (fn [] - (rfh/href history ::asd)))] + (fn [] + (rfh/href history ::asd)))] (is (= nil value)) (is (= [{:type :warn :message ["missing route" ::asd]}] @@ -153,7 +153,7 @@ (done))))) {:use-fragment false}) create-link #(doto - (js/document.createElement "A") + (js/document.createElement "A") (.setAttribute "href" (rfh/href history ::foo))) document-link (create-link) shadow-link (create-link)] diff --git a/test/cljs/reitit/frontend/test_utils.cljs b/test/cljs/reitit/frontend/test_utils.cljs index b70c5f37..6bd1163f 100644 --- a/test/cljs/reitit/frontend/test_utils.cljs +++ b/test/cljs/reitit/frontend/test_utils.cljs @@ -10,6 +10,6 @@ (set! js/console.warn (partial log :warn)) (f) (finally - (set! js/console.warn original-console-warn)))] + (set! js/console.warn original-console-warn)))] {:value value :messages @messages})) From d0a9fd196ba7139fe9a699247e3074e83fe7bd06 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Tue, 10 Jan 2023 08:08:33 +0200 Subject: [PATCH 29/33] (c) year --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ce40a746..152d9197 100644 --- a/README.md +++ b/README.md @@ -184,6 +184,6 @@ Roadmap is mostly written in [issues](https://github.com/metosin/reitit/issues). ## License -Copyright © 2017-2021 [Metosin Oy](http://www.metosin.fi) +Copyright © 2017-2022 [Metosin Oy](http://www.metosin.fi) Distributed under the Eclipse Public License, the same as Clojure. From 358c44769833ff755011c38e6205960142062cd2 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Thu, 12 Jan 2023 19:17:19 +0200 Subject: [PATCH 30/33] Update to latest malli --- CHANGELOG.md | 2 +- project.clj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24965571..13a793fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,7 +29,7 @@ We use [Break Versioning][breakver]. The version numbers follow a `. Date: Sun, 22 Jan 2023 14:07:38 +0200 Subject: [PATCH 31/33] CHANGELOG --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13a793fe..d43adc70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,8 +14,9 @@ We use [Break Versioning][breakver]. The version numbers follow a `. Date: Sun, 22 Jan 2023 14:23:51 +0200 Subject: [PATCH 32/33] update malli --- CHANGELOG.md | 2 +- project.clj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d43adc70..69ef1f07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,7 @@ We use [Break Versioning][breakver]. The version numbers follow a `. Date: Sun, 22 Jan 2023 14:24:30 +0200 Subject: [PATCH 33/33] (c) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 152d9197..7cc3ac9e 100644 --- a/README.md +++ b/README.md @@ -184,6 +184,6 @@ Roadmap is mostly written in [issues](https://github.com/metosin/reitit/issues). ## License -Copyright © 2017-2022 [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.