diff --git a/README.md b/README.md index fbc27bdd..604824e0 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ See the [full documentation](https://metosin.github.io/reitit/) for details. ## Modules +* `reitit` - all bundled * `reitit-core` - the routing core * `reitit-ring` - a [ring router](https://metosin.github.io/reitit/ring/ring.html) * `reitit-middleware` - [common data-driven middleware](https://metosin.github.io/reitit/ring/default_middleware.html) for `reitit-ring` @@ -26,11 +27,12 @@ See the [full documentation](https://metosin.github.io/reitit/) for details. * `reitit-schema` [Schema](https://github.com/plumatic/schema) coercion * `reitit-swagger` [Swagger2](https://swagger.io/) apidocs * `reitit-swagger-ui` Integrated [Swagger UI](https://github.com/swagger-api/swagger-ui) +* [`reitit-frontend`](frontend/basics.md) Tools for frontend routing. Bubblin' under: -* `reitit-http` with enchanced Pedestal-style Interceptors (WIP) -* `reitit-frontend` with Keechma-style Controllers (WIP) +* `reitit-http` http-routing with Pedestal-style Interceptors (WIP) +* `reitit-sieppari` support for [Sieppari](https://github.com/metosin/sieppari) Interceptors (WIP) ## Latest version @@ -60,8 +62,9 @@ Optionally, the parts can be required separately: ;; frontend helpers (alpha) [metosin/reitit-frontend "0.2.0-SNAPSHOT"] -;; http helpers (alpha) +;; http with interceptors (alpha) [metosin/reitit-http "0.2.0-SNAPSHOT"] +[metosin/reitit-sieppari "0.2.0-SNAPSHOT"] ``` ## Quick start diff --git a/doc/README.md b/doc/README.md index 9e729ff9..1f2184e3 100644 --- a/doc/README.md +++ b/doc/README.md @@ -13,15 +13,24 @@ Modules: +* `reitit` - all bundled * `reitit-core` - the routing core -* [`reitit-ring`](ring/ring.md) with [data-driven middleware](https://metosin.github.io/reitit/ring/data_driven_middleware.html) +* `reitit-ring` - a [ring router](https://metosin.github.io/reitit/ring/ring.html) +* `reitit-middleware` - [common data-driven middleware](https://metosin.github.io/reitit/ring/default_middleware.html) for `reitit-ring` * `reitit-spec` [clojure.spec](https://clojure.org/about/spec) coercion * `reitit-schema` [Schema](https://github.com/plumatic/schema) coercion * `reitit-swagger` [Swagger2](https://swagger.io/) apidocs * `reitit-swagger-ui` Integrated [Swagger UI](https://github.com/swagger-api/swagger-ui). * [`reitit-frontend`](frontend/basics.md) Tools for frontend routing. -To use Reitit, add the following dependency to your project: +Bubblin' under: + +* `reitit-http` http-routing with Pedestal-style Interceptors (WIP) +* `reitit-sieppari` support for [Sieppari](https://github.com/metosin/sieppari) Interceptors (WIP) + +## Latest version + +All bundled: ```clj [metosin/reitit "0.2.0-SNAPSHOT"] @@ -47,8 +56,9 @@ Optionally, the parts can be required separately: ;; frontend helpers (alpha) [metosin/reitit-frontend "0.2.0-SNAPSHOT"] -;; http helpers (alpha) +;; http with interceptors (alpha) [metosin/reitit-http "0.2.0-SNAPSHOT"] +[metosin/reitit-sieppari "0.2.0-SNAPSHOT"] ``` For discussions, there is a [#reitit](https://clojurians.slack.com/messages/reitit/) channel in [Clojurians slack](http://clojurians.net/). diff --git a/modules/reitit-core/src/reitit/interceptor.cljc b/modules/reitit-core/src/reitit/interceptor.cljc index 9b085ec8..8b36d518 100644 --- a/modules/reitit-core/src/reitit/interceptor.cljc +++ b/modules/reitit-core/src/reitit/interceptor.cljc @@ -7,10 +7,19 @@ (defprotocol IntoInterceptor (into-interceptor [this data opts])) -(defrecord Interceptor [name handler? enter leave error]) -(defrecord Endpoint [data interceptors]) +(defrecord Interceptor [name enter leave error]) +(defrecord Endpoint [data interceptors queue]) (defrecord Context [request response exception]) +(defprotocol Executor + (queue + [this interceptors] + "takes a sequence of interceptors and compiles them to queue for the executor") + (execute + [this interceptors request] + [this interceptors request respond raise] + "executes the interceptor chain")) + (defn context [request] (map->Context {:request request})) @@ -50,7 +59,7 @@ :cljs function) (into-interceptor [this data opts] (into-interceptor - {:handler? true + {:name ::handler :enter (fn [ctx] (assoc ctx :response (this (:request ctx))))} data opts)) @@ -106,10 +115,12 @@ (defn compile-result ([route opts] (compile-result route opts nil)) - ([[_ {:keys [interceptors handler] :as data}] opts _] - (map->Endpoint - {:interceptors (chain (into (vec interceptors) [handler]) data opts) - :data data}))) + ([[_ {:keys [interceptors handler] :as data}] {:keys [::queue] :as opts} _] + (let [chain (chain (into (vec interceptors) [handler]) data opts)] + (map->Endpoint + {:interceptors chain + :queue ((or queue identity) chain) + :data data})))) (defn router "Creates a [[reitit.core/Router]] from raw route data and optionally an options map with diff --git a/modules/reitit-http/src/reitit/http.cljc b/modules/reitit-http/src/reitit/http.cljc index a7039ff1..e1ef8321 100644 --- a/modules/reitit-http/src/reitit/http.cljc +++ b/modules/reitit-http/src/reitit/http.cljc @@ -5,16 +5,7 @@ [reitit.core :as r] [reitit.impl :as impl])) -(defrecord Endpoint [data handler path method interceptors queue]) - -(defprotocol Executor - (queue - [this interceptors] - "takes a sequence of interceptors and compiles them to queue for the executor") - (execute - [this request interceptors] - [this request interceptors respond raise] - "executes the interceptor chain")) +(defrecord Endpoint [data interceptors queue handler path method]) (defn coerce-handler [[path data] {:keys [expand] :as opts}] [path (reduce @@ -23,23 +14,16 @@ (update acc method expand opts) acc)) data ring/http-methods)]) -(defn compile-result [[path data] {:keys [::queue] :as opts}] +(defn compile-result [[path data] opts] (let [[top childs] (ring/group-keys data) - ->handler (fn [handler] - (if handler - (fn [ctx] - (->> ctx :request handler (assoc ctx :response))))) compile (fn [[path data] opts scope] - (let [data (update data :handler ->handler)] - (interceptor/compile-result [path data] opts scope))) + (interceptor/compile-result [path data] opts scope)) ->endpoint (fn [p d m s] - (let [compiled (compile [p d] opts s) - interceptors (:interceptors compiled)] + (let [compiled (compile [p d] opts s)] (-> compiled (map->Endpoint) (assoc :path p) - (assoc :method m) - (assoc :queue ((or queue identity) interceptors))))) + (assoc :method m)))) ->methods (fn [any? data] (reduce (fn [acc method] @@ -83,41 +67,44 @@ | key | description | | ----------------|-------------| - | `:executor` | [[Executor]] for the interceptor chain + | `:executor` | `reitit.interceptor.Executor` for the interceptor chain | `:interceptors` | Optional sequence of interceptors that are always run before any other interceptors, even for the default handler" [router default-handler {:keys [executor interceptors]}] (let [default-handler (or default-handler (fn ([_]) ([_ respond _] (respond nil)))) - default-queue (queue executor (interceptor/into-interceptor (concat interceptors [default-handler]) nil (r/options router))) + default-queue (->> [default-handler] + (concat interceptors) + (map #(interceptor/into-interceptor % nil (r/options router))) + (interceptor/queue executor)) router-opts (-> (r/options router) - (assoc ::queue (partial queue executor)) - (update :interceptors (partial concat interceptors))) - router (router (r/routes router) router-opts)] + (assoc ::interceptor/queue (partial interceptor/queue executor)) + (update-in [:data :interceptors] (partial into (vec interceptors)))) + router (reitit.http/router (r/routes router) router-opts)] (with-meta (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) - interceptors (-> result method :interceptors) + endpoint (-> match :result method) + interceptors (or (:queue endpoint) (:interceptors endpoint)) request (-> request (impl/fast-assoc :path-params path-params) (impl/fast-assoc ::r/match match) (impl/fast-assoc ::r/router router))] - (execute executor interceptors request)) - (execute executor default-queue request))) + (interceptor/execute executor interceptors request)) + (interceptor/execute executor default-queue request))) ([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) - interceptors (-> result method :interceptors) + endpoint (-> match :result method) + interceptors (or (:queue endpoint) (:interceptors endpoint)) request (-> request (impl/fast-assoc :path-params path-params) (impl/fast-assoc ::r/match match) (impl/fast-assoc ::r/router router))] - (execute executor interceptors request respond raise)) - (execute executor default-queue request respond raise)) + (interceptor/execute executor interceptors request respond raise)) + (interceptor/execute executor default-queue request respond raise)) nil)) {::r/router router}))) diff --git a/modules/reitit-sieppari/project.clj b/modules/reitit-sieppari/project.clj new file mode 100644 index 00000000..16d14129 --- /dev/null +++ b/modules/reitit-sieppari/project.clj @@ -0,0 +1,10 @@ +(defproject metosin/reitit-sieppari "0.2.0-SNAPSHOT" + :description "Reitit: Sieppari Interceptors" + :url "https://github.com/metosin/reitit" + :license {:name "Eclipse Public License" + :url "http://www.eclipse.org/legal/epl-v10.html"} + :plugins [[lein-parent "0.3.2"]] + :parent-project {:path "../../project.clj" + :inherit [:deploy-repositories :managed-dependencies]} + :dependencies [[metosin/reitit-core] + [metosin/sieppari]]) diff --git a/modules/reitit-sieppari/src/reitit/interceptor/sieppari.clj b/modules/reitit-sieppari/src/reitit/interceptor/sieppari.clj new file mode 100644 index 00000000..53218b8e --- /dev/null +++ b/modules/reitit-sieppari/src/reitit/interceptor/sieppari.clj @@ -0,0 +1,14 @@ +(ns reitit.interceptor.sieppari + (:require [reitit.interceptor :as interceptor] + [sieppari.queue :as queue] + [sieppari.core :as sieppari])) + +(def executor + (reify + interceptor/Executor + (queue [_ interceptors] + (queue/into-queue interceptors)) + (execute [_ interceptors request] + (sieppari/execute interceptors request)) + (execute [_ interceptors request respond raise] + (sieppari/execute interceptors request respond raise)))) diff --git a/modules/reitit/project.clj b/modules/reitit/project.clj index 07ab18a3..5c1dff83 100644 --- a/modules/reitit/project.clj +++ b/modules/reitit/project.clj @@ -14,4 +14,5 @@ [metosin/reitit-http] [metosin/reitit-swagger] [metosin/reitit-swagger-ui] - [metosin/reitit-frontend]]) + [metosin/reitit-frontend] + [metosin/reitit-sieppari]]) diff --git a/project.clj b/project.clj index 32bab156..5bd53d69 100644 --- a/project.clj +++ b/project.clj @@ -19,13 +19,15 @@ [metosin/reitit-swagger "0.2.0-SNAPSHOT"] [metosin/reitit-swagger-ui "0.2.0-SNAPSHOT"] [metosin/reitit-frontend "0.2.0-SNAPSHOT"] + [metosin/reitit-sieppari "0.2.0-SNAPSHOT"] [meta-merge "1.0.0"] [ring/ring-core "1.6.3"] [metosin/spec-tools "0.7.1"] [metosin/schema-tools "0.10.3"] [metosin/ring-swagger-ui "2.2.10"] [metosin/muuntaja "0.6.0-alpha1"] - [metosin/jsonista "0.2.1"]] + [metosin/jsonista "0.2.1"] + [metosin/sieppari "0.0.0-alpha1"]] :plugins [[jonase/eastwood "0.2.6"] [lein-doo "0.1.10"] @@ -46,7 +48,8 @@ "modules/reitit-schema/src" "modules/reitit-swagger/src" "modules/reitit-swagger-ui/src" - "modules/reitit-frontend/src"] + "modules/reitit-frontend/src" + "modules/reitit-sieppari/src"] :dependencies [[org.clojure/clojure "1.9.0"] [org.clojure/clojurescript "1.10.339"] @@ -61,6 +64,7 @@ [ikitommi/immutant-web "3.0.0-alpha1"] [metosin/muuntaja "0.6.0-alpha1"] [metosin/ring-swagger-ui "2.2.10"] + [metosin/sieppari "0.0.0-alpha1"] [metosin/jsonista "0.2.1"] [criterium "0.4.4"] diff --git a/scripts/lein-modules b/scripts/lein-modules index a288b8f9..1a95289f 100755 --- a/scripts/lein-modules +++ b/scripts/lein-modules @@ -3,6 +3,17 @@ set -e # Modules -for ext in reitit-core reitit-spec reitit-schema reitit-ring reitit-middleware reitit-http reitit-swagger reitit-swagger-ui reitit-frontend reitit; do +for ext in \ + reitit-core \ + reitit-spec \ + reitit-schema \ + reitit-ring \ + reitit-middleware \ + reitit-http \ + reitit-swagger \ + reitit-swagger-ui \ + reitit-frontend \ + reitit-sieppari \ + reitit; do cd modules/$ext; lein "$@"; cd ../..; done diff --git a/test/cljc/reitit/interceptor_test.cljc b/test/cljc/reitit/interceptor_test.cljc index 1af4f4fd..cecc7a8a 100644 --- a/test/cljc/reitit/interceptor_test.cljc +++ b/test/cljc/reitit/interceptor_test.cljc @@ -182,18 +182,20 @@ (is (= [::enter_i1 ::enter_i3 :ok ::leave_i3 ::leave_i1] (app "/api"))) (testing "routes contain list of actually applied interceptors" - (is (= [::i1 ::i3 nil] (->> (r/compiled-routes router) - first - last - :interceptors - (map :name))))) + (is (= [::i1 ::i3 ::interceptor/handler] + (->> (r/compiled-routes router) + first + last + :interceptors + (map :name))))) (testing "match contains list of actually applied interceptors" - (is (= [::i1 ::i3 nil] (->> "/api" - (r/match-by-path router) - :result - :interceptors - (map :name)))))))))) + (is (= [::i1 ::i3 ::interceptor/handler] + (->> "/api" + (r/match-by-path router) + :result + :interceptors + (map :name)))))))))) (deftest chain-test (testing "chain can produce interceptor chain of any IntoInterceptor" @@ -227,7 +229,10 @@ (is (= [::olipa ::kerran ::avaruus :ok] (app "/ping"))))) (testing "interceptors can be re-ordered" - (let [app (create {::interceptor/transform (partial sort-by (juxt :handler? :name))})] + (let [app (create {::interceptor/transform (fn [interceptors] + (concat + (sort-by :name (butlast interceptors)) + [(last interceptors)]))})] (is (= [::avaruus ::kerran ::olipa :ok] (app "/ping"))))) (testing "adding debug interceptor between interceptors"