From bc2706147ced755120a397a8a86d9d0fffb57c2b Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 19 Aug 2017 16:04:13 +0300 Subject: [PATCH 01/11] PartialMatch & route-names --- README.md | 27 ++++++++++++++- src/reitit/core.cljc | 58 +++++++++++++++++++++++++-------- src/reitit/impl.cljc | 14 +++++--- test/cljc/reitit/core_test.cljc | 15 +++++++-- 4 files changed, 91 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index c73eba40..59a6f0c6 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,13 @@ The expanded routes: ; ["/api/user/:id" {:name :user/user}]] ``` +Route names: + +```clj +(reitit/route-names router) +; [:user/ping :user/user] +``` + Path-based routing: ```clj @@ -114,8 +121,19 @@ Name-based (reverse) routing: ```clj (reitit/match-by-name router ::user) -; ExceptionInfo missing path-params for route '/api/user/:id': #{:id} +; #PartialMatch{:template "/api/user/:id", +; :meta {:name :user/user}, +; :handler nil, +; :params nil, +; :required #{:id}} +(reitit/partial-match? (reitit/match-by-name router ::user)) +; true +``` + +Only a partial match. Let's provide path-parameters: + +```clj (reitit/match-by-name router ::user {:id "1"}) ; #Match{:template "/api/user/:id" ; :meta {:name :user/user} @@ -124,6 +142,13 @@ Name-based (reverse) routing: ; :params {:id "1"}} ``` +There is also a exception throwing version: + +``` +(reitit/match-by-name! router ::user) +; ExceptionInfo missing path-params for route /api/user/:id: #{:id} +``` + ## Route meta-data Routes can have arbitrary meta-data. For nested routes, the meta-data is accumulated from root towards leafs using [meta-merge](https://github.com/weavejester/meta-merge). diff --git a/src/reitit/core.cljc b/src/reitit/core.cljc index cdd36186..9bc35368 100644 --- a/src/reitit/core.cljc +++ b/src/reitit/core.cljc @@ -60,30 +60,54 @@ (cond->> (->> (walk data opts) (map-meta merge-meta)) coerce (into [] (keep #(coerce % opts))))) +(defn name-lookup [[_ {:keys [name]}] opts] + (if name #{name})) + +(defn find-names [routes opts] + (into [] (keep #(-> % second :name) routes))) + (defn compile-route [[p m :as route] {:keys [compile] :as opts}] [p m (if compile (compile route opts))]) (defprotocol Routing (routes [this]) + (route-names [this]) (match-by-path [this path]) - (match-by-name [this name] [this name parameters])) + (match-by-name [this name] [this name params])) -(defrecord Match [template meta path handler params]) +(defrecord Match [template meta handler params path]) +(defrecord PartialMatch [template meta handler params required]) + +(defn partial-match? [x] + (instance? PartialMatch x)) + +(defn match-by-name! + ([this name] + (match-by-name! this name nil)) + ([this name params] + (if-let [match (match-by-name this name params)] + (if-not (partial-match? match) + match + (impl/throw-on-missing-path-params + (:template match) (:required match) params))))) (def default-router-options - {:expand expand + {:lookup name-lookup + :expand expand :coerce (fn [route _] route) :compile (fn [[_ {:keys [handler]}] _] handler)}) -(defrecord LinearRouter [routes data lookup] +(defrecord LinearRouter [routes names data lookup] Routing (routes [_] routes) + (route-names [_] + names) (match-by-path [_ path] (reduce (fn [acc ^Route route] (if-let [params ((:matcher route) path)] - (reduced (->Match (:path route) (:meta route) path (:handler route) params)))) + (reduced (->Match (:path route) (:meta route) (:handler route) params path)))) nil data)) (match-by-name [_ name] (if-let [match (lookup name)] @@ -99,19 +123,24 @@ (linear-router routes {})) ([routes opts] (let [compiled (map #(compile-route % opts) routes) + names (find-names routes opts) [data lookup] (reduce (fn [[data lookup] [p {:keys [name] :as meta} handler]] - (let [route (impl/create [p meta handler])] + (let [{:keys [params] :as route} (impl/create [p meta handler]) + f #(if-let [path (impl/path-for route %)] + (->Match p meta handler % path) + (->PartialMatch p meta handler % params))] [(conj data route) - (if name - (assoc lookup name #(->Match p meta (impl/path-for route %) handler %)) - lookup)])) [[] {}] compiled)] - (->LinearRouter routes data lookup)))) + (if name (assoc lookup name f) lookup)])) + [[] {}] compiled)] + (->LinearRouter routes names data lookup)))) -(defrecord LookupRouter [routes data lookup] +(defrecord LookupRouter [routes names data lookup] Routing (routes [_] routes) + (route-names [_] + names) (match-by-path [_ path] (data path)) (match-by-name [_ name] @@ -134,13 +163,14 @@ {:route route :routes routes}))) (let [compiled (map #(compile-route % opts) routes) + names (find-names routes opts) [data lookup] (reduce (fn [[data lookup] [p {:keys [name] :as meta} handler]] - [(assoc data p (->Match p meta p handler {})) + [(assoc data p (->Match p meta handler {} p)) (if name - (assoc lookup name #(->Match p meta p handler %)) + (assoc lookup name #(->Match p meta handler % p)) lookup)]) [{} {}] compiled)] - (->LookupRouter routes data lookup)))) + (->LookupRouter routes names data lookup)))) (defn router "Create a [[Router]] from raw route data and optionally an options map. diff --git a/src/reitit/impl.cljc b/src/reitit/impl.cljc index 38fb5577..cd6b5d69 100644 --- a/src/reitit/impl.cljc +++ b/src/reitit/impl.cljc @@ -122,12 +122,16 @@ :handler handler}))) (defn path-for [^Route route params] - (when-not (every? #(contains? params %) (:params route)) + (if-let [required (:params route)] + (if (every? #(contains? params %) required) + (str "/" (str/join \/ (map #(get (or params {}) % %) (:parts route))))) + (:path route))) + +(defn throw-on-missing-path-params [template required params] + (when-not (every? #(contains? params %) required) (let [defined (-> params keys set) - required (:params route) missing (clojure.set/difference required defined)] (throw (ex-info - (str "missing path-params for route '" (:path route) "': " missing) - {:params params, :required required})))) - (str "/" (str/join \/ (map #(get params % %) (:parts route))))) + (str "missing path-params for route " template ": " missing) + {:params params, :required required}))))) diff --git a/test/cljc/reitit/core_test.cljc b/test/cljc/reitit/core_test.cljc index 6e28e101..77a2e7fd 100644 --- a/test/cljc/reitit/core_test.cljc +++ b/test/cljc/reitit/core_test.cljc @@ -25,11 +25,19 @@ :params {:size "large"}}) (reitit/match-by-name router ::beer {:size "large"}))) (is (= nil (reitit/match-by-name router "ILLEGAL"))) - (testing "name-based routing at runtime for missing parameters" + (is (= [::beer] (reitit/route-names router))) + (testing "name-based routing with missing parameters" + (is (= (reitit/map->PartialMatch + {:template "/api/ipa/:size" + :meta {:name ::beer} + :required #{:size} + :params nil}) + (reitit/match-by-name router ::beer))) + (is (= true (reitit/partial-match? (reitit/match-by-name router ::beer)))) (is (thrown-with-msg? ExceptionInfo - #"^missing path-params for route '/api/ipa/:size': \#\{:size\}$" - (reitit/match-by-name router ::beer)))))) + #"^missing path-params for route /api/ipa/:size: \#\{:size\}$" + (reitit/match-by-name! router ::beer)))))) (testing "lookup router" (let [router (reitit/router ["/api" ["/ipa" ["/large" ::beer]]])] @@ -49,6 +57,7 @@ :params {:size "large"}}) (reitit/match-by-name router ::beer {:size "large"}))) (is (= nil (reitit/match-by-name router "ILLEGAL"))) + (is (= [::beer] (reitit/route-names router))) (testing "can't be created with wildcard routes" (is (thrown-with-msg? ExceptionInfo From 0276e82dd99a0a68833c14a59ec5174ce0336c61 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 19 Aug 2017 16:04:44 +0300 Subject: [PATCH 02/11] Polish docs, more tests --- README.md | 21 ++++++++++++++++----- src/reitit/core.cljc | 4 ++-- test/cljc/reitit/ring_test.cljc | 28 ++++++++++++++++++++++++++-- 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 59a6f0c6..e1e195af 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ Same routes flattened: ["/api/ping" ::ping]] ``` -## Routers +## Routing For routing, a `Router` is needed. Reitit ships with 2 different router implementations: `LinearRouter` and `LookupRouter`, both based on the awesome [Pedestal](https://github.com/pedestal/pedestal/tree/master/route) implementation. @@ -78,7 +78,7 @@ Creating a router: (reitit/router [["/api" ["/ping" ::ping] - ["/user/:id" ::user]])) + ["/user/:id" ::user]]])) ``` `LinearRouter` is created (as there are wildcard): @@ -254,7 +254,8 @@ Routing based on `:request-method`: (def app (ring/ring-handler (ring/router - ["/ping" {:get handler + ["/ping" {:name ::ping + :get handler :post handler}]))) (app {:request-method :get, :uri "/ping"}) @@ -264,6 +265,16 @@ Routing based on `:request-method`: ; nil ``` +Reverse routing: + +```clj +(-> app + (ring/get-router) + (reitit/match-by-name ::ping) + :path) +; "/ping" +``` + Some middleware and a new handler: ```clj @@ -387,8 +398,8 @@ Routers can be configured via options. Options allow things like [`clojure.spec` | `:routes` | Initial resolved routes (default `[]`) | `:meta` | Initial expanded route-meta vector (default `[]`) | `:expand` | Function of `arg opts => meta` to expand route arg to route meta-data (default `reitit.core/expand`) - | `:coerce` | Function of `[path meta] opts => [path meta]` to coerce resolved route, can throw or return `nil` - | `:compile` | Function of `[path meta] opts => handler` to compile a route handler + | `:coerce` | Function of `route opts => route` to coerce resolved route, can throw or return `nil` + | `:compile` | Function of `route opts => handler` to compile a route handler ## Special thanks diff --git a/src/reitit/core.cljc b/src/reitit/core.cljc index 9bc35368..45bf3901 100644 --- a/src/reitit/core.cljc +++ b/src/reitit/core.cljc @@ -183,8 +183,8 @@ | `:routes` | Initial resolved routes (default `[]`) | `:meta` | Initial expanded route-meta vector (default `[]`) | `:expand` | Function of `arg opts => meta` to expand route arg to route meta-data (default `reitit.core/expand`) - | `:coerce` | Function of `[path meta] opts => [path meta]` to coerce resolved route, can throw or return `nil` - | `:compile` | Function of `[path meta] opts => handler` to compile a route handler" + | `:coerce` | Function of `route opts => route` to coerce resolved route, can throw or return `nil` + | `:compile` | Function of `route opts => handler` to compile a route handler" ([data] (router data {})) ([data opts] diff --git a/test/cljc/reitit/ring_test.cljc b/test/cljc/reitit/ring_test.cljc index bbd63e0b..641c40a7 100644 --- a/test/cljc/reitit/ring_test.cljc +++ b/test/cljc/reitit/ring_test.cljc @@ -2,7 +2,8 @@ (:require [clojure.test :refer [deftest testing is]] [reitit.middleware :as middleware] [reitit.ring :as ring] - [clojure.set :as set]) + [clojure.set :as set] + [reitit.core :as reitit]) #?(:clj (:import (clojure.lang ExceptionInfo)))) @@ -122,7 +123,30 @@ respond (partial reset! result), raise ::not-called] (app {:uri "/api/users" :request-method :post} respond raise) (is (= {:status 200, :body [:api :users :post :ok :post :users :api]} - @result))))))) + @result)))))) + + (testing "named routes" + (let [router (ring/router + [["/api" + ["/all" {:handler handler :name ::all}] + ["/get" {:get {:handler handler :name ::HIDDEN} + :name ::get}] + ["/users" {:get handler + :post handler + :handler handler + :name ::users}]]]) + app (ring/ring-handler router)] + + (testing "router can be extracted" + (is (= router (ring/get-router app)))) + + (testing "only top-level route names are matched" + (is (= [::all ::get ::users] + (reitit/route-names router)))) + + (testing "all named routes can be matched" + (doseq [name (reitit/route-names router)] + (is (= name (-> (reitit/match-by-name router name) :meta :name)))))))) (defn wrap-enforce-roles [handler] (fn [{:keys [::roles] :as request}] From 9c0ac5bcc0361e1a7a6597606b9361feb943431f Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 19 Aug 2017 16:05:42 +0300 Subject: [PATCH 03/11] Sketch for real world perf tests --- project.clj | 1 + test/clj/reitit/router_test.clj | 132 ++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 test/clj/reitit/router_test.clj diff --git a/project.clj b/project.clj index 97dbaf25..f5178328 100644 --- a/project.clj +++ b/project.clj @@ -22,6 +22,7 @@ :jvm-opts ^:replace ["-server"] :dependencies [[org.clojure/clojure "1.9.0-alpha17"] [org.clojure/clojurescript "1.9.660"] + [cheshire "5.8.0"] [criterium "0.4.4"] [org.clojure/test.check "0.9.0"] [org.clojure/tools.namespace "0.2.11"] diff --git a/test/clj/reitit/router_test.clj b/test/clj/reitit/router_test.clj new file mode 100644 index 00000000..8dcc38d4 --- /dev/null +++ b/test/clj/reitit/router_test.clj @@ -0,0 +1,132 @@ +(ns reitit.router-test + (:require [clojure.test :refer [deftest testing is]] + [cheshire.core :as json] + [clojure.string :as str] + [reitit.core :as reitit])) + +;; +;; extract sample routes +;; + +(defn swagger->routes [url ring?] + (let [route-number (atom 0) + ->route-name #(keyword "test" (str "route" (swap! route-number inc))) + ->endpoint (fn [m] + (if ring? + (reduce-kv + (fn [acc k v] + (assoc acc k {:handler #'identity, :name (->route-name)})) + {} (select-keys m #{:get :head :patch :delete :options :post :put})) + (->route-name)))] + (-> (slurp url) + (json/parse-string true) + (->> :paths + (mapv (fn [[p v]] [(-> p name (str/replace #"\{(.*?)\}" ":$1") (->> (str "/"))) (->endpoint v)])))))) + +(defn valid-urls [router] + (->> + (for [name (reitit/route-names router) + :let [match (reitit/match-by-name router name) + params (if (reitit/partial-match? match) + (-> match :required (zipmap (range))))]] + (:path (reitit/match-by-name router name params))) + (into []))) + +(comment + (swagger->routes "https://api.opensensors.io/doc" false)) + +(defn bench-routes [routes f] + (let [router (reitit/router routes) + urls (valid-urls router) + random-url #(rand-nth urls) + log-time #(let [now (System/nanoTime)] (%) (- (System/nanoTime) now)) + total 10000 + dropped (int (* total 0.45))] + (mapv + #(let [times (->> (range total) + (mapv + (fn [_] + (let [now (System/nanoTime)] + (f %) + (- (System/nanoTime) now)))) + (sort) + (drop dropped) + (drop-last dropped)) + avg (int (/ (reduce + times) (count times)))] + [(-> % f :template) avg]) urls))) + +(defn bench [routes no-paths?] + (let [routes (mapv (fn [[path name]] + (if no-paths? + [(str/replace path #"\:" "") name] + [path name])) routes) + router (reitit/router routes)] + (doseq [[path time] (bench-routes routes #(reitit/match-by-path router %))] + (println path "\t" time)))) + +;; +;; Perf tests +;; + +(def opensensors-routes + [["/v2/whoami" :test/route1] + ["/v2/users/:user-id/datasets" :test/route2] + ["/v2/public/projects/:project-id/datasets" :test/route3] + ["/v1/public/topics/:topic" :test/route4] + ["/v1/users/:user-id/orgs/:org-id" :test/route5] + ["/v1/search/topics/:term" :test/route6] + ["/v1/users/:user-id/invitations" :test/route7] + ["/v1/orgs/:org-id/devices/:batch/:type" :test/route8] + ["/v1/users/:user-id/topics" :test/route9] + ["/v1/users/:user-id/bookmarks/followers" :test/route10] + ["/v2/datasets/:dataset-id" :test/route11] + ["/v1/orgs/:org-id/usage-stats" :test/route12] + ["/v1/orgs/:org-id/devices/:client-id" :test/route13] + ["/v1/messages/user/:user-id" :test/route14] + ["/v1/users/:user-id/devices" :test/route15] + ["/v1/public/users/:user-id" :test/route16] + ["/v1/orgs/:org-id/errors" :test/route17] + ["/v1/public/orgs/:org-id" :test/route18] + ["/v1/orgs/:org-id/invitations" :test/route19] + ["/v2/public/messages/dataset/bulk" :test/route20] + ["/v1/users/:user-id/devices/bulk" :test/route21] + ["/v1/users/:user-id/device-errors" :test/route22] + ["/v2/login" :test/route23] + ["/v1/users/:user-id/usage-stats" :test/route24] + ["/v2/users/:user-id/devices" :test/route25] + ["/v1/users/:user-id/claim-device/:client-id" :test/route26] + ["/v2/public/projects/:project-id" :test/route27] + ["/v2/public/datasets/:dataset-id" :test/route28] + ["/v2/users/:user-id/topics/bulk" :test/route29] + ["/v1/messages/device/:client-id" :test/route30] + ["/v1/users/:user-id/owned-orgs" :test/route31] + ["/v1/topics/:topic" :test/route32] + ["/v1/users/:user-id/bookmark/:topic" :test/route33] + ["/v1/orgs/:org-id/members/:user-id" :test/route34] + ["/v1/users/:user-id/devices/:client-id" :test/route35] + ["/v1/users/:user-id" :test/route36] + ["/v1/orgs/:org-id/devices" :test/route37] + ["/v1/orgs/:org-id/members" :test/route38] + ["/v1/orgs/:org-id/members/invitation-data/:user-id" :test/route39] + ["/v2/orgs/:org-id/topics" :test/route40] + ["/v1/whoami" :test/route41] + ["/v1/orgs/:org-id" :test/route42] + ["/v1/users/:user-id/api-key" :test/route43] + ["/v2/schemas" :test/route44] + ["/v2/users/:user-id/topics" :test/route45] + ["/v1/orgs/:org-id/confirm-membership/:token" :test/route46] + ["/v2/topics/:topic" :test/route47] + ["/v1/messages/topic/:topic" :test/route48] + ["/v1/users/:user-id/devices/:client-id/reset-password" :test/route49] + ["/v2/topics" :test/route50] + ["/v1/login" :test/route51] + ["/v1/users/:user-id/orgs" :test/route52] + ["/v2/public/messages/dataset/:dataset-id" :test/route53] + ["/v1/topics" :test/route54] + ["/v1/orgs" :test/route55] + ["/v1/users/:user-id/bookmarks" :test/route56] + ["/v1/orgs/:org-id/topics" :test/route57]]) + +(comment + (bench opensensors-routes false) + (bench opensensors-routes true)) From b780a760ef562d3e03c8bd4e606d2f75829657e3 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sat, 19 Aug 2017 21:25:47 +0300 Subject: [PATCH 04/11] Initial tests for the opensensors routing --- .../clj/reitit/opensensors_routing_test.clj | 305 ++++++++++++++++++ test/clj/reitit/router_test.clj | 132 -------- 2 files changed, 305 insertions(+), 132 deletions(-) create mode 100644 perf-test/clj/reitit/opensensors_routing_test.clj delete mode 100644 test/clj/reitit/router_test.clj diff --git a/perf-test/clj/reitit/opensensors_routing_test.clj b/perf-test/clj/reitit/opensensors_routing_test.clj new file mode 100644 index 00000000..237c1c50 --- /dev/null +++ b/perf-test/clj/reitit/opensensors_routing_test.clj @@ -0,0 +1,305 @@ +(ns reitit.opensensors-routing-test + (:require [clojure.test :refer [deftest testing is]] + [cheshire.core :as json] + [clojure.string :as str] + [bidi.bidi :as bidi] + [ataraxy.core :as ataraxy] + [reitit.core :as reitit])) + +(defn raw-title [color s] + (println (str color (apply str (repeat (count s) "#")) "\u001B[0m")) + (println (str color s "\u001B[0m")) + (println (str color (apply str (repeat (count s) "#")) "\u001B[0m"))) + +(def title (partial raw-title "\u001B[35m")) +(def suite (partial raw-title "\u001B[32m")) + +;; +;; extract sample routes +;; + +(defn swagger->routes [url ring?] + (let [route-number (atom 0) + ->route-name #(keyword "test" (str "route" (swap! route-number inc))) + ->endpoint (fn [m] + (if ring? + (reduce-kv + (fn [acc k v] + (assoc acc k {:handler #'identity, :name (->route-name)})) + {} (select-keys m #{:get :head :patch :delete :options :post :put})) + (->route-name)))] + (-> (slurp url) + (json/parse-string true) + (->> :paths + (mapv (fn [[p v]] [(-> p name (str/replace #"\{(.*?)\}" ":$1") (->> (str "/"))) (->endpoint v)])))))) + +(defn valid-urls [router] + (->> + (for [name (reitit/route-names router) + :let [match (reitit/match-by-name router name) + params (if (reitit/partial-match? match) + (-> match :required (zipmap (range))))]] + (:path (reitit/match-by-name router name params))) + (into []))) + +(comment + (swagger->routes "https://api.opensensors.io/doc" false)) + +(defn bench-routes [routes f] + (let [router (reitit/router routes) + urls (valid-urls router) + random-url #(rand-nth urls) + log-time #(let [now (System/nanoTime)] (%) (- (System/nanoTime) now)) + total 10000 + dropped (int (* total 0.45))] + (mapv + #(let [times (->> (range total) + (mapv + (fn [_] + (let [now (System/nanoTime) + result (f %) + total (- (System/nanoTime) now)] + (assert result) + total))) + (sort) + (drop dropped) + (drop-last dropped)) + avg (int (/ (reduce + times) (count times)))] + [% avg]) urls))) + +(defn bench [routes no-paths?] + (let [routes (mapv (fn [[path name]] + (if no-paths? + [(str/replace path #"\:" "") name] + [path name])) routes) + router (reitit/router routes)] + (doseq [[path time] (bench-routes routes #(reitit/match-by-path router %))] + (println path "\t" time)))) + +(defn bench [routes no-paths?] + (let [routes (mapv (fn [[path name]] + (if no-paths? + [(str/replace path #"\:" "") name] + [path name])) routes) + router (reitit/router routes)] + (doseq [[path time] (bench-routes routes #(reitit/match-by-path router %))] + (println path "\t" time)))) +;; +;; Perf tests +;; + +(def opensensors-routes + [["/v2/whoami" :test/route1] + ["/v2/users/:user-id/datasets" :test/route2] + ["/v2/public/projects/:project-id/datasets" :test/route3] + ["/v1/public/topics/:topic" :test/route4] + ["/v1/users/:user-id/orgs/:org-id" :test/route5] + ["/v1/search/topics/:term" :test/route6] + ["/v1/users/:user-id/invitations" :test/route7] + ["/v1/orgs/:org-id/devices/:batch/:type" :test/route8] + ["/v1/users/:user-id/topics" :test/route9] + ["/v1/users/:user-id/bookmarks/followers" :test/route10] + ["/v2/datasets/:dataset-id" :test/route11] + ["/v1/orgs/:org-id/usage-stats" :test/route12] + ["/v1/orgs/:org-id/devices/:client-id" :test/route13] + ["/v1/messages/user/:user-id" :test/route14] + ["/v1/users/:user-id/devices" :test/route15] + ["/v1/public/users/:user-id" :test/route16] + ["/v1/orgs/:org-id/errors" :test/route17] + ["/v1/public/orgs/:org-id" :test/route18] + ["/v1/orgs/:org-id/invitations" :test/route19] + ["/v2/public/messages/dataset/bulk" :test/route20] + ["/v1/users/:user-id/devices/bulk" :test/route21] + ["/v1/users/:user-id/device-errors" :test/route22] + ["/v2/login" :test/route23] + ["/v1/users/:user-id/usage-stats" :test/route24] + ["/v2/users/:user-id/devices" :test/route25] + ["/v1/users/:user-id/claim-device/:client-id" :test/route26] + ["/v2/public/projects/:project-id" :test/route27] + ["/v2/public/datasets/:dataset-id" :test/route28] + ["/v2/users/:user-id/topics/bulk" :test/route29] + ["/v1/messages/device/:client-id" :test/route30] + ["/v1/users/:user-id/owned-orgs" :test/route31] + ["/v1/topics/:topic" :test/route32] + ["/v1/users/:user-id/bookmark/:topic" :test/route33] + ["/v1/orgs/:org-id/members/:user-id" :test/route34] + ["/v1/users/:user-id/devices/:client-id" :test/route35] + ["/v1/users/:user-id" :test/route36] + ["/v1/orgs/:org-id/devices" :test/route37] + ["/v1/orgs/:org-id/members" :test/route38] + ["/v1/orgs/:org-id/members/invitation-data/:user-id" :test/route39] + ["/v2/orgs/:org-id/topics" :test/route40] + ["/v1/whoami" :test/route41] + ["/v1/orgs/:org-id" :test/route42] + ["/v1/users/:user-id/api-key" :test/route43] + ["/v2/schemas" :test/route44] + ["/v2/users/:user-id/topics" :test/route45] + ["/v1/orgs/:org-id/confirm-membership/:token" :test/route46] + ["/v2/topics/:topic" :test/route47] + ["/v1/messages/topic/:topic" :test/route48] + ["/v1/users/:user-id/devices/:client-id/reset-password" :test/route49] + ["/v2/topics" :test/route50] + ["/v1/login" :test/route51] + ["/v1/users/:user-id/orgs" :test/route52] + ["/v2/public/messages/dataset/:dataset-id" :test/route53] + ["/v1/topics" :test/route54] + ["/v1/orgs" :test/route55] + ["/v1/users/:user-id/bookmarks" :test/route56] + ["/v1/orgs/:org-id/topics" :test/route57]]) + +(def opensensors-bidi-routes + ["/" {"v1/" {"public/" {["topics/" :topic] :test/route4 + ["users/" :user-id] :test/route16 + ["orgs/" :org-id] :test/route18} + ["users/" :user-id] {["/orgs/" :org-id] :test/route5 + "/invitations" :test/route7 + "/topics" :route9 + "/bookmarks/followers" :test/route10 + "/devices" {"" :route15 + "/bulk" :test/route21 + ["/" :client-id] :test/route35 + ["/" :client-id "/reset-password"] :test/route49} + "/device-errors" :test/route22 + "/usage-stats" :test/route24 + ["/claim-device/" :client-id] :test/route26 + "/owned-orgs" :test/route31 + ["/bookmark/" :topic] :test/route33 + "" :test/route36 + "/orgs" :test/route52 + "/api-key" :test/route43 + "/bookmarks" :test/route56} + ["search/topics/" :term] :test/route6 + "orgs" {"" :test/route55 + ["/" :org-id] {"/devices" {"" :test/route37 + ["/" :device-id] :test/route13 + ["/" :batch "/" :type] :test/route8} + "/usage-stats" :test/route12 + "/invitations" :test/route19 + "/members" {["/" :user-id] :test/route34 + "" :test/route38 + ["/invitation-data/" :user-id] :test/route39} + "/errors" :test/route17 + "" :test/route42 + ["/confirm-membership/" :token] :test/route46 + "/topics" :test/route57}} + "messages/" {["user/" :user-id] :test/route14 + ["device/" :client-id] :test/route30 + ["topic/" :topic] :test/route48} + "topics" {["/" :topic] :test/route32 + "" :test/route54} + "whoami" :test/route41 + "login" :test/route51} + "v2/" {"whoami" :test/route1 + ["users/" :user-id "/"] {"datasets" :test/route2 + "devices" :test/route25 + "topics" {"/bulk" :test/route29 + "" :test/route45}} + "public/" {["projects/" :project-id] {"/datasets" :test/route3 + "" :test/route27} + "messages/dataset/bulk" :test/route20 + ["datasets/" :dataset-id] :test/route28 + ["messages/dataset/" :dataset-id] :test/route53} + ["datasets/" :dataset-id] :test/route11 + "login" :test/route23 + ["orgs/" :org-id "/topics"] :test/route40 + "schemas" :test/route44 + ["topics/" :topic] :test/route47 + "topics" :test/route50}}]) + +(def opensensors-ataraxy-routes + (ataraxy/compile + '{"/v1/" {"public/" {["topics/" topic] [:test/route4 topic] + ["users/" user-id] [:test/route16 user-id] + ["orgs/" org-id] [:test/route18 org-id]} + ["users/" user-id] {["/orgs/" org-id] [:test/route5 user-id org-id] + "/invitations" [:test/route7 user-id] + "/topics" [:route9 user-id] + "/bookmarks/followers" [:test/route10 user-id] + "/devices" {"" [:route15 user-id] + "/bulk" [:test/route21 user-id] + ["/" client-id] [:test/route35 user-id client-id] + ["/" client-id "/reset-password"] [:test/route49 user-id client-id]} + "/device-errors" [:test/route22 user-id] + "/usage-stats" [:test/route24 user-id] + ["/claim-device/" client-id] [:test/route26 user-id client-id] + "/owned-orgs" [:test/route31 user-id] + ["/bookmark/" topic] [:test/route33 user-id topic] + "" [:test/route36 user-id] + "/orgs" [:test/route52 user-id] + "/api-key" [:test/route43 user-id] + "/bookmarks" [:test/route56 user-id]} + ["search/topics/" term] [:test/route6 term] + "orgs" {"" [:test/route55] + ["/" org-id] {"/devices" {"" [:test/route37 org-id] + ["/" device-id] [:test/route13 org-id device-id] + ["/" batch "/" type] [:test/route8 org-id batch type]} + "/usage-stats" [:test/route12 org-id] + "/invitations" [:test/route19 org-id] + "/members" {["/" user-id] [:test/route34 org-id user-id] + "" [:test/route38 org-id] + ["/invitation-data/" user-id] [:test/route39 org-id user-id]} + "/errors" [:test/route17 org-id] + "" [:test/route42 org-id] + ["/confirm-membership/" token] [:test/route46 org-id token] + "/topics" [:test/route57 org-id]}} + "messages/" {["user/" user-id] [:test/route14 user-id] + ["device/" client-id] [:test/route30 client-id] + ["topic/" topic] [:test/route48 topic]} + "topics" {["/" topic] [:test/route32 topic] + "" [:test/route54]} + "whoami" [:test/route41] + "login" [:test/route51]} + "/v2/" {"whoami" [:test/route1] + ["users/" user-id] {"/datasets" [:test/route2 user-id] + "/devices" [:test/route25 user-id] + "topics/" {"bulk" [:test/route29 user-id] + "" [:test/route45 user-id]}} + "public/" {["projects/" project-id] {"/datasets" [:test/route3 project-id] + "" [:test/route27 project-id]} + "messages/dataset/bulk" [:test/route20] + ["datasets/" dataset-id] [:test/route28 dataset-id] + ["messages/dataset/" dataset-id] [:test/route53 dataset-id]} + ["datasets/" dataset-id] [:test/route11 dataset-id] + "login" [:test/route23] + ["orgs/" org-id "/topics"] [:test/route40 org-id] + "schemas" [:test/route44] + ["topics/" topic] [:test/route47 topic] + "topics" [:test/route50]}})) + +(comment + (bench opensensors-routes false) + (bench opensensors-routes true)) + +(comment + (doseq [route (valid-urls (reitit/router opensensors-routes))] + (let [match (bidi/match-route opensensors-bidi-routes route)] + (if-not match + (println route))))) + +(comment + (let [ataraxy-routes (ataraxy/compile opensensors-ataraxy-routes)] + (doseq [route (valid-urls (reitit/router opensensors-routes))] + (let [match (ataraxy/matches ataraxy-routes {:uri route})] + (if-not match + (println route)))))) + +(defn bench! [routes name f] + (System/gc) + (println) + (title name) + (println) + (doseq [[path time] (bench-routes routes f)] + (println (format "%7s" time) "\t" path))) + +(defn bench-all! [] + (let [routes opensensors-routes + router (reitit/router routes) + reitit-f #(reitit/match-by-path router %) + bidi-f #(bidi/match-route opensensors-bidi-routes %) + ataraxy-f #(ataraxy/matches opensensors-ataraxy-routes {:uri %})] + (bench! routes "reitit" reitit-f) + (bench! routes "bidi" bidi-f) + (bench! routes "ataraxy" ataraxy-f))) + +(comment + (bench-all!)) diff --git a/test/clj/reitit/router_test.clj b/test/clj/reitit/router_test.clj deleted file mode 100644 index 8dcc38d4..00000000 --- a/test/clj/reitit/router_test.clj +++ /dev/null @@ -1,132 +0,0 @@ -(ns reitit.router-test - (:require [clojure.test :refer [deftest testing is]] - [cheshire.core :as json] - [clojure.string :as str] - [reitit.core :as reitit])) - -;; -;; extract sample routes -;; - -(defn swagger->routes [url ring?] - (let [route-number (atom 0) - ->route-name #(keyword "test" (str "route" (swap! route-number inc))) - ->endpoint (fn [m] - (if ring? - (reduce-kv - (fn [acc k v] - (assoc acc k {:handler #'identity, :name (->route-name)})) - {} (select-keys m #{:get :head :patch :delete :options :post :put})) - (->route-name)))] - (-> (slurp url) - (json/parse-string true) - (->> :paths - (mapv (fn [[p v]] [(-> p name (str/replace #"\{(.*?)\}" ":$1") (->> (str "/"))) (->endpoint v)])))))) - -(defn valid-urls [router] - (->> - (for [name (reitit/route-names router) - :let [match (reitit/match-by-name router name) - params (if (reitit/partial-match? match) - (-> match :required (zipmap (range))))]] - (:path (reitit/match-by-name router name params))) - (into []))) - -(comment - (swagger->routes "https://api.opensensors.io/doc" false)) - -(defn bench-routes [routes f] - (let [router (reitit/router routes) - urls (valid-urls router) - random-url #(rand-nth urls) - log-time #(let [now (System/nanoTime)] (%) (- (System/nanoTime) now)) - total 10000 - dropped (int (* total 0.45))] - (mapv - #(let [times (->> (range total) - (mapv - (fn [_] - (let [now (System/nanoTime)] - (f %) - (- (System/nanoTime) now)))) - (sort) - (drop dropped) - (drop-last dropped)) - avg (int (/ (reduce + times) (count times)))] - [(-> % f :template) avg]) urls))) - -(defn bench [routes no-paths?] - (let [routes (mapv (fn [[path name]] - (if no-paths? - [(str/replace path #"\:" "") name] - [path name])) routes) - router (reitit/router routes)] - (doseq [[path time] (bench-routes routes #(reitit/match-by-path router %))] - (println path "\t" time)))) - -;; -;; Perf tests -;; - -(def opensensors-routes - [["/v2/whoami" :test/route1] - ["/v2/users/:user-id/datasets" :test/route2] - ["/v2/public/projects/:project-id/datasets" :test/route3] - ["/v1/public/topics/:topic" :test/route4] - ["/v1/users/:user-id/orgs/:org-id" :test/route5] - ["/v1/search/topics/:term" :test/route6] - ["/v1/users/:user-id/invitations" :test/route7] - ["/v1/orgs/:org-id/devices/:batch/:type" :test/route8] - ["/v1/users/:user-id/topics" :test/route9] - ["/v1/users/:user-id/bookmarks/followers" :test/route10] - ["/v2/datasets/:dataset-id" :test/route11] - ["/v1/orgs/:org-id/usage-stats" :test/route12] - ["/v1/orgs/:org-id/devices/:client-id" :test/route13] - ["/v1/messages/user/:user-id" :test/route14] - ["/v1/users/:user-id/devices" :test/route15] - ["/v1/public/users/:user-id" :test/route16] - ["/v1/orgs/:org-id/errors" :test/route17] - ["/v1/public/orgs/:org-id" :test/route18] - ["/v1/orgs/:org-id/invitations" :test/route19] - ["/v2/public/messages/dataset/bulk" :test/route20] - ["/v1/users/:user-id/devices/bulk" :test/route21] - ["/v1/users/:user-id/device-errors" :test/route22] - ["/v2/login" :test/route23] - ["/v1/users/:user-id/usage-stats" :test/route24] - ["/v2/users/:user-id/devices" :test/route25] - ["/v1/users/:user-id/claim-device/:client-id" :test/route26] - ["/v2/public/projects/:project-id" :test/route27] - ["/v2/public/datasets/:dataset-id" :test/route28] - ["/v2/users/:user-id/topics/bulk" :test/route29] - ["/v1/messages/device/:client-id" :test/route30] - ["/v1/users/:user-id/owned-orgs" :test/route31] - ["/v1/topics/:topic" :test/route32] - ["/v1/users/:user-id/bookmark/:topic" :test/route33] - ["/v1/orgs/:org-id/members/:user-id" :test/route34] - ["/v1/users/:user-id/devices/:client-id" :test/route35] - ["/v1/users/:user-id" :test/route36] - ["/v1/orgs/:org-id/devices" :test/route37] - ["/v1/orgs/:org-id/members" :test/route38] - ["/v1/orgs/:org-id/members/invitation-data/:user-id" :test/route39] - ["/v2/orgs/:org-id/topics" :test/route40] - ["/v1/whoami" :test/route41] - ["/v1/orgs/:org-id" :test/route42] - ["/v1/users/:user-id/api-key" :test/route43] - ["/v2/schemas" :test/route44] - ["/v2/users/:user-id/topics" :test/route45] - ["/v1/orgs/:org-id/confirm-membership/:token" :test/route46] - ["/v2/topics/:topic" :test/route47] - ["/v1/messages/topic/:topic" :test/route48] - ["/v1/users/:user-id/devices/:client-id/reset-password" :test/route49] - ["/v2/topics" :test/route50] - ["/v1/login" :test/route51] - ["/v1/users/:user-id/orgs" :test/route52] - ["/v2/public/messages/dataset/:dataset-id" :test/route53] - ["/v1/topics" :test/route54] - ["/v1/orgs" :test/route55] - ["/v1/users/:user-id/bookmarks" :test/route56] - ["/v1/orgs/:org-id/topics" :test/route57]]) - -(comment - (bench opensensors-routes false) - (bench opensensors-routes true)) From 40fcf5761e9d29e75815325547ad033cb3d3f2cb Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sun, 20 Aug 2017 08:09:31 +0300 Subject: [PATCH 05/11] compojure-api routing perf --- .../clj/reitit/opensensors_routing_test.clj | 94 ++++++++++++++++++- 1 file changed, 90 insertions(+), 4 deletions(-) diff --git a/perf-test/clj/reitit/opensensors_routing_test.clj b/perf-test/clj/reitit/opensensors_routing_test.clj index 237c1c50..ff980c11 100644 --- a/perf-test/clj/reitit/opensensors_routing_test.clj +++ b/perf-test/clj/reitit/opensensors_routing_test.clj @@ -4,6 +4,7 @@ [clojure.string :as str] [bidi.bidi :as bidi] [ataraxy.core :as ataraxy] + [compojure.api.sweet :refer [api routes context ANY]] [reitit.core :as reitit])) (defn raw-title [color s] @@ -266,6 +267,83 @@ ["topics/" topic] [:test/route47 topic] "topics" [:test/route50]}})) +(def opensensors-compojure-api-routes + (let [handler (constantly {:status 200, :body ""})] + (routes + (context "/v1" [] + (context "/public" [] + (ANY "/topics/:topic" [] {:name :test/route4} handler) + (ANY "/users/:user-id" [] {:name :test/route16} handler) + (ANY "/orgs/:org-id" [] {:name :test/route18} handler)) + (context "/users/:user-id" [] + (ANY "/orgs/:org-id" [] {:name :test/route5} handler) + (ANY "/invitations" [] {:name :test/route7} handler) + (ANY "/topics" [] {:name :test/route9} handler) + (ANY "/bookmarks/followers" [] {:name :test/route10} handler) + (context "/devices" [] + (ANY "/" [] {:name :test/route15} handler) + (ANY "/bulk" [] {:name :test/route21} handler) + (ANY "/:client-id" [] {:name :test/route35} handler) + (ANY "/:client-id/reset-password" [] {:name :test/route49} handler)) + (ANY "/device-errors" [] {:name :test/route22} handler) + (ANY "/usage-stats" [] {:name :test/route24} handler) + (ANY "/claim-device/:client-id" [] {:name :test/route26} handler) + (ANY "/owned-orgs" [] {:name :test/route31} handler) + (ANY "/bookmark/:topic" [] {:name :test/route33} handler) + (ANY "/" [] {:name :test/route36} handler) + (ANY "/orgs" [] {:name :test/route52} handler) + (ANY "/api-key" [] {:name :test/route43} handler) + (ANY "/bookmarks" [] {:name :test/route56} handler)) + (ANY "/search/topics/:term" [] {:name :test/route6} handler) + (context "/orgs" [] + (ANY "/" [] {:name :test/route55} handler) + (context "/:org-id" [] + (context "/devices" [] + (ANY "/" [] {:name :test/route37} handler) + (ANY "/:device-id" [] {:name :test/route13} handler) + (ANY "/:batch/:type" [] {:name :test/route8} handler)) + (ANY "/usage-stats" [] {:name :test/route12} handler) + (ANY "/invitations" [] {:name :test/route19} handler) + (context "/members" [] + (ANY "/:user-id" [] {:name :test/route34} handler) + (ANY "/" [] {:name :test/route38} handler) + (ANY "/invitation-data/:user-id" [] {:name :test/route39} handler)) + (ANY "/errors" [] {:name :test/route17} handler) + (ANY "/" [] {:name :test/route42} handler) + (ANY "/confirm-membership/:token" [] {:name :test/route46} handler) + (ANY "/topics" [] {:name :test/route57} handler))) + (context "/messages" [] + (ANY "/user/:user-id" [] {:name :test/route14} handler) + (ANY "/device/:client-id" [] {:name :test/route30} handler) + (ANY "/topic/:topic" [] {:name :test/route48} handler)) + (context "/topics" [] + (ANY "/:topic" [] {:name :test/route32} handler) + (ANY "/" [] {:name :test/route54} handler)) + (ANY "/whoami" [] {:name :test/route41} handler) + (ANY "/login" [] {:name :test/route51} handler)) + (context "/v2" [] + (ANY "/whoami" [] {:name :test/route1} handler) + (context "/users/:user-id" [] + (ANY "/datasets" [] {:name :test/route2} handler) + (ANY "/devices" [] {:name :test/route25} handler) + (context "/topics" [] + (ANY "/bulk" [] {:name :test/route29} handler) + (ANY "/" [] {:name :test/route54} handler)) + (ANY "/" [] {:name :test/route45} handler)) + (context "/public" [] + (context "/projects/:project-id" [] + (ANY "/datasets" [] {:name :test/route3} handler) + (ANY "/" [] {:name :test/route27} handler)) + (ANY "/messages/dataset/bulk" [] {:name :test/route20} handler) + (ANY "/datasets/:dataset-id" [] {:name :test/route28} handler) + (ANY "/messages/dataset/:dataset-id" [] {:name :test/route53} handler)) + (ANY "/datasets/:dataset-id" [] {:name :test/route11} handler) + (ANY "/login" [] {:name :test/route23} handler) + (ANY "/orgs/:org-id/topics" [] {:name :test/route40} handler) + (ANY "/schemas" [] {:name :test/route44} handler) + (ANY "/topics/:topic" [] {:name :test/route47} handler) + (ANY "/topics" [] {:name :test/route50} handler))))) + (comment (bench opensensors-routes false) (bench opensensors-routes true)) @@ -283,6 +361,12 @@ (if-not match (println route)))))) +(comment + (doseq [route (valid-urls (reitit/router opensensors-routes))] + (let [match (opensensors-compojure-api-routes {:uri route :request-method :get})] + (if-not match + (println route))))) + (defn bench! [routes name f] (System/gc) (println) @@ -296,10 +380,12 @@ router (reitit/router routes) reitit-f #(reitit/match-by-path router %) bidi-f #(bidi/match-route opensensors-bidi-routes %) - ataraxy-f #(ataraxy/matches opensensors-ataraxy-routes {:uri %})] - (bench! routes "reitit" reitit-f) - (bench! routes "bidi" bidi-f) - (bench! routes "ataraxy" ataraxy-f))) + ataraxy-f #(ataraxy/matches opensensors-ataraxy-routes {:uri %}) + compojure-api-f #(opensensors-compojure-api-routes {:uri % :request-method :get})] + (bench! routes "reitit" reitit-f) ;; 2958ns 11% + (bench! routes "bidi" bidi-f) ;; 18919ns 71% + (bench! routes "ataraxy" ataraxy-f) ;; 26461ns 100% + (bench! routes "compojure-api" compojure-api-f))) ;; 10463ns 40% (comment (bench-all!)) From 2d5e92d116fdd0d8edce4f9adc197bde0e8bc6c0 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sun, 20 Aug 2017 09:52:44 +0300 Subject: [PATCH 06/11] Test also pedestal & ring-routes --- .../clj/reitit/opensensors_routing_test.clj | 425 +++++++++++------- 1 file changed, 270 insertions(+), 155 deletions(-) diff --git a/perf-test/clj/reitit/opensensors_routing_test.clj b/perf-test/clj/reitit/opensensors_routing_test.clj index ff980c11..8d2e55e6 100644 --- a/perf-test/clj/reitit/opensensors_routing_test.clj +++ b/perf-test/clj/reitit/opensensors_routing_test.clj @@ -2,10 +2,19 @@ (:require [clojure.test :refer [deftest testing is]] [cheshire.core :as json] [clojure.string :as str] + [reitit.core :as reitit] + [reitit.ring :as ring] + [bidi.bidi :as bidi] + [ataraxy.core :as ataraxy] + [compojure.api.sweet :refer [api routes context ANY]] - [reitit.core :as reitit])) + + [io.pedestal.http.route.definition.table :as table] + [io.pedestal.http.route.map-tree :as map-tree] + [io.pedestal.http.route.router :as pedestal] + [io.pedestal.http.route :as route])) (defn raw-title [color s] (println (str color (apply str (repeat (count s) "#")) "\u001B[0m")) @@ -89,64 +98,66 @@ ;; Perf tests ;; +(def handler (constantly {:status 200, :body ""})) + (def opensensors-routes - [["/v2/whoami" :test/route1] - ["/v2/users/:user-id/datasets" :test/route2] - ["/v2/public/projects/:project-id/datasets" :test/route3] - ["/v1/public/topics/:topic" :test/route4] - ["/v1/users/:user-id/orgs/:org-id" :test/route5] - ["/v1/search/topics/:term" :test/route6] - ["/v1/users/:user-id/invitations" :test/route7] - ["/v1/orgs/:org-id/devices/:batch/:type" :test/route8] - ["/v1/users/:user-id/topics" :test/route9] - ["/v1/users/:user-id/bookmarks/followers" :test/route10] - ["/v2/datasets/:dataset-id" :test/route11] - ["/v1/orgs/:org-id/usage-stats" :test/route12] - ["/v1/orgs/:org-id/devices/:client-id" :test/route13] - ["/v1/messages/user/:user-id" :test/route14] - ["/v1/users/:user-id/devices" :test/route15] - ["/v1/public/users/:user-id" :test/route16] - ["/v1/orgs/:org-id/errors" :test/route17] - ["/v1/public/orgs/:org-id" :test/route18] - ["/v1/orgs/:org-id/invitations" :test/route19] - ["/v2/public/messages/dataset/bulk" :test/route20] - ["/v1/users/:user-id/devices/bulk" :test/route21] - ["/v1/users/:user-id/device-errors" :test/route22] - ["/v2/login" :test/route23] - ["/v1/users/:user-id/usage-stats" :test/route24] - ["/v2/users/:user-id/devices" :test/route25] - ["/v1/users/:user-id/claim-device/:client-id" :test/route26] - ["/v2/public/projects/:project-id" :test/route27] - ["/v2/public/datasets/:dataset-id" :test/route28] - ["/v2/users/:user-id/topics/bulk" :test/route29] - ["/v1/messages/device/:client-id" :test/route30] - ["/v1/users/:user-id/owned-orgs" :test/route31] - ["/v1/topics/:topic" :test/route32] - ["/v1/users/:user-id/bookmark/:topic" :test/route33] - ["/v1/orgs/:org-id/members/:user-id" :test/route34] - ["/v1/users/:user-id/devices/:client-id" :test/route35] - ["/v1/users/:user-id" :test/route36] - ["/v1/orgs/:org-id/devices" :test/route37] - ["/v1/orgs/:org-id/members" :test/route38] - ["/v1/orgs/:org-id/members/invitation-data/:user-id" :test/route39] - ["/v2/orgs/:org-id/topics" :test/route40] - ["/v1/whoami" :test/route41] - ["/v1/orgs/:org-id" :test/route42] - ["/v1/users/:user-id/api-key" :test/route43] - ["/v2/schemas" :test/route44] - ["/v2/users/:user-id/topics" :test/route45] - ["/v1/orgs/:org-id/confirm-membership/:token" :test/route46] - ["/v2/topics/:topic" :test/route47] - ["/v1/messages/topic/:topic" :test/route48] - ["/v1/users/:user-id/devices/:client-id/reset-password" :test/route49] - ["/v2/topics" :test/route50] - ["/v1/login" :test/route51] - ["/v1/users/:user-id/orgs" :test/route52] - ["/v2/public/messages/dataset/:dataset-id" :test/route53] - ["/v1/topics" :test/route54] - ["/v1/orgs" :test/route55] - ["/v1/users/:user-id/bookmarks" :test/route56] - ["/v1/orgs/:org-id/topics" :test/route57]]) + [["/v2/whoami" {:handler handler, :name :test/route1}] + ["/v2/users/:user-id/datasets" {:handler handler, :name :test/route2}] + ["/v2/public/projects/:project-id/datasets" {:handler handler, :name :test/route3}] + ["/v1/public/topics/:topic" {:handler handler, :name :test/route4}] + ["/v1/users/:user-id/orgs/:org-id" {:handler handler, :name :test/route5}] + ["/v1/search/topics/:term" {:handler handler, :name :test/route6}] + ["/v1/users/:user-id/invitations" {:handler handler, :name :test/route7}] + #_["/v1/orgs/:org-id/devices/:batch/:type" {:handler handler, :name :test/route8}] + ["/v1/users/:user-id/topics" {:handler handler, :name :test/route9}] + ["/v1/users/:user-id/bookmarks/followers" {:handler handler, :name :test/route10}] + ["/v2/datasets/:dataset-id" {:handler handler, :name :test/route11}] + ["/v1/orgs/:org-id/usage-stats" {:handler handler, :name :test/route12}] + ["/v1/orgs/:org-id/devices/:client-id" {:handler handler, :name :test/route13}] + ["/v1/messages/user/:user-id" {:handler handler, :name :test/route14}] + ["/v1/users/:user-id/devices" {:handler handler, :name :test/route15}] + ["/v1/public/users/:user-id" {:handler handler, :name :test/route16}] + ["/v1/orgs/:org-id/errors" {:handler handler, :name :test/route17}] + ["/v1/public/orgs/:org-id" {:handler handler, :name :test/route18}] + ["/v1/orgs/:org-id/invitations" {:handler handler, :name :test/route19}] + ["/v2/public/messages/dataset/bulk" {:handler handler, :name :test/route20}] + #_["/v1/users/:user-id/devices/bulk" {:handler handler, :name :test/route21}] + ["/v1/users/:user-id/device-errors" {:handler handler, :name :test/route22}] + ["/v2/login" {:handler handler, :name :test/route23}] + ["/v1/users/:user-id/usage-stats" {:handler handler, :name :test/route24}] + ["/v2/users/:user-id/devices" {:handler handler, :name :test/route25}] + ["/v1/users/:user-id/claim-device/:client-id" {:handler handler, :name :test/route26}] + ["/v2/public/projects/:project-id" {:handler handler, :name :test/route27}] + ["/v2/public/datasets/:dataset-id" {:handler handler, :name :test/route28}] + ["/v2/users/:user-id/topics/bulk" {:handler handler, :name :test/route29}] + ["/v1/messages/device/:client-id" {:handler handler, :name :test/route30}] + ["/v1/users/:user-id/owned-orgs" {:handler handler, :name :test/route31}] + ["/v1/topics/:topic" {:handler handler, :name :test/route32}] + ["/v1/users/:user-id/bookmark/:topic" {:handler handler, :name :test/route33}] + ["/v1/orgs/:org-id/members/:user-id" {:handler handler, :name :test/route34}] + ["/v1/users/:user-id/devices/:client-id" {:handler handler, :name :test/route35}] + ["/v1/users/:user-id" {:handler handler, :name :test/route36}] + ["/v1/orgs/:org-id/devices" {:handler handler, :name :test/route37}] + ["/v1/orgs/:org-id/members" {:handler handler, :name :test/route38}] + #_["/v1/orgs/:org-id/members/invitation-data/:user-id" {:handler handler, :name :test/route39}] + ["/v2/orgs/:org-id/topics" {:handler handler, :name :test/route40}] + ["/v1/whoami" {:handler handler, :name :test/route41}] + ["/v1/orgs/:org-id" {:handler handler, :name :test/route42}] + ["/v1/users/:user-id/api-key" {:handler handler, :name :test/route43}] + ["/v2/schemas" {:handler handler, :name :test/route44}] + ["/v2/users/:user-id/topics" {:handler handler, :name :test/route45}] + ["/v1/orgs/:org-id/confirm-membership/:token" {:handler handler, :name :test/route46}] + ["/v2/topics/:topic" {:handler handler, :name :test/route47}] + ["/v1/messages/topic/:topic" {:handler handler, :name :test/route48}] + ["/v1/users/:user-id/devices/:client-id/reset-password" {:handler handler, :name :test/route49}] + ["/v2/topics" {:handler handler, :name :test/route50}] + ["/v1/login" {:handler handler, :name :test/route51}] + ["/v1/users/:user-id/orgs" {:handler handler, :name :test/route52}] + ["/v2/public/messages/dataset/:dataset-id" {:handler handler, :name :test/route53}] + ["/v1/topics" {:handler handler, :name :test/route54}] + ["/v1/orgs" {:handler handler, :name :test/route55}] + ["/v1/users/:user-id/bookmarks" {:handler handler, :name :test/route56}] + ["/v1/orgs/:org-id/topics" {:handler handler, :name :test/route57}]]) (def opensensors-bidi-routes ["/" {"v1/" {"public/" {["topics/" :topic] :test/route4 @@ -157,7 +168,7 @@ "/topics" :route9 "/bookmarks/followers" :test/route10 "/devices" {"" :route15 - "/bulk" :test/route21 + #_#_"/bulk" :test/route21 ["/" :client-id] :test/route35 ["/" :client-id "/reset-password"] :test/route49} "/device-errors" :test/route22 @@ -173,12 +184,12 @@ "orgs" {"" :test/route55 ["/" :org-id] {"/devices" {"" :test/route37 ["/" :device-id] :test/route13 - ["/" :batch "/" :type] :test/route8} + #_#_["/" :batch "/" :type] :test/route8} "/usage-stats" :test/route12 "/invitations" :test/route19 "/members" {["/" :user-id] :test/route34 "" :test/route38 - ["/invitation-data/" :user-id] :test/route39} + #_#_["/invitation-data/" :user-id] :test/route39} "/errors" :test/route17 "" :test/route42 ["/confirm-membership/" :token] :test/route46 @@ -217,7 +228,7 @@ "/topics" [:route9 user-id] "/bookmarks/followers" [:test/route10 user-id] "/devices" {"" [:route15 user-id] - "/bulk" [:test/route21 user-id] + #_#_"/bulk" [:test/route21 user-id] ["/" client-id] [:test/route35 user-id client-id] ["/" client-id "/reset-password"] [:test/route49 user-id client-id]} "/device-errors" [:test/route22 user-id] @@ -233,12 +244,12 @@ "orgs" {"" [:test/route55] ["/" org-id] {"/devices" {"" [:test/route37 org-id] ["/" device-id] [:test/route13 org-id device-id] - ["/" batch "/" type] [:test/route8 org-id batch type]} + #_#_["/" batch "/" type] [:test/route8 org-id batch type]} "/usage-stats" [:test/route12 org-id] "/invitations" [:test/route19 org-id] "/members" {["/" user-id] [:test/route34 org-id user-id] "" [:test/route38 org-id] - ["/invitation-data/" user-id] [:test/route39 org-id user-id]} + #_#_["/invitation-data/" user-id] [:test/route39 org-id user-id]} "/errors" [:test/route17 org-id] "" [:test/route42 org-id] ["/confirm-membership/" token] [:test/route46 org-id token] @@ -268,124 +279,228 @@ "topics" [:test/route50]}})) (def opensensors-compojure-api-routes - (let [handler (constantly {:status 200, :body ""})] - (routes - (context "/v1" [] - (context "/public" [] - (ANY "/topics/:topic" [] {:name :test/route4} handler) - (ANY "/users/:user-id" [] {:name :test/route16} handler) - (ANY "/orgs/:org-id" [] {:name :test/route18} handler)) - (context "/users/:user-id" [] - (ANY "/orgs/:org-id" [] {:name :test/route5} handler) - (ANY "/invitations" [] {:name :test/route7} handler) - (ANY "/topics" [] {:name :test/route9} handler) - (ANY "/bookmarks/followers" [] {:name :test/route10} handler) - (context "/devices" [] - (ANY "/" [] {:name :test/route15} handler) - (ANY "/bulk" [] {:name :test/route21} handler) - (ANY "/:client-id" [] {:name :test/route35} handler) - (ANY "/:client-id/reset-password" [] {:name :test/route49} handler)) - (ANY "/device-errors" [] {:name :test/route22} handler) - (ANY "/usage-stats" [] {:name :test/route24} handler) - (ANY "/claim-device/:client-id" [] {:name :test/route26} handler) - (ANY "/owned-orgs" [] {:name :test/route31} handler) - (ANY "/bookmark/:topic" [] {:name :test/route33} handler) - (ANY "/" [] {:name :test/route36} handler) - (ANY "/orgs" [] {:name :test/route52} handler) - (ANY "/api-key" [] {:name :test/route43} handler) - (ANY "/bookmarks" [] {:name :test/route56} handler)) - (ANY "/search/topics/:term" [] {:name :test/route6} handler) - (context "/orgs" [] - (ANY "/" [] {:name :test/route55} handler) - (context "/:org-id" [] - (context "/devices" [] - (ANY "/" [] {:name :test/route37} handler) - (ANY "/:device-id" [] {:name :test/route13} handler) - (ANY "/:batch/:type" [] {:name :test/route8} handler)) - (ANY "/usage-stats" [] {:name :test/route12} handler) - (ANY "/invitations" [] {:name :test/route19} handler) - (context "/members" [] - (ANY "/:user-id" [] {:name :test/route34} handler) - (ANY "/" [] {:name :test/route38} handler) - (ANY "/invitation-data/:user-id" [] {:name :test/route39} handler)) - (ANY "/errors" [] {:name :test/route17} handler) - (ANY "/" [] {:name :test/route42} handler) - (ANY "/confirm-membership/:token" [] {:name :test/route46} handler) - (ANY "/topics" [] {:name :test/route57} handler))) - (context "/messages" [] - (ANY "/user/:user-id" [] {:name :test/route14} handler) - (ANY "/device/:client-id" [] {:name :test/route30} handler) - (ANY "/topic/:topic" [] {:name :test/route48} handler)) - (context "/topics" [] - (ANY "/:topic" [] {:name :test/route32} handler) - (ANY "/" [] {:name :test/route54} handler)) - (ANY "/whoami" [] {:name :test/route41} handler) - (ANY "/login" [] {:name :test/route51} handler)) - (context "/v2" [] - (ANY "/whoami" [] {:name :test/route1} handler) - (context "/users/:user-id" [] - (ANY "/datasets" [] {:name :test/route2} handler) - (ANY "/devices" [] {:name :test/route25} handler) - (context "/topics" [] - (ANY "/bulk" [] {:name :test/route29} handler) - (ANY "/" [] {:name :test/route54} handler)) - (ANY "/" [] {:name :test/route45} handler)) - (context "/public" [] - (context "/projects/:project-id" [] - (ANY "/datasets" [] {:name :test/route3} handler) - (ANY "/" [] {:name :test/route27} handler)) - (ANY "/messages/dataset/bulk" [] {:name :test/route20} handler) - (ANY "/datasets/:dataset-id" [] {:name :test/route28} handler) - (ANY "/messages/dataset/:dataset-id" [] {:name :test/route53} handler)) - (ANY "/datasets/:dataset-id" [] {:name :test/route11} handler) - (ANY "/login" [] {:name :test/route23} handler) - (ANY "/orgs/:org-id/topics" [] {:name :test/route40} handler) - (ANY "/schemas" [] {:name :test/route44} handler) - (ANY "/topics/:topic" [] {:name :test/route47} handler) - (ANY "/topics" [] {:name :test/route50} handler))))) + (routes + (context "/v1" [] + (context "/public" [] + (ANY "/topics/:topic" [] {:name :test/route4} handler) + (ANY "/users/:user-id" [] {:name :test/route16} handler) + (ANY "/orgs/:org-id" [] {:name :test/route18} handler)) + (context "/users/:user-id" [] + (ANY "/orgs/:org-id" [] {:name :test/route5} handler) + (ANY "/invitations" [] {:name :test/route7} handler) + (ANY "/topics" [] {:name :test/route9} handler) + (ANY "/bookmarks/followers" [] {:name :test/route10} handler) + (context "/devices" [] + (ANY "/" [] {:name :test/route15} handler) + #_(ANY "/bulk" [] {:name :test/route21} handler) + (ANY "/:client-id" [] {:name :test/route35} handler) + (ANY "/:client-id/reset-password" [] {:name :test/route49} handler)) + (ANY "/device-errors" [] {:name :test/route22} handler) + (ANY "/usage-stats" [] {:name :test/route24} handler) + (ANY "/claim-device/:client-id" [] {:name :test/route26} handler) + (ANY "/owned-orgs" [] {:name :test/route31} handler) + (ANY "/bookmark/:topic" [] {:name :test/route33} handler) + (ANY "/" [] {:name :test/route36} handler) + (ANY "/orgs" [] {:name :test/route52} handler) + (ANY "/api-key" [] {:name :test/route43} handler) + (ANY "/bookmarks" [] {:name :test/route56} handler)) + (ANY "/search/topics/:term" [] {:name :test/route6} handler) + (context "/orgs" [] + (ANY "/" [] {:name :test/route55} handler) + (context "/:org-id" [] + (context "/devices" [] + (ANY "/" [] {:name :test/route37} handler) + (ANY "/:device-id" [] {:name :test/route13} handler) + #_(ANY "/:batch/:type" [] {:name :test/route8} handler)) + (ANY "/usage-stats" [] {:name :test/route12} handler) + (ANY "/invitations" [] {:name :test/route19} handler) + (context "/members" [] + (ANY "/:user-id" [] {:name :test/route34} handler) + (ANY "/" [] {:name :test/route38} handler) + #_(ANY "/invitation-data/:user-id" [] {:name :test/route39} handler)) + (ANY "/errors" [] {:name :test/route17} handler) + (ANY "/" [] {:name :test/route42} handler) + (ANY "/confirm-membership/:token" [] {:name :test/route46} handler) + (ANY "/topics" [] {:name :test/route57} handler))) + (context "/messages" [] + (ANY "/user/:user-id" [] {:name :test/route14} handler) + (ANY "/device/:client-id" [] {:name :test/route30} handler) + (ANY "/topic/:topic" [] {:name :test/route48} handler)) + (context "/topics" [] + (ANY "/:topic" [] {:name :test/route32} handler) + (ANY "/" [] {:name :test/route54} handler)) + (ANY "/whoami" [] {:name :test/route41} handler) + (ANY "/login" [] {:name :test/route51} handler)) + (context "/v2" [] + (ANY "/whoami" [] {:name :test/route1} handler) + (context "/users/:user-id" [] + (ANY "/datasets" [] {:name :test/route2} handler) + (ANY "/devices" [] {:name :test/route25} handler) + (context "/topics" [] + (ANY "/bulk" [] {:name :test/route29} handler) + (ANY "/" [] {:name :test/route54} handler)) + (ANY "/" [] {:name :test/route45} handler)) + (context "/public" [] + (context "/projects/:project-id" [] + (ANY "/datasets" [] {:name :test/route3} handler) + (ANY "/" [] {:name :test/route27} handler)) + (ANY "/messages/dataset/bulk" [] {:name :test/route20} handler) + (ANY "/datasets/:dataset-id" [] {:name :test/route28} handler) + (ANY "/messages/dataset/:dataset-id" [] {:name :test/route53} handler)) + (ANY "/datasets/:dataset-id" [] {:name :test/route11} handler) + (ANY "/login" [] {:name :test/route23} handler) + (ANY "/orgs/:org-id/topics" [] {:name :test/route40} handler) + (ANY "/schemas" [] {:name :test/route44} handler) + (ANY "/topics/:topic" [] {:name :test/route47} handler) + (ANY "/topics" [] {:name :test/route50} handler)))) + +(def opensensors-pedestal-routes + (map-tree/router + (table/table-routes + [["/v2/whoami" :get handler :route-name :test/route1] + ["/v2/users/:user-id/datasets" :get handler :route-name :test/route2] + ["/v2/public/projects/:project-id/datasets" :get handler :route-name :test/route3] + ["/v1/public/topics/:topic" :get handler :route-name :test/route4] + ["/v1/users/:user-id/orgs/:org-id" :get handler :route-name :test/route5] + ["/v1/search/topics/:term" :get handler :route-name :test/route6] + ["/v1/users/:user-id/invitations" :get handler :route-name :test/route7] + #_["/v1/orgs/:org-id/devices/:batch/:type" :get handler :route-name :test/route8] + ["/v1/users/:user-id/topics" :get handler :route-name :test/route9] + ["/v1/users/:user-id/bookmarks/followers" :get handler :route-name :test/route10] + ["/v2/datasets/:dataset-id" :get handler :route-name :test/route11] + ["/v1/orgs/:org-id/usage-stats" :get handler :route-name :test/route12] + ["/v1/orgs/:org-id/devices/:client-id" :get handler :route-name :test/route13] + ["/v1/messages/user/:user-id" :get handler :route-name :test/route14] + ["/v1/users/:user-id/devices" :get handler :route-name :test/route15] + ["/v1/public/users/:user-id" :get handler :route-name :test/route16] + ["/v1/orgs/:org-id/errors" :get handler :route-name :test/route17] + ["/v1/public/orgs/:org-id" :get handler :route-name :test/route18] + ["/v1/orgs/:org-id/invitations" :get handler :route-name :test/route19] + ["/v2/public/messages/dataset/bulk" :get handler :route-name :test/route20] + #_["/v1/users/:user-id/devices/bulk" :get handler :route-name :test/route21] + ["/v1/users/:user-id/device-errors" :get handler :route-name :test/route22] + ["/v2/login" :get handler :route-name :test/route23] + ["/v1/users/:user-id/usage-stats" :get handler :route-name :test/route24] + ["/v2/users/:user-id/devices" :get handler :route-name :test/route25] + ["/v1/users/:user-id/claim-device/:client-id" :get handler :route-name :test/route26] + ["/v2/public/projects/:project-id" :get handler :route-name :test/route27] + ["/v2/public/datasets/:dataset-id" :get handler :route-name :test/route28] + ["/v2/users/:user-id/topics/bulk" :get handler :route-name :test/route29] + ["/v1/messages/device/:client-id" :get handler :route-name :test/route30] + ["/v1/users/:user-id/owned-orgs" :get handler :route-name :test/route31] + ["/v1/topics/:topic" :get handler :route-name :test/route32] + ["/v1/users/:user-id/bookmark/:topic" :get handler :route-name :test/route33] + ["/v1/orgs/:org-id/members/:user-id" :get handler :route-name :test/route34] + ["/v1/users/:user-id/devices/:client-id" :get handler :route-name :test/route35] + ["/v1/users/:user-id" :get handler :route-name :test/route36] + ["/v1/orgs/:org-id/devices" :get handler :route-name :test/route37] + ["/v1/orgs/:org-id/members" :get handler :route-name :test/route38] + #_["/v1/orgs/:org-id/members/invitation-data/:user-id" :get handler :route-name :test/route39] + ["/v2/orgs/:org-id/topics" :get handler :route-name :test/route40] + ["/v1/whoami" :get handler :route-name :test/route41] + ["/v1/orgs/:org-id" :get handler :route-name :test/route42] + ["/v1/users/:user-id/api-key" :get handler :route-name :test/route43] + ["/v2/schemas" :get handler :route-name :test/route44] + ["/v2/users/:user-id/topics" :get handler :route-name :test/route45] + ["/v1/orgs/:org-id/confirm-membership/:token" :get handler :route-name :test/route46] + ["/v2/topics/:topic" :get handler :route-name :test/route47] + ["/v1/messages/topic/:topic" :get handler :route-name :test/route48] + ["/v1/users/:user-id/devices/:client-id/reset-password" :get handler :route-name :test/route49] + ["/v2/topics" :get handler :route-name :test/route50] + ["/v1/login" :get handler :route-name :test/route51] + ["/v1/users/:user-id/orgs" :get handler :route-name :test/route52] + ["/v2/public/messages/dataset/:dataset-id" :get handler :route-name :test/route53] + ["/v1/topics" :get handler :route-name :test/route54] + ["/v1/orgs" :get handler :route-name :test/route55] + ["/v1/users/:user-id/bookmarks" :get handler :route-name :test/route56] + ["/v1/orgs/:org-id/topics" :get handler :route-name :test/route57]]))) + +(comment + (pedestal/find-route + (map-tree/router + (table/table-routes + [["/v1/orgs/:org-id/members/:user-id" :get (constantly "") :route-name :test/route34] + ["/v1/orgs/:org-id/members/invitation-data/:user-id" :get (constantly "") :route-name :test/route39]])) + {:path-info "/v1/orgs/0/members/invitation-data/1" :request-method :get}) + + (require '[io.pedestal.http.route.definition.table :as table]) + (require '[io.pedestal.http.route.map-tree :as map-tree]) + (require '[io.pedestal.http.route.router :as pedestal]) + + (pedestal/find-route + (map-tree/router + (table/table-routes + [["/:a" :get (constantly "") :route-name ::ping] + ["/evil/ping" :get (constantly "") :route-name ::evil-ping]])) + {:path-info "/evil/ping" :request-method :get})) + +(doseq [route (valid-urls (reitit/router opensensors-routes))] + (let [match (pedestal/find-route opensensors-pedestal-routes {:path-info route :request-method :get})] + (if-not match + (println route)))) (comment (bench opensensors-routes false) (bench opensensors-routes true)) (comment + + (doseq [route (valid-urls (reitit/router opensensors-routes))] + (let [app (ring/ring-handler (reitit/router opensensors-routes)) + match (app {:uri route :request-method :get})] + (if-not match + (println route)))) + (doseq [route (valid-urls (reitit/router opensensors-routes))] (let [match (bidi/match-route opensensors-bidi-routes route)] (if-not match - (println route))))) + (println route)))) -(comment - (let [ataraxy-routes (ataraxy/compile opensensors-ataraxy-routes)] - (doseq [route (valid-urls (reitit/router opensensors-routes))] - (let [match (ataraxy/matches ataraxy-routes {:uri route})] - (if-not match - (println route)))))) + (doseq [route (valid-urls (reitit/router opensensors-routes))] + (let [match (ataraxy/matches opensensors-ataraxy-routes {:uri route})] + (if-not match + (println route)))) -(comment (doseq [route (valid-urls (reitit/router opensensors-routes))] (let [match (opensensors-compojure-api-routes {:uri route :request-method :get})] + (if-not match + (println route)))) + + (doseq [route (valid-urls (reitit/router opensensors-routes))] + (let [match (pedestal/find-route opensensors-pedestal-routes {:path-info route :request-method :get})] (if-not match (println route))))) (defn bench! [routes name f] (System/gc) (println) - (title name) + (suite name) (println) - (doseq [[path time] (bench-routes routes f)] - (println (format "%7s" time) "\t" path))) + (let [times (for [[path time] (bench-routes routes f)] + (do + (println (format "%7s" time) "\t" path) + time))] + (title (str "average: " (int (/ (reduce + times) (count times))))))) (defn bench-all! [] (let [routes opensensors-routes router (reitit/router routes) reitit-f #(reitit/match-by-path router %) + reitit-ring-f (let [app (ring/ring-handler (reitit/router opensensors-routes))] + #(app {:uri % :request-method :get})) bidi-f #(bidi/match-route opensensors-bidi-routes %) ataraxy-f #(ataraxy/matches opensensors-ataraxy-routes {:uri %}) - compojure-api-f #(opensensors-compojure-api-routes {:uri % :request-method :get})] - (bench! routes "reitit" reitit-f) ;; 2958ns 11% - (bench! routes "bidi" bidi-f) ;; 18919ns 71% - (bench! routes "ataraxy" ataraxy-f) ;; 26461ns 100% - (bench! routes "compojure-api" compojure-api-f))) ;; 10463ns 40% + compojure-api-f #(opensensors-compojure-api-routes {:uri % :request-method :get}) + pedestal-f #(pedestal/find-route opensensors-pedestal-routes {:path-info % :request-method :get})] + + (bench! routes "reitit" reitit-f) ;; 2538ns 10% + (bench! routes "pedestal" pedestal-f) ;; 2737ns 11% + (bench! routes "reitit-ring" reitit-ring-f) ;; 2845ns 11% + (bench! routes "compojure-api" compojure-api-f) ;; 10215ns 41% + (bench! routes "bidi" bidi-f) ;; 19298ns 77% + (bench! routes "ataraxy" ataraxy-f) ;; 24950ns 100% + + )) (comment (bench-all!)) From 1ea6b558490134d47f1b9a613934b0a4828fea72 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sun, 20 Aug 2017 19:14:49 +0300 Subject: [PATCH 07/11] Testing LookupRouter perf, +20% with fast-assoc --- .../clj/reitit/opensensors_routing_test.clj | 571 +++++++++++++++++- src/reitit/impl.cljc | 4 + src/reitit/ring.cljc | 14 +- test/cljc/reitit/core_test.cljc | 8 +- 4 files changed, 573 insertions(+), 24 deletions(-) diff --git a/perf-test/clj/reitit/opensensors_routing_test.clj b/perf-test/clj/reitit/opensensors_routing_test.clj index 8d2e55e6..2d204506 100644 --- a/perf-test/clj/reitit/opensensors_routing_test.clj +++ b/perf-test/clj/reitit/opensensors_routing_test.clj @@ -98,7 +98,7 @@ ;; Perf tests ;; -(def handler (constantly {:status 200, :body ""})) +(def handler (constantly {:status 200, :body "ok"})) (def opensensors-routes [["/v2/whoami" {:handler handler, :name :test/route1}] @@ -446,7 +446,7 @@ (comment (doseq [route (valid-urls (reitit/router opensensors-routes))] - (let [app (ring/ring-handler (reitit/router opensensors-routes)) + (let [app (ring/ring-handler (ring/router opensensors-routes)) match (app {:uri route :request-method :get})] (if-not match (println route)))) @@ -471,36 +471,581 @@ (if-not match (println route))))) -(defn bench! [routes name f] +(defn bench! [routes verbose? name f] (System/gc) (println) (suite name) (println) (let [times (for [[path time] (bench-routes routes f)] (do - (println (format "%7s" time) "\t" path) + (when verbose? (println (format "%7s" time) "\t" path)) time))] (title (str "average: " (int (/ (reduce + times) (count times))))))) -(defn bench-all! [] +(defn bench-rest! [] (let [routes opensensors-routes router (reitit/router routes) reitit-f #(reitit/match-by-path router %) - reitit-ring-f (let [app (ring/ring-handler (reitit/router opensensors-routes))] + reitit-ring-f (let [app (ring/ring-handler (ring/router opensensors-routes))] #(app {:uri % :request-method :get})) bidi-f #(bidi/match-route opensensors-bidi-routes %) ataraxy-f #(ataraxy/matches opensensors-ataraxy-routes {:uri %}) compojure-api-f #(opensensors-compojure-api-routes {:uri % :request-method :get}) pedestal-f #(pedestal/find-route opensensors-pedestal-routes {:path-info % :request-method :get})] - (bench! routes "reitit" reitit-f) ;; 2538ns 10% - (bench! routes "pedestal" pedestal-f) ;; 2737ns 11% - (bench! routes "reitit-ring" reitit-ring-f) ;; 2845ns 11% - (bench! routes "compojure-api" compojure-api-f) ;; 10215ns 41% - (bench! routes "bidi" bidi-f) ;; 19298ns 77% - (bench! routes "ataraxy" ataraxy-f) ;; 24950ns 100% + (bench! routes true "reitit" reitit-f) ;; 2538ns 10% + (bench! routes true "pedestal" pedestal-f) ;; 2737ns 11% + (bench! routes true "reitit-ring" reitit-ring-f) ;; 2845ns 11% + (bench! routes true "compojure-api" compojure-api-f) ;; 10215ns 41% + (bench! routes true "bidi" bidi-f) ;; 19298ns 77% + (bench! routes true "ataraxy" ataraxy-f) ;; 24950ns 100% )) (comment - (bench-all!)) + (bench-rest!)) + +;; +;; CQRSish +;; + +(def commands + #{:upsert-appeal + :upsert-appeal-verdict + :delete-appeal + :delete-appeal-verdict + :mark-seen + :mark-everything-seen + :upsert-application-handler + :remove-application-handler + :cancel-inforequest + :cancel-application + :cancel-application-authority + :undo-cancellation + :request-for-complement + :cleanup-krysp + :submit-application + :refresh-ktj + :save-application-drawings + :create-application + :add-operation + :update-op-description + :change-primary-operation + :change-permit-sub-type + :change-location + :change-application-state + :return-to-draft + :change-warranty-start-date + :change-warranty-end-date + :add-link-permit + :remove-link-permit-by-app-id + :create-change-permit + :create-continuation-period-permit + :convert-to-application + :add-bulletin-comment + :move-to-proclaimed + :move-to-verdict-given + :move-to-final + :save-proclaimed-bulletin + :save-verdict-given-bulletin + :set-municipality-hears-neighbors + :archive-documents + :mark-pre-verdict-phase-archived + :save-asianhallinta-config + :create-assignment + :update-assignment + :complete-assignment + :bind-attachment + :bind-attachments + :set-attachment-type + :approve-attachment + :reject-attachment + :reject-attachment-note + :create-attachments + :create-ram-attachment + :delete-attachment + :delete-attachment-version + :upload-attachment + :rotate-pdf + :upsert-stamp-template + :delete-stamp-template + :stamp-attachments + :sign-attachments + :set-attachment-meta + :set-attachment-not-needed + :set-attachments-as-verdict-attachment + :set-attachment-as-construction-time + :set-attachment-visibility + :convert-to-pdfa + :invite-with-role + :approve-invite + :decline-invitation + :remove-auth + :change-auth + :unsubscribe-notifications + :subscribe-notifications + :set-calendar-enabled-for-authority + :create-calendar-slots + :update-calendar-slot + :delete-calendar-slot + :add-reservation-type-for-organization + :update-reservation-type + :delete-reservation-type + :reserve-calendar-slot + :accept-reservation + :decline-reservation + :cancel-reservation + :mark-reservation-update-seen + :add-campaign + :delete-campaign + :change-email-init + :change-email + :can-target-comment-to-authority + :can-mark-answered + :add-comment + :company-update + :company-lock + :company-user-update + :company-user-delete + :company-user-delete-all + :company-invite-user + :company-add-user + :company-invite + :company-cancel-invite + :save-company-tags + :update-application-company-notes + :inform-construction-started + :inform-construction-ready + :copy-application + :update-3d-map-server-details + :set-3d-map-enabled + :redirect-to-3d-map + :create-archiving-project + :submit-archiving-project + :create-doc + :remove-doc + :set-doc-status + :update-doc + :update-task + :remove-document-data + :approve-doc + :reject-doc + :reject-doc-note + :set-user-to-document + :set-current-user-to-document + :set-company-to-document + :set-feature + :remove-uploaded-file + :create-foreman-application + :update-foreman-other-applications + :link-foreman-task + :update-guest-authority-organization + :remove-guest-authority-organization + :invite-guest + :toggle-guest-subscription + :delete-guest-application + :info-link-delete + :info-link-reorder + :info-link-upsert + :mark-seen-organization-links + :create-inspection-summary-template + :delete-inspection-summary-template + :modify-inspection-summary-template + :set-inspection-summary-template-for-operation + :create-inspection-summary + :delete-inspection-summary + :toggle-inspection-summary-locking + :add-target-to-inspection-summary + :edit-inspection-summary-target + :remove-target-from-inspection-summary + :set-target-status + :set-inspection-date + :approve-application + :move-attachments-to-backing-system + :parties-as-krysp + :merge-details-from-krysp + :application-to-asianhallinta + :attachments-to-asianhallinta + :order-verdict-attachment-prints + :frontend-log + :reset-frontend-log + :new-verdict-template + :set-verdict-template-name + :save-verdict-template-draft-value + :publish-verdict-template + :toggle-delete-verdict-template + :copy-verdict-template + :save-verdict-template-settings-value + :add-verdict-template-review + :update-verdict-template-review + :add-verdict-template-plan + :update-verdict-template-plan + :set-default-operation-verdict-template + :upsert-phrase + :delete-phrase + :neighbor-add + :neighbor-add-owners + :neighbor-update + :neighbor-remove + :neighbor-send-invite + :neighbor-mark-done + :neighbor-response + :change-urgency + :add-authority-notice + :add-application-tags + :init-sign + :cancel-sign + :convert-to-normal-inforequests + :update-organization + :add-scope + :create-organization + :add-organization-link + :update-organization-link + :remove-organization-link + :update-allowed-autologin-ips + :set-organization-selected-operations + :organization-operations-attachments + :set-organization-app-required-fields-filling-obligatory + :set-automatic-ok-for-attachments + :set-organization-assignments + :set-organization-inspection-summaries + :set-organization-extended-construction-waste-report + :set-organization-validate-verdict-given-date + :set-organization-use-attachment-links-integration + :set-organization-calendars-enabled + :set-organization-boolean-attribute + :set-organization-permanent-archive-start-date + :set-organization-neighbor-order-email + :set-organization-submit-notification-email + :set-organization-inforequest-notification-email + :set-organization-default-reservation-location + :set-krysp-endpoint + :set-kopiolaitos-info + :save-vendor-backend-redirect-config + :update-organization-name + :save-organization-tags + :update-map-server-details + :update-user-layers + :update-suti-server-details + :section-toggle-enabled + :section-toggle-operation + :upsert-handler-role + :toggle-handler-role + :upsert-assignment-trigger + :remove-assignment-trigger + :update-docstore-info + :browser-timing + :create-application-from-previous-permit + :screenmessages-add + :screenmessages-reset + :add-single-sign-on-key + :update-single-sign-on-key + :remove-single-sign-on-key + :create-statement-giver + :delete-statement-giver + :request-for-statement + :ely-statement-request + :delete-statement + :save-statement-as-draft + :give-statement + :request-for-statement-reply + :save-statement-reply-as-draft + :reply-statement + :suti-toggle-enabled + :suti-toggle-operation + :suti-www + :suti-update-id + :suti-update-added + :create-task + :delete-task + :approve-task + :reject-task + :review-done + :mark-review-faulty + :resend-review-to-backing-system + :set-tos-function-for-operation + :remove-tos-function-from-operation + :set-tos-function-for-application + :force-fix-tos-function-for-application + :store-tos-metadata-for-attachment + :store-tos-metadata-for-application + :store-tos-metadata-for-process + :set-myyntipalvelu-for-attachment + :create-user + :create-rest-api-user + :update-user + :applicant-to-authority + :update-default-application-filter + :save-application-filter + :remove-application-filter + :update-user-organization + :remove-user-organization + :update-user-roles + :check-password + :change-passwd + :reset-password + :admin-reset-password + :set-user-enabled + :login + :impersonate-authority + :register-user + :confirm-account-link + :retry-rakentajafi + :remove-user-attachment + :copy-user-attachments-to-application + :remove-user-notification + :notifications-update + :check-for-verdict + :new-verdict-draft + :save-verdict-draft + :publish-verdict + :delete-verdict + :sign-verdict + :create-digging-permit}) + +(def queries + #{:comments + :actions + :allowed-actions + :allowed-actions-for-category + :admin-attachment-report + :appeals + :application + :application-authorities + :application-commenters + :enable-accordions + :party-document-names + :application-submittable + :inforequest-markers + :change-application-state-targets + :link-permit-required + :app-matches-for-link-permits + :all-operations-in + :application-handlers + :application-organization-handler-roles + :application-organization-archive-enabled + :application-bulletins + :application-bulletin-municipalities + :application-bulletin-states + :bulletin + :bulletin-versions + :bulletin-comments + :publish-bulletin-enabled + :municipality-hears-neighbors-visible + :applications-search + :applications-search-default + :applications-for-new-appointment-page + :get-application-operations + :applications + :latest-applications + :event-search + :tasks-tab-visible + :application-info-tab-visible + :application-summary-tab-visible + :application-verdict-tab-visible + :document-states + :archiving-operations-enabled + :permanent-archive-enabled + :application-in-final-archiving-state + :asianhallinta-config + :assignments-for-application + :assignment-targets + :assignments-search + :assignment-count + :assignments + :assignment + :bind-attachments-job + :attachments + :attachment + :attachment-groups + :attachments-filters + :attachments-tag-groups + :attachment-types + :ram-linked-attachments + :attachment-operations + :stamp-templates + :custom-stamps + :stamp-attachments-job + :signing-possible + :set-attachment-group-enabled + :invites + :my-calendars + :calendar + :calendars-for-authority-admin + :calendar-slots + :reservation-types-for-organization + :available-calendar-slots + :application-calendar-config + :calendar-actions-required + :applications-with-appointments + :my-reserved-slots + :campaigns + :campaign + :company + :company-users-for-person-selector + :company-tags + :companies + :user-company-locked + :company-search-user + :remove-company-tag-ok + :company-notes + :enable-company-search + :info-construction-status + :copy-application-invite-candidates + :application-copyable-to-location + :application-copyable + :source-application + :user-is-pure-digitizer + :digitizing-enabled + :document + :validate-doc + :fetch-validation-errors + :schemas + :features + :apply-fixture + :foreman-history + :foreman-applications + :resolve-guest-authority-candidate + :guest-authorities-organization + :application-guests + :guest-authorities-application-organization + :get-link-account-token + :info-links + :organization-links + :organization-inspection-summary-settings + :inspection-summaries-for-application + :get-building-info-from-wfs + :external-api-enabled + :integration-messages + :ely-statement-types + :frontend-log-entries + :newest-version + :verdict-templates + :verdict-template-categories + :verdict-template + :verdict-template-settings + :verdict-template-reviews + :verdict-template-plans + :default-operation-verdict-templates + :organization-phrases + :application-phrases + :owners + :application-property-owners + :municipality-borders + :active-municipalities + :municipality-active + :neighbor-application + :authority-notice + :find-sign-process + :organization-by-user + :all-attachment-types-by-user + :organization-name-by-user + :user-organizations-for-permit-type + :user-organizations-for-archiving-project + :organizations + :allowed-autologin-ips-for-organization + :organization-by-id + :permit-types + :municipalities-with-organization + :municipalities + :all-operations-for-organization + :selected-operations-for-municipality + :addable-operations + :organization-details + :krysp-config + :kopiolaitos-config + :get-organization-names + :vendor-backend-redirect-config + :remove-tag-ok + :get-organization-tags + :get-organization-areas + :get-map-layers-data + :municipality-for-property + :property-borders + :screenmessages + :get-single-sign-on-keys + :get-organizations-statement-givers + :get-possible-statement-statuses + :get-statement-givers + :statement-replies-enabled + :statement-is-replyable + :authorized-for-requesting-statement-reply + :statement-attachment-allowed + :statements-after-approve-allowed + :neighbors-statement-enabled + :suti-admin-details + :suti-operations + :suti-application-data + :suti-application-products + :suti-pre-sent-state + :task-types-for-application + :review-can-be-marked-done + :is-end-review + :available-tos-functions + :tos-metadata-schema + :case-file-data + :tos-operations-enabled + :common-area-application + :user + :users + :users-in-same-organizations + :user-by-email + :users-for-datatables + :saved-application-filters + :redirect-after-login + :user-attachments + :add-user-attachment-allowed + :email-in-use + :enable-foreman-search + :calendars-enabled + :verdict-attachment-type + :selected-digging-operations-for-organization + :ya-extensions + :approve-ya-extension}) + +(def cqrs-routes + (mapv (fn [command] [(str "/command/" (name command)) {:post handler :name command}]) commands)) + +(def cqrs-routes-pedestal + (map-tree/router + (table/table-routes + (mapv (fn [command] [(str "/command/" (name command)) :post handler :route-name command]) commands)))) + +(class (:tree-map cqrs-routes-pedestal)) + +(class (:data (ring/router cqrs-routes))) + +(comment + + (doseq [route (valid-urls (reitit/router cqrs-routes))] + (let [app (ring/ring-handler (ring/router cqrs-routes)) + match (app {:uri route :request-method :post})] + (if-not match + (println route)))) + + (doseq [route (valid-urls (reitit/router cqrs-routes))] + (let [match (pedestal/find-route cqrs-routes-pedestal {:path-info route :request-method :post})] + (if-not match + (println route))))) + +(defn bench-cqrs! [] + (let [routes cqrs-routes + router (reitit/router cqrs-routes) + reitit-f #(reitit/match-by-path router %) + reitit-ring-f (let [app (ring/ring-handler (ring/router routes))] + #(app {:uri % :request-method :post})) + pedestal-f #(pedestal/find-route cqrs-routes-pedestal {:path-info % :request-method :post})] + + ;; 125ns + (bench! routes false "reitit" reitit-f) + + ;; 272ns + ;; 219ns (fast-assoc) + (bench! routes false "reitit-ring" reitit-ring-f) + + ;; 172ns + (bench! routes false "pedestal" pedestal-f))) + +(comment + (bench-cqrs!)) + diff --git a/src/reitit/impl.cljc b/src/reitit/impl.cljc index cd6b5d69..fcae9e7b 100644 --- a/src/reitit/impl.cljc +++ b/src/reitit/impl.cljc @@ -135,3 +135,7 @@ (ex-info (str "missing path-params for route " template ": " missing) {:params params, :required required}))))) + +(defn fast-assoc + #?@(:clj [[^clojure.lang.Associative a k v] (.assoc a k v)] + :cljs [[a k v] (assoc a k v)])) diff --git a/src/reitit/ring.cljc b/src/reitit/ring.cljc index 98b52e9a..2badd934 100644 --- a/src/reitit/ring.cljc +++ b/src/reitit/ring.cljc @@ -1,7 +1,8 @@ (ns reitit.ring (:require [meta-merge.core :refer [meta-merge]] [reitit.middleware :as middleware] - [reitit.core :as reitit])) + [reitit.core :as reitit] + [reitit.impl :as impl])) (def http-methods #{:get :head :patch :delete :options :post :put}) (defrecord MethodHandlers [get head patch delete options post put]) @@ -18,10 +19,10 @@ (fn ([request] (if-let [match (reitit/match-by-path router (:uri request))] - ((:handler match) (assoc request ::match match)))) + ((:handler match) (impl/fast-assoc request ::match match)))) ([request respond raise] (if-let [match (reitit/match-by-path router (:uri request))] - ((:handler match) (assoc request ::match match) respond raise)))) + ((:handler match) (impl/fast-assoc request ::match match) respond raise)))) {::router router})) (defn get-router [handler] @@ -46,14 +47,13 @@ #(assoc %1 %2 (middleware/compile-handler [path (meta-merge top %3)] opts %2)) {} childs)) - default-handler (if (:handler top) (middleware/compile-handler [path meta] opts)) - resolved-handler #(or (% handlers) default-handler)] + default-handler (if (:handler top) (middleware/compile-handler [path meta] opts))] (fn ([request] - (if-let [handler (resolved-handler (:request-method request))] + (if-let [handler (or ((:request-method request) handlers) default-handler)] (handler request))) ([request respond raise] - (if-let [handler (resolved-handler (:request-method request))] + (if-let [handler (or ((:request-method request) handlers) default-handler)] (handler request respond raise)))))))) (defn router diff --git a/test/cljc/reitit/core_test.cljc b/test/cljc/reitit/core_test.cljc index 77a2e7fd..f89ca40d 100644 --- a/test/cljc/reitit/core_test.cljc +++ b/test/cljc/reitit/core_test.cljc @@ -117,14 +117,14 @@ (let [pong (constantly "ok") routes ["/api" {:mw [:api]} ["/ping" :kikka] - ["/user/:id" {:parameters {:id String}} - ["/:sub-id" {:parameters {:sub-id String}}]] + ["/user/:id" {:parameters {:id "String"}} + ["/:sub-id" {:parameters {:sub-id "String"}}]] ["/pong" pong] ["/admin" {:mw [:admin] :roles #{:admin}} ["/user" {:roles ^:replace #{:user}}] ["/db" {:mw [:db]}]]] expected [["/api/ping" {:mw [:api], :name :kikka}] - ["/api/user/:id/:sub-id" {:mw [:api], :parameters {:id String, :sub-id String}}] + ["/api/user/:id/:sub-id" {:mw [:api], :parameters {:id "String", :sub-id "String"}}] ["/api/pong" {:mw [:api], :handler pong}] ["/api/admin/user" {:mw [:api :admin], :roles #{:user}}] ["/api/admin/db" {:mw [:api :admin :db], :roles #{:admin}}]] @@ -132,7 +132,7 @@ (is (= expected (reitit/resolve-routes routes {}))) (is (= (reitit/map->Match {:template "/api/user/:id/:sub-id" - :meta {:mw [:api], :parameters {:id String, :sub-id String}} + :meta {:mw [:api], :parameters {:id "String", :sub-id "String"}} :path "/api/user/1/2" :params {:id "1", :sub-id "2"}}) (reitit/match-by-path router "/api/user/1/2")))))) From bbcf0c8350886895c8fdc833a91d82cbdd8345cb Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sun, 20 Aug 2017 20:40:50 +0300 Subject: [PATCH 08/11] 2x speed with LookupRouter & fast-map on clj --- perf-test/clj/reitit/opensensors_routing_test.clj | 2 ++ src/reitit/core.cljc | 8 ++++---- src/reitit/impl.cljc | 8 ++++++++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/perf-test/clj/reitit/opensensors_routing_test.clj b/perf-test/clj/reitit/opensensors_routing_test.clj index 2d204506..63ec8f84 100644 --- a/perf-test/clj/reitit/opensensors_routing_test.clj +++ b/perf-test/clj/reitit/opensensors_routing_test.clj @@ -1037,10 +1037,12 @@ pedestal-f #(pedestal/find-route cqrs-routes-pedestal {:path-info % :request-method :post})] ;; 125ns + ;; 62ns (fast-map) (bench! routes false "reitit" reitit-f) ;; 272ns ;; 219ns (fast-assoc) + ;; 171ns (fast-map) (bench! routes false "reitit-ring" reitit-ring-f) ;; 172ns diff --git a/src/reitit/core.cljc b/src/reitit/core.cljc index 45bf3901..bf252217 100644 --- a/src/reitit/core.cljc +++ b/src/reitit/core.cljc @@ -142,12 +142,12 @@ (route-names [_] names) (match-by-path [_ path] - (data path)) + (impl/fast-get data path)) (match-by-name [_ name] - (if-let [match (lookup name)] + (if-let [match (impl/fast-get lookup name)] (match nil))) (match-by-name [_ name params] - (if-let [match (lookup name)] + (if-let [match (impl/fast-get lookup name)] (match params)))) (defn lookup-router @@ -170,7 +170,7 @@ (if name (assoc lookup name #(->Match p meta handler % p)) lookup)]) [{} {}] compiled)] - (->LookupRouter routes names data lookup)))) + (->LookupRouter routes names (impl/fast-map data) (impl/fast-map lookup))))) (defn router "Create a [[Router]] from raw route data and optionally an options map. diff --git a/src/reitit/impl.cljc b/src/reitit/impl.cljc index fcae9e7b..0e95c253 100644 --- a/src/reitit/impl.cljc +++ b/src/reitit/impl.cljc @@ -139,3 +139,11 @@ (defn fast-assoc #?@(:clj [[^clojure.lang.Associative a k v] (.assoc a k v)] :cljs [[a k v] (assoc a k v)])) + +(defn fast-map [m] + #?@(:clj [(java.util.HashMap. m)] + :cljs [m])) + +(defn fast-get + #?@(:clj [[^java.util.HashMap m k] (.get m k)] + :cljs [[m k] (m k)])) From 3e3f728c4ed38639bc7c26cc6d8e55bd98e47baa Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sun, 20 Aug 2017 21:00:20 +0300 Subject: [PATCH 09/11] reitit/options, reitit/router-type, reified protocols --- .../clj/reitit/opensensors_routing_test.clj | 648 +++--------------- src/reitit/core.cljc | 86 +-- test/cljc/reitit/core_test.cljc | 6 +- 3 files changed, 130 insertions(+), 610 deletions(-) diff --git a/perf-test/clj/reitit/opensensors_routing_test.clj b/perf-test/clj/reitit/opensensors_routing_test.clj index 63ec8f84..d25b7942 100644 --- a/perf-test/clj/reitit/opensensors_routing_test.clj +++ b/perf-test/clj/reitit/opensensors_routing_test.clj @@ -281,78 +281,78 @@ (def opensensors-compojure-api-routes (routes (context "/v1" [] - (context "/public" [] - (ANY "/topics/:topic" [] {:name :test/route4} handler) - (ANY "/users/:user-id" [] {:name :test/route16} handler) - (ANY "/orgs/:org-id" [] {:name :test/route18} handler)) - (context "/users/:user-id" [] - (ANY "/orgs/:org-id" [] {:name :test/route5} handler) - (ANY "/invitations" [] {:name :test/route7} handler) - (ANY "/topics" [] {:name :test/route9} handler) - (ANY "/bookmarks/followers" [] {:name :test/route10} handler) - (context "/devices" [] - (ANY "/" [] {:name :test/route15} handler) - #_(ANY "/bulk" [] {:name :test/route21} handler) - (ANY "/:client-id" [] {:name :test/route35} handler) - (ANY "/:client-id/reset-password" [] {:name :test/route49} handler)) - (ANY "/device-errors" [] {:name :test/route22} handler) - (ANY "/usage-stats" [] {:name :test/route24} handler) - (ANY "/claim-device/:client-id" [] {:name :test/route26} handler) - (ANY "/owned-orgs" [] {:name :test/route31} handler) - (ANY "/bookmark/:topic" [] {:name :test/route33} handler) - (ANY "/" [] {:name :test/route36} handler) - (ANY "/orgs" [] {:name :test/route52} handler) - (ANY "/api-key" [] {:name :test/route43} handler) - (ANY "/bookmarks" [] {:name :test/route56} handler)) - (ANY "/search/topics/:term" [] {:name :test/route6} handler) - (context "/orgs" [] - (ANY "/" [] {:name :test/route55} handler) - (context "/:org-id" [] - (context "/devices" [] - (ANY "/" [] {:name :test/route37} handler) - (ANY "/:device-id" [] {:name :test/route13} handler) - #_(ANY "/:batch/:type" [] {:name :test/route8} handler)) - (ANY "/usage-stats" [] {:name :test/route12} handler) - (ANY "/invitations" [] {:name :test/route19} handler) - (context "/members" [] - (ANY "/:user-id" [] {:name :test/route34} handler) - (ANY "/" [] {:name :test/route38} handler) - #_(ANY "/invitation-data/:user-id" [] {:name :test/route39} handler)) - (ANY "/errors" [] {:name :test/route17} handler) - (ANY "/" [] {:name :test/route42} handler) - (ANY "/confirm-membership/:token" [] {:name :test/route46} handler) - (ANY "/topics" [] {:name :test/route57} handler))) - (context "/messages" [] - (ANY "/user/:user-id" [] {:name :test/route14} handler) - (ANY "/device/:client-id" [] {:name :test/route30} handler) - (ANY "/topic/:topic" [] {:name :test/route48} handler)) - (context "/topics" [] - (ANY "/:topic" [] {:name :test/route32} handler) - (ANY "/" [] {:name :test/route54} handler)) - (ANY "/whoami" [] {:name :test/route41} handler) - (ANY "/login" [] {:name :test/route51} handler)) + (context "/public" [] + (ANY "/topics/:topic" [] {:name :test/route4} handler) + (ANY "/users/:user-id" [] {:name :test/route16} handler) + (ANY "/orgs/:org-id" [] {:name :test/route18} handler)) + (context "/users/:user-id" [] + (ANY "/orgs/:org-id" [] {:name :test/route5} handler) + (ANY "/invitations" [] {:name :test/route7} handler) + (ANY "/topics" [] {:name :test/route9} handler) + (ANY "/bookmarks/followers" [] {:name :test/route10} handler) + (context "/devices" [] + (ANY "/" [] {:name :test/route15} handler) + #_(ANY "/bulk" [] {:name :test/route21} handler) + (ANY "/:client-id" [] {:name :test/route35} handler) + (ANY "/:client-id/reset-password" [] {:name :test/route49} handler)) + (ANY "/device-errors" [] {:name :test/route22} handler) + (ANY "/usage-stats" [] {:name :test/route24} handler) + (ANY "/claim-device/:client-id" [] {:name :test/route26} handler) + (ANY "/owned-orgs" [] {:name :test/route31} handler) + (ANY "/bookmark/:topic" [] {:name :test/route33} handler) + (ANY "/" [] {:name :test/route36} handler) + (ANY "/orgs" [] {:name :test/route52} handler) + (ANY "/api-key" [] {:name :test/route43} handler) + (ANY "/bookmarks" [] {:name :test/route56} handler)) + (ANY "/search/topics/:term" [] {:name :test/route6} handler) + (context "/orgs" [] + (ANY "/" [] {:name :test/route55} handler) + (context "/:org-id" [] + (context "/devices" [] + (ANY "/" [] {:name :test/route37} handler) + (ANY "/:device-id" [] {:name :test/route13} handler) + #_(ANY "/:batch/:type" [] {:name :test/route8} handler)) + (ANY "/usage-stats" [] {:name :test/route12} handler) + (ANY "/invitations" [] {:name :test/route19} handler) + (context "/members" [] + (ANY "/:user-id" [] {:name :test/route34} handler) + (ANY "/" [] {:name :test/route38} handler) + #_(ANY "/invitation-data/:user-id" [] {:name :test/route39} handler)) + (ANY "/errors" [] {:name :test/route17} handler) + (ANY "/" [] {:name :test/route42} handler) + (ANY "/confirm-membership/:token" [] {:name :test/route46} handler) + (ANY "/topics" [] {:name :test/route57} handler))) + (context "/messages" [] + (ANY "/user/:user-id" [] {:name :test/route14} handler) + (ANY "/device/:client-id" [] {:name :test/route30} handler) + (ANY "/topic/:topic" [] {:name :test/route48} handler)) + (context "/topics" [] + (ANY "/:topic" [] {:name :test/route32} handler) + (ANY "/" [] {:name :test/route54} handler)) + (ANY "/whoami" [] {:name :test/route41} handler) + (ANY "/login" [] {:name :test/route51} handler)) (context "/v2" [] - (ANY "/whoami" [] {:name :test/route1} handler) - (context "/users/:user-id" [] - (ANY "/datasets" [] {:name :test/route2} handler) - (ANY "/devices" [] {:name :test/route25} handler) - (context "/topics" [] - (ANY "/bulk" [] {:name :test/route29} handler) - (ANY "/" [] {:name :test/route54} handler)) - (ANY "/" [] {:name :test/route45} handler)) - (context "/public" [] - (context "/projects/:project-id" [] - (ANY "/datasets" [] {:name :test/route3} handler) - (ANY "/" [] {:name :test/route27} handler)) - (ANY "/messages/dataset/bulk" [] {:name :test/route20} handler) - (ANY "/datasets/:dataset-id" [] {:name :test/route28} handler) - (ANY "/messages/dataset/:dataset-id" [] {:name :test/route53} handler)) - (ANY "/datasets/:dataset-id" [] {:name :test/route11} handler) - (ANY "/login" [] {:name :test/route23} handler) - (ANY "/orgs/:org-id/topics" [] {:name :test/route40} handler) - (ANY "/schemas" [] {:name :test/route44} handler) - (ANY "/topics/:topic" [] {:name :test/route47} handler) - (ANY "/topics" [] {:name :test/route50} handler)))) + (ANY "/whoami" [] {:name :test/route1} handler) + (context "/users/:user-id" [] + (ANY "/datasets" [] {:name :test/route2} handler) + (ANY "/devices" [] {:name :test/route25} handler) + (context "/topics" [] + (ANY "/bulk" [] {:name :test/route29} handler) + (ANY "/" [] {:name :test/route54} handler)) + (ANY "/" [] {:name :test/route45} handler)) + (context "/public" [] + (context "/projects/:project-id" [] + (ANY "/datasets" [] {:name :test/route3} handler) + (ANY "/" [] {:name :test/route27} handler)) + (ANY "/messages/dataset/bulk" [] {:name :test/route20} handler) + (ANY "/datasets/:dataset-id" [] {:name :test/route28} handler) + (ANY "/messages/dataset/:dataset-id" [] {:name :test/route53} handler)) + (ANY "/datasets/:dataset-id" [] {:name :test/route11} handler) + (ANY "/login" [] {:name :test/route23} handler) + (ANY "/orgs/:org-id/topics" [] {:name :test/route40} handler) + (ANY "/schemas" [] {:name :test/route44} handler) + (ANY "/topics/:topic" [] {:name :test/route47} handler) + (ANY "/topics" [] {:name :test/route50} handler)))) (def opensensors-pedestal-routes (map-tree/router @@ -493,12 +493,12 @@ compojure-api-f #(opensensors-compojure-api-routes {:uri % :request-method :get}) pedestal-f #(pedestal/find-route opensensors-pedestal-routes {:path-info % :request-method :get})] - (bench! routes true "reitit" reitit-f) ;; 2538ns 10% - (bench! routes true "pedestal" pedestal-f) ;; 2737ns 11% - (bench! routes true "reitit-ring" reitit-ring-f) ;; 2845ns 11% - (bench! routes true "compojure-api" compojure-api-f) ;; 10215ns 41% - (bench! routes true "bidi" bidi-f) ;; 19298ns 77% - (bench! routes true "ataraxy" ataraxy-f) ;; 24950ns 100% + (bench! routes true "reitit" reitit-f) ;; 2538ns 10% + (bench! routes true "pedestal" pedestal-f) ;; 2737ns 11% + (bench! routes true "reitit-ring" reitit-ring-f) ;; 2845ns 11% + (bench! routes true "compojure-api" compojure-api-f) ;; 10215ns 41% + (bench! routes true "bidi" bidi-f) ;; 19298ns 77% + (bench! routes true "ataraxy" ataraxy-f) ;; 24950ns 100% )) @@ -509,499 +509,9 @@ ;; CQRSish ;; -(def commands - #{:upsert-appeal - :upsert-appeal-verdict - :delete-appeal - :delete-appeal-verdict - :mark-seen - :mark-everything-seen - :upsert-application-handler - :remove-application-handler - :cancel-inforequest - :cancel-application - :cancel-application-authority - :undo-cancellation - :request-for-complement - :cleanup-krysp - :submit-application - :refresh-ktj - :save-application-drawings - :create-application - :add-operation - :update-op-description - :change-primary-operation - :change-permit-sub-type - :change-location - :change-application-state - :return-to-draft - :change-warranty-start-date - :change-warranty-end-date - :add-link-permit - :remove-link-permit-by-app-id - :create-change-permit - :create-continuation-period-permit - :convert-to-application - :add-bulletin-comment - :move-to-proclaimed - :move-to-verdict-given - :move-to-final - :save-proclaimed-bulletin - :save-verdict-given-bulletin - :set-municipality-hears-neighbors - :archive-documents - :mark-pre-verdict-phase-archived - :save-asianhallinta-config - :create-assignment - :update-assignment - :complete-assignment - :bind-attachment - :bind-attachments - :set-attachment-type - :approve-attachment - :reject-attachment - :reject-attachment-note - :create-attachments - :create-ram-attachment - :delete-attachment - :delete-attachment-version - :upload-attachment - :rotate-pdf - :upsert-stamp-template - :delete-stamp-template - :stamp-attachments - :sign-attachments - :set-attachment-meta - :set-attachment-not-needed - :set-attachments-as-verdict-attachment - :set-attachment-as-construction-time - :set-attachment-visibility - :convert-to-pdfa - :invite-with-role - :approve-invite - :decline-invitation - :remove-auth - :change-auth - :unsubscribe-notifications - :subscribe-notifications - :set-calendar-enabled-for-authority - :create-calendar-slots - :update-calendar-slot - :delete-calendar-slot - :add-reservation-type-for-organization - :update-reservation-type - :delete-reservation-type - :reserve-calendar-slot - :accept-reservation - :decline-reservation - :cancel-reservation - :mark-reservation-update-seen - :add-campaign - :delete-campaign - :change-email-init - :change-email - :can-target-comment-to-authority - :can-mark-answered - :add-comment - :company-update - :company-lock - :company-user-update - :company-user-delete - :company-user-delete-all - :company-invite-user - :company-add-user - :company-invite - :company-cancel-invite - :save-company-tags - :update-application-company-notes - :inform-construction-started - :inform-construction-ready - :copy-application - :update-3d-map-server-details - :set-3d-map-enabled - :redirect-to-3d-map - :create-archiving-project - :submit-archiving-project - :create-doc - :remove-doc - :set-doc-status - :update-doc - :update-task - :remove-document-data - :approve-doc - :reject-doc - :reject-doc-note - :set-user-to-document - :set-current-user-to-document - :set-company-to-document - :set-feature - :remove-uploaded-file - :create-foreman-application - :update-foreman-other-applications - :link-foreman-task - :update-guest-authority-organization - :remove-guest-authority-organization - :invite-guest - :toggle-guest-subscription - :delete-guest-application - :info-link-delete - :info-link-reorder - :info-link-upsert - :mark-seen-organization-links - :create-inspection-summary-template - :delete-inspection-summary-template - :modify-inspection-summary-template - :set-inspection-summary-template-for-operation - :create-inspection-summary - :delete-inspection-summary - :toggle-inspection-summary-locking - :add-target-to-inspection-summary - :edit-inspection-summary-target - :remove-target-from-inspection-summary - :set-target-status - :set-inspection-date - :approve-application - :move-attachments-to-backing-system - :parties-as-krysp - :merge-details-from-krysp - :application-to-asianhallinta - :attachments-to-asianhallinta - :order-verdict-attachment-prints - :frontend-log - :reset-frontend-log - :new-verdict-template - :set-verdict-template-name - :save-verdict-template-draft-value - :publish-verdict-template - :toggle-delete-verdict-template - :copy-verdict-template - :save-verdict-template-settings-value - :add-verdict-template-review - :update-verdict-template-review - :add-verdict-template-plan - :update-verdict-template-plan - :set-default-operation-verdict-template - :upsert-phrase - :delete-phrase - :neighbor-add - :neighbor-add-owners - :neighbor-update - :neighbor-remove - :neighbor-send-invite - :neighbor-mark-done - :neighbor-response - :change-urgency - :add-authority-notice - :add-application-tags - :init-sign - :cancel-sign - :convert-to-normal-inforequests - :update-organization - :add-scope - :create-organization - :add-organization-link - :update-organization-link - :remove-organization-link - :update-allowed-autologin-ips - :set-organization-selected-operations - :organization-operations-attachments - :set-organization-app-required-fields-filling-obligatory - :set-automatic-ok-for-attachments - :set-organization-assignments - :set-organization-inspection-summaries - :set-organization-extended-construction-waste-report - :set-organization-validate-verdict-given-date - :set-organization-use-attachment-links-integration - :set-organization-calendars-enabled - :set-organization-boolean-attribute - :set-organization-permanent-archive-start-date - :set-organization-neighbor-order-email - :set-organization-submit-notification-email - :set-organization-inforequest-notification-email - :set-organization-default-reservation-location - :set-krysp-endpoint - :set-kopiolaitos-info - :save-vendor-backend-redirect-config - :update-organization-name - :save-organization-tags - :update-map-server-details - :update-user-layers - :update-suti-server-details - :section-toggle-enabled - :section-toggle-operation - :upsert-handler-role - :toggle-handler-role - :upsert-assignment-trigger - :remove-assignment-trigger - :update-docstore-info - :browser-timing - :create-application-from-previous-permit - :screenmessages-add - :screenmessages-reset - :add-single-sign-on-key - :update-single-sign-on-key - :remove-single-sign-on-key - :create-statement-giver - :delete-statement-giver - :request-for-statement - :ely-statement-request - :delete-statement - :save-statement-as-draft - :give-statement - :request-for-statement-reply - :save-statement-reply-as-draft - :reply-statement - :suti-toggle-enabled - :suti-toggle-operation - :suti-www - :suti-update-id - :suti-update-added - :create-task - :delete-task - :approve-task - :reject-task - :review-done - :mark-review-faulty - :resend-review-to-backing-system - :set-tos-function-for-operation - :remove-tos-function-from-operation - :set-tos-function-for-application - :force-fix-tos-function-for-application - :store-tos-metadata-for-attachment - :store-tos-metadata-for-application - :store-tos-metadata-for-process - :set-myyntipalvelu-for-attachment - :create-user - :create-rest-api-user - :update-user - :applicant-to-authority - :update-default-application-filter - :save-application-filter - :remove-application-filter - :update-user-organization - :remove-user-organization - :update-user-roles - :check-password - :change-passwd - :reset-password - :admin-reset-password - :set-user-enabled - :login - :impersonate-authority - :register-user - :confirm-account-link - :retry-rakentajafi - :remove-user-attachment - :copy-user-attachments-to-application - :remove-user-notification - :notifications-update - :check-for-verdict - :new-verdict-draft - :save-verdict-draft - :publish-verdict - :delete-verdict - :sign-verdict - :create-digging-permit}) +(def commands #{:upsert-appeal :upsert-appeal-verdict :delete-appeal :delete-appeal-verdict :mark-seen :mark-everything-seen :upsert-application-handler :remove-application-handler :cancel-inforequest :cancel-application :cancel-application-authority :undo-cancellation :request-for-complement :cleanup-krysp :submit-application :refresh-ktj :save-application-drawings :create-application :add-operation :update-op-description :change-primary-operation :change-permit-sub-type :change-location :change-application-state :return-to-draft :change-warranty-start-date :change-warranty-end-date :add-link-permit :remove-link-permit-by-app-id :create-change-permit :create-continuation-period-permit :convert-to-application :add-bulletin-comment :move-to-proclaimed :move-to-verdict-given :move-to-final :save-proclaimed-bulletin :save-verdict-given-bulletin :set-municipality-hears-neighbors :archive-documents :mark-pre-verdict-phase-archived :save-asianhallinta-config :create-assignment :update-assignment :complete-assignment :bind-attachment :bind-attachments :set-attachment-type :approve-attachment :reject-attachment :reject-attachment-note :create-attachments :create-ram-attachment :delete-attachment :delete-attachment-version :upload-attachment :rotate-pdf :upsert-stamp-template :delete-stamp-template :stamp-attachments :sign-attachments :set-attachment-meta :set-attachment-not-needed :set-attachments-as-verdict-attachment :set-attachment-as-construction-time :set-attachment-visibility :convert-to-pdfa :invite-with-role :approve-invite :decline-invitation :remove-auth :change-auth :unsubscribe-notifications :subscribe-notifications :set-calendar-enabled-for-authority :create-calendar-slots :update-calendar-slot :delete-calendar-slot :add-reservation-type-for-organization :update-reservation-type :delete-reservation-type :reserve-calendar-slot :accept-reservation :decline-reservation :cancel-reservation :mark-reservation-update-seen :add-campaign :delete-campaign :change-email-init :change-email :can-target-comment-to-authority :can-mark-answered :add-comment :company-update :company-lock :company-user-update :company-user-delete :company-user-delete-all :company-invite-user :company-add-user :company-invite :company-cancel-invite :save-company-tags :update-application-company-notes :inform-construction-started :inform-construction-ready :copy-application :update-3d-map-server-details :set-3d-map-enabled :redirect-to-3d-map :create-archiving-project :submit-archiving-project :create-doc :remove-doc :set-doc-status :update-doc :update-task :remove-document-data :approve-doc :reject-doc :reject-doc-note :set-user-to-document :set-current-user-to-document :set-company-to-document :set-feature :remove-uploaded-file :create-foreman-application :update-foreman-other-applications :link-foreman-task :update-guest-authority-organization :remove-guest-authority-organization :invite-guest :toggle-guest-subscription :delete-guest-application :info-link-delete :info-link-reorder :info-link-upsert :mark-seen-organization-links :create-inspection-summary-template :delete-inspection-summary-template :modify-inspection-summary-template :set-inspection-summary-template-for-operation :create-inspection-summary :delete-inspection-summary :toggle-inspection-summary-locking :add-target-to-inspection-summary :edit-inspection-summary-target :remove-target-from-inspection-summary :set-target-status :set-inspection-date :approve-application :move-attachments-to-backing-system :parties-as-krysp :merge-details-from-krysp :application-to-asianhallinta :attachments-to-asianhallinta :order-verdict-attachment-prints :frontend-log :reset-frontend-log :new-verdict-template :set-verdict-template-name :save-verdict-template-draft-value :publish-verdict-template :toggle-delete-verdict-template :copy-verdict-template :save-verdict-template-settings-value :add-verdict-template-review :update-verdict-template-review :add-verdict-template-plan :update-verdict-template-plan :set-default-operation-verdict-template :upsert-phrase :delete-phrase :neighbor-add :neighbor-add-owners :neighbor-update :neighbor-remove :neighbor-send-invite :neighbor-mark-done :neighbor-response :change-urgency :add-authority-notice :add-application-tags :init-sign :cancel-sign :convert-to-normal-inforequests :update-organization :add-scope :create-organization :add-organization-link :update-organization-link :remove-organization-link :update-allowed-autologin-ips :set-organization-selected-operations :organization-operations-attachments :set-organization-app-required-fields-filling-obligatory :set-automatic-ok-for-attachments :set-organization-assignments :set-organization-inspection-summaries :set-organization-extended-construction-waste-report :set-organization-validate-verdict-given-date :set-organization-use-attachment-links-integration :set-organization-calendars-enabled :set-organization-boolean-attribute :set-organization-permanent-archive-start-date :set-organization-neighbor-order-email :set-organization-submit-notification-email :set-organization-inforequest-notification-email :set-organization-default-reservation-location :set-krysp-endpoint :set-kopiolaitos-info :save-vendor-backend-redirect-config :update-organization-name :save-organization-tags :update-map-server-details :update-user-layers :update-suti-server-details :section-toggle-enabled :section-toggle-operation :upsert-handler-role :toggle-handler-role :upsert-assignment-trigger :remove-assignment-trigger :update-docstore-info :browser-timing :create-application-from-previous-permit :screenmessages-add :screenmessages-reset :add-single-sign-on-key :update-single-sign-on-key :remove-single-sign-on-key :create-statement-giver :delete-statement-giver :request-for-statement :ely-statement-request :delete-statement :save-statement-as-draft :give-statement :request-for-statement-reply :save-statement-reply-as-draft :reply-statement :suti-toggle-enabled :suti-toggle-operation :suti-www :suti-update-id :suti-update-added :create-task :delete-task :approve-task :reject-task :review-done :mark-review-faulty :resend-review-to-backing-system :set-tos-function-for-operation :remove-tos-function-from-operation :set-tos-function-for-application :force-fix-tos-function-for-application :store-tos-metadata-for-attachment :store-tos-metadata-for-application :store-tos-metadata-for-process :set-myyntipalvelu-for-attachment :create-user :create-rest-api-user :update-user :applicant-to-authority :update-default-application-filter :save-application-filter :remove-application-filter :update-user-organization :remove-user-organization :update-user-roles :check-password :change-passwd :reset-password :admin-reset-password :set-user-enabled :login :impersonate-authority :register-user :confirm-account-link :retry-rakentajafi :remove-user-attachment :copy-user-attachments-to-application :remove-user-notification :notifications-update :check-for-verdict :new-verdict-draft :save-verdict-draft :publish-verdict :delete-verdict :sign-verdict :create-digging-permit}) -(def queries - #{:comments - :actions - :allowed-actions - :allowed-actions-for-category - :admin-attachment-report - :appeals - :application - :application-authorities - :application-commenters - :enable-accordions - :party-document-names - :application-submittable - :inforequest-markers - :change-application-state-targets - :link-permit-required - :app-matches-for-link-permits - :all-operations-in - :application-handlers - :application-organization-handler-roles - :application-organization-archive-enabled - :application-bulletins - :application-bulletin-municipalities - :application-bulletin-states - :bulletin - :bulletin-versions - :bulletin-comments - :publish-bulletin-enabled - :municipality-hears-neighbors-visible - :applications-search - :applications-search-default - :applications-for-new-appointment-page - :get-application-operations - :applications - :latest-applications - :event-search - :tasks-tab-visible - :application-info-tab-visible - :application-summary-tab-visible - :application-verdict-tab-visible - :document-states - :archiving-operations-enabled - :permanent-archive-enabled - :application-in-final-archiving-state - :asianhallinta-config - :assignments-for-application - :assignment-targets - :assignments-search - :assignment-count - :assignments - :assignment - :bind-attachments-job - :attachments - :attachment - :attachment-groups - :attachments-filters - :attachments-tag-groups - :attachment-types - :ram-linked-attachments - :attachment-operations - :stamp-templates - :custom-stamps - :stamp-attachments-job - :signing-possible - :set-attachment-group-enabled - :invites - :my-calendars - :calendar - :calendars-for-authority-admin - :calendar-slots - :reservation-types-for-organization - :available-calendar-slots - :application-calendar-config - :calendar-actions-required - :applications-with-appointments - :my-reserved-slots - :campaigns - :campaign - :company - :company-users-for-person-selector - :company-tags - :companies - :user-company-locked - :company-search-user - :remove-company-tag-ok - :company-notes - :enable-company-search - :info-construction-status - :copy-application-invite-candidates - :application-copyable-to-location - :application-copyable - :source-application - :user-is-pure-digitizer - :digitizing-enabled - :document - :validate-doc - :fetch-validation-errors - :schemas - :features - :apply-fixture - :foreman-history - :foreman-applications - :resolve-guest-authority-candidate - :guest-authorities-organization - :application-guests - :guest-authorities-application-organization - :get-link-account-token - :info-links - :organization-links - :organization-inspection-summary-settings - :inspection-summaries-for-application - :get-building-info-from-wfs - :external-api-enabled - :integration-messages - :ely-statement-types - :frontend-log-entries - :newest-version - :verdict-templates - :verdict-template-categories - :verdict-template - :verdict-template-settings - :verdict-template-reviews - :verdict-template-plans - :default-operation-verdict-templates - :organization-phrases - :application-phrases - :owners - :application-property-owners - :municipality-borders - :active-municipalities - :municipality-active - :neighbor-application - :authority-notice - :find-sign-process - :organization-by-user - :all-attachment-types-by-user - :organization-name-by-user - :user-organizations-for-permit-type - :user-organizations-for-archiving-project - :organizations - :allowed-autologin-ips-for-organization - :organization-by-id - :permit-types - :municipalities-with-organization - :municipalities - :all-operations-for-organization - :selected-operations-for-municipality - :addable-operations - :organization-details - :krysp-config - :kopiolaitos-config - :get-organization-names - :vendor-backend-redirect-config - :remove-tag-ok - :get-organization-tags - :get-organization-areas - :get-map-layers-data - :municipality-for-property - :property-borders - :screenmessages - :get-single-sign-on-keys - :get-organizations-statement-givers - :get-possible-statement-statuses - :get-statement-givers - :statement-replies-enabled - :statement-is-replyable - :authorized-for-requesting-statement-reply - :statement-attachment-allowed - :statements-after-approve-allowed - :neighbors-statement-enabled - :suti-admin-details - :suti-operations - :suti-application-data - :suti-application-products - :suti-pre-sent-state - :task-types-for-application - :review-can-be-marked-done - :is-end-review - :available-tos-functions - :tos-metadata-schema - :case-file-data - :tos-operations-enabled - :common-area-application - :user - :users - :users-in-same-organizations - :user-by-email - :users-for-datatables - :saved-application-filters - :redirect-after-login - :user-attachments - :add-user-attachment-allowed - :email-in-use - :enable-foreman-search - :calendars-enabled - :verdict-attachment-type - :selected-digging-operations-for-organization - :ya-extensions - :approve-ya-extension}) +(def queries #{:comments :actions :allowed-actions :allowed-actions-for-category :admin-attachment-report :appeals :application :application-authorities :application-commenters :enable-accordions :party-document-names :application-submittable :inforequest-markers :change-application-state-targets :link-permit-required :app-matches-for-link-permits :all-operations-in :application-handlers :application-organization-handler-roles :application-organization-archive-enabled :application-bulletins :application-bulletin-municipalities :application-bulletin-states :bulletin :bulletin-versions :bulletin-comments :publish-bulletin-enabled :municipality-hears-neighbors-visible :applications-search :applications-search-default :applications-for-new-appointment-page :get-application-operations :applications :latest-applications :event-search :tasks-tab-visible :application-info-tab-visible :application-summary-tab-visible :application-verdict-tab-visible :document-states :archiving-operations-enabled :permanent-archive-enabled :application-in-final-archiving-state :asianhallinta-config :assignments-for-application :assignment-targets :assignments-search :assignment-count :assignments :assignment :bind-attachments-job :attachments :attachment :attachment-groups :attachments-filters :attachments-tag-groups :attachment-types :ram-linked-attachments :attachment-operations :stamp-templates :custom-stamps :stamp-attachments-job :signing-possible :set-attachment-group-enabled :invites :my-calendars :calendar :calendars-for-authority-admin :calendar-slots :reservation-types-for-organization :available-calendar-slots :application-calendar-config :calendar-actions-required :applications-with-appointments :my-reserved-slots :campaigns :campaign :company :company-users-for-person-selector :company-tags :companies :user-company-locked :company-search-user :remove-company-tag-ok :company-notes :enable-company-search :info-construction-status :copy-application-invite-candidates :application-copyable-to-location :application-copyable :source-application :user-is-pure-digitizer :digitizing-enabled :document :validate-doc :fetch-validation-errors :schemas :features :apply-fixture :foreman-history :foreman-applications :resolve-guest-authority-candidate :guest-authorities-organization :application-guests :guest-authorities-application-organization :get-link-account-token :info-links :organization-links :organization-inspection-summary-settings :inspection-summaries-for-application :get-building-info-from-wfs :external-api-enabled :integration-messages :ely-statement-types :frontend-log-entries :newest-version :verdict-templates :verdict-template-categories :verdict-template :verdict-template-settings :verdict-template-reviews :verdict-template-plans :default-operation-verdict-templates :organization-phrases :application-phrases :owners :application-property-owners :municipality-borders :active-municipalities :municipality-active :neighbor-application :authority-notice :find-sign-process :organization-by-user :all-attachment-types-by-user :organization-name-by-user :user-organizations-for-permit-type :user-organizations-for-archiving-project :organizations :allowed-autologin-ips-for-organization :organization-by-id :permit-types :municipalities-with-organization :municipalities :all-operations-for-organization :selected-operations-for-municipality :addable-operations :organization-details :krysp-config :kopiolaitos-config :get-organization-names :vendor-backend-redirect-config :remove-tag-ok :get-organization-tags :get-organization-areas :get-map-layers-data :municipality-for-property :property-borders :screenmessages :get-single-sign-on-keys :get-organizations-statement-givers :get-possible-statement-statuses :get-statement-givers :statement-replies-enabled :statement-is-replyable :authorized-for-requesting-statement-reply :statement-attachment-allowed :statements-after-approve-allowed :neighbors-statement-enabled :suti-admin-details :suti-operations :suti-application-data :suti-application-products :suti-pre-sent-state :task-types-for-application :review-can-be-marked-done :is-end-review :available-tos-functions :tos-metadata-schema :case-file-data :tos-operations-enabled :common-area-application :user :users :users-in-same-organizations :user-by-email :users-for-datatables :saved-application-filters :redirect-after-login :user-attachments :add-user-attachment-allowed :email-in-use :enable-foreman-search :calendars-enabled :verdict-attachment-type :selected-digging-operations-for-organization :ya-extensions :approve-ya-extension}) (def cqrs-routes (mapv (fn [command] [(str "/command/" (name command)) {:post handler :name command}]) commands)) diff --git a/src/reitit/core.cljc b/src/reitit/core.cljc index bf252217..6ee1c63a 100644 --- a/src/reitit/core.cljc +++ b/src/reitit/core.cljc @@ -70,7 +70,9 @@ [p m (if compile (compile route opts))]) (defprotocol Routing + (router-type [this]) (routes [this]) + (options [this]) (route-names [this]) (match-by-path [this path]) (match-by-name [this name] [this name params])) @@ -97,25 +99,6 @@ :coerce (fn [route _] route) :compile (fn [[_ {:keys [handler]}] _] handler)}) -(defrecord LinearRouter [routes names data lookup] - Routing - (routes [_] - routes) - (route-names [_] - names) - (match-by-path [_ path] - (reduce - (fn [acc ^Route route] - (if-let [params ((:matcher route) path)] - (reduced (->Match (:path route) (:meta route) (:handler route) params path)))) - nil data)) - (match-by-name [_ name] - (if-let [match (lookup name)] - (match nil))) - (match-by-name [_ name params] - (if-let [match (lookup name)] - (match params)))) - (defn linear-router "Creates a [[LinearRouter]] from resolved routes and optional expanded options. See [[router]] for available options" @@ -132,26 +115,33 @@ (->PartialMatch p meta handler % params))] [(conj data route) (if name (assoc lookup name f) lookup)])) - [[] {}] compiled)] - (->LinearRouter routes names data lookup)))) - -(defrecord LookupRouter [routes names data lookup] - Routing - (routes [_] - routes) - (route-names [_] - names) - (match-by-path [_ path] - (impl/fast-get data path)) - (match-by-name [_ name] - (if-let [match (impl/fast-get lookup name)] - (match nil))) - (match-by-name [_ name params] - (if-let [match (impl/fast-get lookup name)] - (match params)))) + [[] {}] compiled) + lookup (impl/fast-map lookup)] + (reify + Routing + (router-type [_] + :linear-router) + (routes [_] + routes) + (options [_] + opts) + (route-names [_] + names) + (match-by-path [_ path] + (reduce + (fn [acc ^Route route] + (if-let [params ((:matcher route) path)] + (reduced (->Match (:path route) (:meta route) (:handler route) params path)))) + nil data)) + (match-by-name [_ name] + (if-let [match (impl/fast-get lookup name)] + (match nil))) + (match-by-name [_ name params] + (if-let [match (impl/fast-get lookup name)] + (match params))))))) (defn lookup-router - "Creates a [[LookupRouter]] from resolved routes and optional + "Creates a LookupRouter from resolved routes and optional expanded options. See [[router]] for available options" ([routes] (lookup-router routes {})) @@ -169,8 +159,26 @@ [(assoc data p (->Match p meta handler {} p)) (if name (assoc lookup name #(->Match p meta handler % p)) - lookup)]) [{} {}] compiled)] - (->LookupRouter routes names (impl/fast-map data) (impl/fast-map lookup))))) + lookup)]) [{} {}] compiled) + data (impl/fast-map data) + lookup (impl/fast-map lookup)] + (reify Routing + (router-type [_] + :lookup-router) + (routes [_] + routes) + (options [_] + opts) + (route-names [_] + names) + (match-by-path [_ path] + (impl/fast-get data path)) + (match-by-name [_ name] + (if-let [match (impl/fast-get lookup name)] + (match nil))) + (match-by-name [_ name params] + (if-let [match (impl/fast-get lookup name)] + (match params))))))) (defn router "Create a [[Router]] from raw route data and optionally an options map. diff --git a/test/cljc/reitit/core_test.cljc b/test/cljc/reitit/core_test.cljc index f89ca40d..90d7d721 100644 --- a/test/cljc/reitit/core_test.cljc +++ b/test/cljc/reitit/core_test.cljc @@ -9,9 +9,10 @@ (testing "linear router" (let [router (reitit/router ["/api" ["/ipa" ["/:size" ::beer]]])] - (is (instance? LinearRouter router)) + (is (= :linear-router (reitit/router-type router))) (is (= [["/api/ipa/:size" {:name ::beer}]] (reitit/routes router))) + (is (= true (map? (reitit/options router)))) (is (= (reitit/map->Match {:template "/api/ipa/:size" :meta {:name ::beer} @@ -41,9 +42,10 @@ (testing "lookup router" (let [router (reitit/router ["/api" ["/ipa" ["/large" ::beer]]])] - (is (instance? LookupRouter router)) + (is (= :lookup-router (reitit/router-type router))) (is (= [["/api/ipa/large" {:name ::beer}]] (reitit/routes router))) + (is (= true (map? (reitit/options router)))) (is (= (reitit/map->Match {:template "/api/ipa/large" :meta {:name ::beer} From 853b7ac24b7de69e44028fa9002268d9310d20c5 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sun, 20 Aug 2017 21:04:35 +0300 Subject: [PATCH 10/11] Cleanup --- project.clj | 1 - test/cljc/reitit/core_test.cljc | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/project.clj b/project.clj index f5178328..97dbaf25 100644 --- a/project.clj +++ b/project.clj @@ -22,7 +22,6 @@ :jvm-opts ^:replace ["-server"] :dependencies [[org.clojure/clojure "1.9.0-alpha17"] [org.clojure/clojurescript "1.9.660"] - [cheshire "5.8.0"] [criterium "0.4.4"] [org.clojure/test.check "0.9.0"] [org.clojure/tools.namespace "0.2.11"] diff --git a/test/cljc/reitit/core_test.cljc b/test/cljc/reitit/core_test.cljc index 90d7d721..ba8c0dbf 100644 --- a/test/cljc/reitit/core_test.cljc +++ b/test/cljc/reitit/core_test.cljc @@ -1,8 +1,8 @@ (ns reitit.core-test (:require [clojure.test :refer [deftest testing is]] - [reitit.core :as reitit #?@(:cljs [:refer [Match LinearRouter LookupRouter]])]) + [reitit.core :as reitit #?@(:cljs [:refer [Match]])]) #?(:clj - (:import (reitit.core Match LinearRouter LookupRouter) + (:import (reitit.core Match) (clojure.lang ExceptionInfo)))) (deftest reitit-test From 8ce6d4e41454a20d2d0a13e84777dc0f1031d4ff Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sun, 20 Aug 2017 21:13:15 +0300 Subject: [PATCH 11/11] Update README --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e1e195af..7cad9a20 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Same routes flattened: ## Routing -For routing, a `Router` is needed. Reitit ships with 2 different router implementations: `LinearRouter` and `LookupRouter`, both based on the awesome [Pedestal](https://github.com/pedestal/pedestal/tree/master/route) implementation. +For routing, a `Router` is needed. Reitit ships with 2 different router implementations: `:linear-router` and `:lookup-router`, both based on the awesome [Pedestal](https://github.com/pedestal/pedestal/tree/master/route) implementation. `Router` is created with `reitit.core/router`, which takes routes and optional options map as arguments. The route-tree gets expanded, optionally coerced and compiled. `Router` support both fast path- and name-based lookups. @@ -81,11 +81,11 @@ Creating a router: ["/user/:id" ::user]]])) ``` -`LinearRouter` is created (as there are wildcard): +`:linear-router` is created (as there are wildcard): ```clj -(class router) -; reitit.core.LinearRouter +(reitit/router-type router) +; :linear-router ``` The expanded routes: @@ -224,11 +224,11 @@ Simple [Ring](https://github.com/ring-clojure/ring)-based routing app: ["/ping" handler]))) ``` -Backed by a `LookupRouter` (as no wildcards found): +Backed by a `:lookup-router` (as no wildcards found): ```clj -(-> app (ring/get-router) class) -; reitit.core.LookupRouter +(-> app (ring/get-router) (reitit/router-type)) +; :lookup-router ``` The expanded routes: