mirror of
https://github.com/metosin/reitit.git
synced 2025-12-20 09:31:11 +00:00
commit
0befddf72c
7 changed files with 771 additions and 83 deletions
62
README.md
62
README.md
|
|
@ -63,9 +63,9 @@ Same routes flattened:
|
||||||
["/api/ping" ::ping]]
|
["/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.
|
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.
|
`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.
|
||||||
|
|
||||||
|
|
@ -78,14 +78,14 @@ Creating a router:
|
||||||
(reitit/router
|
(reitit/router
|
||||||
[["/api"
|
[["/api"
|
||||||
["/ping" ::ping]
|
["/ping" ::ping]
|
||||||
["/user/:id" ::user]]))
|
["/user/:id" ::user]]]))
|
||||||
```
|
```
|
||||||
|
|
||||||
`LinearRouter` is created (as there are wildcard):
|
`:linear-router` is created (as there are wildcard):
|
||||||
|
|
||||||
```clj
|
```clj
|
||||||
(class router)
|
(reitit/router-type router)
|
||||||
; reitit.core.LinearRouter
|
; :linear-router
|
||||||
```
|
```
|
||||||
|
|
||||||
The expanded routes:
|
The expanded routes:
|
||||||
|
|
@ -96,6 +96,13 @@ The expanded routes:
|
||||||
; ["/api/user/:id" {:name :user/user}]]
|
; ["/api/user/:id" {:name :user/user}]]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Route names:
|
||||||
|
|
||||||
|
```clj
|
||||||
|
(reitit/route-names router)
|
||||||
|
; [:user/ping :user/user]
|
||||||
|
```
|
||||||
|
|
||||||
Path-based routing:
|
Path-based routing:
|
||||||
|
|
||||||
```clj
|
```clj
|
||||||
|
|
@ -114,8 +121,19 @@ Name-based (reverse) routing:
|
||||||
|
|
||||||
```clj
|
```clj
|
||||||
(reitit/match-by-name router ::user)
|
(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"})
|
(reitit/match-by-name router ::user {:id "1"})
|
||||||
; #Match{:template "/api/user/:id"
|
; #Match{:template "/api/user/:id"
|
||||||
; :meta {:name :user/user}
|
; :meta {:name :user/user}
|
||||||
|
|
@ -124,6 +142,13 @@ Name-based (reverse) routing:
|
||||||
; :params {:id "1"}}
|
; :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
|
## 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).
|
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).
|
||||||
|
|
@ -199,11 +224,11 @@ Simple [Ring](https://github.com/ring-clojure/ring)-based routing app:
|
||||||
["/ping" handler])))
|
["/ping" handler])))
|
||||||
```
|
```
|
||||||
|
|
||||||
Backed by a `LookupRouter` (as no wildcards found):
|
Backed by a `:lookup-router` (as no wildcards found):
|
||||||
|
|
||||||
```clj
|
```clj
|
||||||
(-> app (ring/get-router) class)
|
(-> app (ring/get-router) (reitit/router-type))
|
||||||
; reitit.core.LookupRouter
|
; :lookup-router
|
||||||
```
|
```
|
||||||
|
|
||||||
The expanded routes:
|
The expanded routes:
|
||||||
|
|
@ -229,7 +254,8 @@ Routing based on `:request-method`:
|
||||||
(def app
|
(def app
|
||||||
(ring/ring-handler
|
(ring/ring-handler
|
||||||
(ring/router
|
(ring/router
|
||||||
["/ping" {:get handler
|
["/ping" {:name ::ping
|
||||||
|
:get handler
|
||||||
:post handler}])))
|
:post handler}])))
|
||||||
|
|
||||||
(app {:request-method :get, :uri "/ping"})
|
(app {:request-method :get, :uri "/ping"})
|
||||||
|
|
@ -239,6 +265,16 @@ Routing based on `:request-method`:
|
||||||
; nil
|
; nil
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Reverse routing:
|
||||||
|
|
||||||
|
```clj
|
||||||
|
(-> app
|
||||||
|
(ring/get-router)
|
||||||
|
(reitit/match-by-name ::ping)
|
||||||
|
:path)
|
||||||
|
; "/ping"
|
||||||
|
```
|
||||||
|
|
||||||
Some middleware and a new handler:
|
Some middleware and a new handler:
|
||||||
|
|
||||||
```clj
|
```clj
|
||||||
|
|
@ -362,8 +398,8 @@ Routers can be configured via options. Options allow things like [`clojure.spec`
|
||||||
| `:routes` | Initial resolved routes (default `[]`)
|
| `:routes` | Initial resolved routes (default `[]`)
|
||||||
| `:meta` | Initial expanded route-meta vector (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`)
|
| `: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`
|
| `:coerce` | Function of `route opts => route` to coerce resolved route, can throw or return `nil`
|
||||||
| `:compile` | Function of `[path meta] opts => handler` to compile a route handler
|
| `:compile` | Function of `route opts => handler` to compile a route handler
|
||||||
|
|
||||||
## Special thanks
|
## Special thanks
|
||||||
|
|
||||||
|
|
|
||||||
563
perf-test/clj/reitit/opensensors_routing_test.clj
Normal file
563
perf-test/clj/reitit/opensensors_routing_test.clj
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -60,38 +60,45 @@
|
||||||
(cond->> (->> (walk data opts) (map-meta merge-meta))
|
(cond->> (->> (walk data opts) (map-meta merge-meta))
|
||||||
coerce (into [] (keep #(coerce % opts)))))
|
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}]
|
(defn compile-route [[p m :as route] {:keys [compile] :as opts}]
|
||||||
[p m (if compile (compile route opts))])
|
[p m (if compile (compile route opts))])
|
||||||
|
|
||||||
(defprotocol Routing
|
(defprotocol Routing
|
||||||
|
(router-type [this])
|
||||||
(routes [this])
|
(routes [this])
|
||||||
|
(options [this])
|
||||||
|
(route-names [this])
|
||||||
(match-by-path [this path])
|
(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
|
(def default-router-options
|
||||||
{:expand expand
|
{:lookup name-lookup
|
||||||
|
:expand expand
|
||||||
:coerce (fn [route _] route)
|
:coerce (fn [route _] route)
|
||||||
:compile (fn [[_ {:keys [handler]}] _] handler)})
|
:compile (fn [[_ {:keys [handler]}] _] handler)})
|
||||||
|
|
||||||
(defrecord LinearRouter [routes data lookup]
|
|
||||||
Routing
|
|
||||||
(routes [_]
|
|
||||||
routes)
|
|
||||||
(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))))
|
|
||||||
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
|
(defn linear-router
|
||||||
"Creates a [[LinearRouter]] from resolved routes and optional
|
"Creates a [[LinearRouter]] from resolved routes and optional
|
||||||
expanded options. See [[router]] for available options"
|
expanded options. See [[router]] for available options"
|
||||||
|
|
@ -99,30 +106,42 @@
|
||||||
(linear-router routes {}))
|
(linear-router routes {}))
|
||||||
([routes opts]
|
([routes opts]
|
||||||
(let [compiled (map #(compile-route % opts) routes)
|
(let [compiled (map #(compile-route % opts) routes)
|
||||||
|
names (find-names routes opts)
|
||||||
[data lookup] (reduce
|
[data lookup] (reduce
|
||||||
(fn [[data lookup] [p {:keys [name] :as meta} handler]]
|
(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)
|
[(conj data route)
|
||||||
(if name
|
(if name (assoc lookup name f) lookup)]))
|
||||||
(assoc lookup name #(->Match p meta (impl/path-for route %) handler %))
|
[[] {}] compiled)
|
||||||
lookup)])) [[] {}] compiled)]
|
lookup (impl/fast-map lookup)]
|
||||||
(->LinearRouter routes data lookup))))
|
(reify
|
||||||
|
Routing
|
||||||
(defrecord LookupRouter [routes data lookup]
|
(router-type [_]
|
||||||
Routing
|
:linear-router)
|
||||||
(routes [_]
|
(routes [_]
|
||||||
routes)
|
routes)
|
||||||
(match-by-path [_ path]
|
(options [_]
|
||||||
(data path))
|
opts)
|
||||||
(match-by-name [_ name]
|
(route-names [_]
|
||||||
(if-let [match (lookup name)]
|
names)
|
||||||
(match nil)))
|
(match-by-path [_ path]
|
||||||
(match-by-name [_ name params]
|
(reduce
|
||||||
(if-let [match (lookup name)]
|
(fn [acc ^Route route]
|
||||||
(match params))))
|
(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
|
(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"
|
expanded options. See [[router]] for available options"
|
||||||
([routes]
|
([routes]
|
||||||
(lookup-router routes {}))
|
(lookup-router routes {}))
|
||||||
|
|
@ -134,13 +153,32 @@
|
||||||
{:route route
|
{:route route
|
||||||
:routes routes})))
|
:routes routes})))
|
||||||
(let [compiled (map #(compile-route % opts) routes)
|
(let [compiled (map #(compile-route % opts) routes)
|
||||||
|
names (find-names routes opts)
|
||||||
[data lookup] (reduce
|
[data lookup] (reduce
|
||||||
(fn [[data lookup] [p {:keys [name] :as meta} handler]]
|
(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
|
(if name
|
||||||
(assoc lookup name #(->Match p meta p handler %))
|
(assoc lookup name #(->Match p meta handler % p))
|
||||||
lookup)]) [{} {}] compiled)]
|
lookup)]) [{} {}] compiled)
|
||||||
(->LookupRouter routes data lookup))))
|
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
|
(defn router
|
||||||
"Create a [[Router]] from raw route data and optionally an options map.
|
"Create a [[Router]] from raw route data and optionally an options map.
|
||||||
|
|
@ -153,8 +191,8 @@
|
||||||
| `:routes` | Initial resolved routes (default `[]`)
|
| `:routes` | Initial resolved routes (default `[]`)
|
||||||
| `:meta` | Initial expanded route-meta vector (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`)
|
| `: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`
|
| `:coerce` | Function of `route opts => route` to coerce resolved route, can throw or return `nil`
|
||||||
| `:compile` | Function of `[path meta] opts => handler` to compile a route handler"
|
| `:compile` | Function of `route opts => handler` to compile a route handler"
|
||||||
([data]
|
([data]
|
||||||
(router data {}))
|
(router data {}))
|
||||||
([data opts]
|
([data opts]
|
||||||
|
|
|
||||||
|
|
@ -122,12 +122,28 @@
|
||||||
:handler handler})))
|
:handler handler})))
|
||||||
|
|
||||||
(defn path-for [^Route route params]
|
(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)
|
(let [defined (-> params keys set)
|
||||||
required (:params route)
|
|
||||||
missing (clojure.set/difference required defined)]
|
missing (clojure.set/difference required defined)]
|
||||||
(throw
|
(throw
|
||||||
(ex-info
|
(ex-info
|
||||||
(str "missing path-params for route '" (:path route) "': " missing)
|
(str "missing path-params for route " template ": " missing)
|
||||||
{:params params, :required required}))))
|
{:params params, :required required})))))
|
||||||
(str "/" (str/join \/ (map #(get params % %) (:parts route)))))
|
|
||||||
|
(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)]))
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
(ns reitit.ring
|
(ns reitit.ring
|
||||||
(:require [meta-merge.core :refer [meta-merge]]
|
(:require [meta-merge.core :refer [meta-merge]]
|
||||||
[reitit.middleware :as middleware]
|
[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})
|
(def http-methods #{:get :head :patch :delete :options :post :put})
|
||||||
(defrecord MethodHandlers [get head patch delete options post put])
|
(defrecord MethodHandlers [get head patch delete options post put])
|
||||||
|
|
@ -18,10 +19,10 @@
|
||||||
(fn
|
(fn
|
||||||
([request]
|
([request]
|
||||||
(if-let [match (reitit/match-by-path router (:uri 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]
|
([request respond raise]
|
||||||
(if-let [match (reitit/match-by-path router (:uri request))]
|
(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}))
|
{::router router}))
|
||||||
|
|
||||||
(defn get-router [handler]
|
(defn get-router [handler]
|
||||||
|
|
@ -46,14 +47,13 @@
|
||||||
#(assoc %1 %2 (middleware/compile-handler
|
#(assoc %1 %2 (middleware/compile-handler
|
||||||
[path (meta-merge top %3)] opts %2))
|
[path (meta-merge top %3)] opts %2))
|
||||||
{} childs))
|
{} childs))
|
||||||
default-handler (if (:handler top) (middleware/compile-handler [path meta] opts))
|
default-handler (if (:handler top) (middleware/compile-handler [path meta] opts))]
|
||||||
resolved-handler #(or (% handlers) default-handler)]
|
|
||||||
(fn
|
(fn
|
||||||
([request]
|
([request]
|
||||||
(if-let [handler (resolved-handler (:request-method request))]
|
(if-let [handler (or ((:request-method request) handlers) default-handler)]
|
||||||
(handler request)))
|
(handler request)))
|
||||||
([request respond raise]
|
([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))))))))
|
(handler request respond raise))))))))
|
||||||
|
|
||||||
(defn router
|
(defn router
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,18 @@
|
||||||
(ns reitit.core-test
|
(ns reitit.core-test
|
||||||
(:require [clojure.test :refer [deftest testing is]]
|
(:require [clojure.test :refer [deftest testing is]]
|
||||||
[reitit.core :as reitit #?@(:cljs [:refer [Match LinearRouter LookupRouter]])])
|
[reitit.core :as reitit #?@(:cljs [:refer [Match]])])
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(:import (reitit.core Match LinearRouter LookupRouter)
|
(:import (reitit.core Match)
|
||||||
(clojure.lang ExceptionInfo))))
|
(clojure.lang ExceptionInfo))))
|
||||||
|
|
||||||
(deftest reitit-test
|
(deftest reitit-test
|
||||||
|
|
||||||
(testing "linear router"
|
(testing "linear router"
|
||||||
(let [router (reitit/router ["/api" ["/ipa" ["/:size" ::beer]]])]
|
(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}]]
|
(is (= [["/api/ipa/:size" {:name ::beer}]]
|
||||||
(reitit/routes router)))
|
(reitit/routes router)))
|
||||||
|
(is (= true (map? (reitit/options router))))
|
||||||
(is (= (reitit/map->Match
|
(is (= (reitit/map->Match
|
||||||
{:template "/api/ipa/:size"
|
{:template "/api/ipa/:size"
|
||||||
:meta {:name ::beer}
|
:meta {:name ::beer}
|
||||||
|
|
@ -25,17 +26,26 @@
|
||||||
:params {:size "large"}})
|
:params {:size "large"}})
|
||||||
(reitit/match-by-name router ::beer {:size "large"})))
|
(reitit/match-by-name router ::beer {:size "large"})))
|
||||||
(is (= nil (reitit/match-by-name router "ILLEGAL")))
|
(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?
|
(is (thrown-with-msg?
|
||||||
ExceptionInfo
|
ExceptionInfo
|
||||||
#"^missing path-params for route '/api/ipa/:size': \#\{:size\}$"
|
#"^missing path-params for route /api/ipa/:size: \#\{:size\}$"
|
||||||
(reitit/match-by-name router ::beer))))))
|
(reitit/match-by-name! router ::beer))))))
|
||||||
|
|
||||||
(testing "lookup router"
|
(testing "lookup router"
|
||||||
(let [router (reitit/router ["/api" ["/ipa" ["/large" ::beer]]])]
|
(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}]]
|
(is (= [["/api/ipa/large" {:name ::beer}]]
|
||||||
(reitit/routes router)))
|
(reitit/routes router)))
|
||||||
|
(is (= true (map? (reitit/options router))))
|
||||||
(is (= (reitit/map->Match
|
(is (= (reitit/map->Match
|
||||||
{:template "/api/ipa/large"
|
{:template "/api/ipa/large"
|
||||||
:meta {:name ::beer}
|
:meta {:name ::beer}
|
||||||
|
|
@ -49,6 +59,7 @@
|
||||||
:params {:size "large"}})
|
:params {:size "large"}})
|
||||||
(reitit/match-by-name router ::beer {:size "large"})))
|
(reitit/match-by-name router ::beer {:size "large"})))
|
||||||
(is (= nil (reitit/match-by-name router "ILLEGAL")))
|
(is (= nil (reitit/match-by-name router "ILLEGAL")))
|
||||||
|
(is (= [::beer] (reitit/route-names router)))
|
||||||
(testing "can't be created with wildcard routes"
|
(testing "can't be created with wildcard routes"
|
||||||
(is (thrown-with-msg?
|
(is (thrown-with-msg?
|
||||||
ExceptionInfo
|
ExceptionInfo
|
||||||
|
|
@ -108,14 +119,14 @@
|
||||||
(let [pong (constantly "ok")
|
(let [pong (constantly "ok")
|
||||||
routes ["/api" {:mw [:api]}
|
routes ["/api" {:mw [:api]}
|
||||||
["/ping" :kikka]
|
["/ping" :kikka]
|
||||||
["/user/:id" {:parameters {:id String}}
|
["/user/:id" {:parameters {:id "String"}}
|
||||||
["/:sub-id" {:parameters {:sub-id String}}]]
|
["/:sub-id" {:parameters {:sub-id "String"}}]]
|
||||||
["/pong" pong]
|
["/pong" pong]
|
||||||
["/admin" {:mw [:admin] :roles #{:admin}}
|
["/admin" {:mw [:admin] :roles #{:admin}}
|
||||||
["/user" {:roles ^:replace #{:user}}]
|
["/user" {:roles ^:replace #{:user}}]
|
||||||
["/db" {:mw [:db]}]]]
|
["/db" {:mw [:db]}]]]
|
||||||
expected [["/api/ping" {:mw [:api], :name :kikka}]
|
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/pong" {:mw [:api], :handler pong}]
|
||||||
["/api/admin/user" {:mw [:api :admin], :roles #{:user}}]
|
["/api/admin/user" {:mw [:api :admin], :roles #{:user}}]
|
||||||
["/api/admin/db" {:mw [:api :admin :db], :roles #{:admin}}]]
|
["/api/admin/db" {:mw [:api :admin :db], :roles #{:admin}}]]
|
||||||
|
|
@ -123,7 +134,7 @@
|
||||||
(is (= expected (reitit/resolve-routes routes {})))
|
(is (= expected (reitit/resolve-routes routes {})))
|
||||||
(is (= (reitit/map->Match
|
(is (= (reitit/map->Match
|
||||||
{:template "/api/user/:id/:sub-id"
|
{: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"
|
:path "/api/user/1/2"
|
||||||
:params {:id "1", :sub-id "2"}})
|
:params {:id "1", :sub-id "2"}})
|
||||||
(reitit/match-by-path router "/api/user/1/2"))))))
|
(reitit/match-by-path router "/api/user/1/2"))))))
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@
|
||||||
(:require [clojure.test :refer [deftest testing is]]
|
(:require [clojure.test :refer [deftest testing is]]
|
||||||
[reitit.middleware :as middleware]
|
[reitit.middleware :as middleware]
|
||||||
[reitit.ring :as ring]
|
[reitit.ring :as ring]
|
||||||
[clojure.set :as set])
|
[clojure.set :as set]
|
||||||
|
[reitit.core :as reitit])
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(:import (clojure.lang ExceptionInfo))))
|
(:import (clojure.lang ExceptionInfo))))
|
||||||
|
|
||||||
|
|
@ -122,7 +123,30 @@
|
||||||
respond (partial reset! result), raise ::not-called]
|
respond (partial reset! result), raise ::not-called]
|
||||||
(app {:uri "/api/users" :request-method :post} respond raise)
|
(app {:uri "/api/users" :request-method :post} respond raise)
|
||||||
(is (= {:status 200, :body [:api :users :post :ok :post :users :api]}
|
(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]
|
(defn wrap-enforce-roles [handler]
|
||||||
(fn [{:keys [::roles] :as request}]
|
(fn [{:keys [::roles] :as request}]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue