mirror of
https://github.com/metosin/reitit.git
synced 2025-12-17 00:11:11 +00:00
commit
0bcfda755f
10 changed files with 196 additions and 89 deletions
18
CHANGELOG.md
18
CHANGELOG.md
|
|
@ -21,6 +21,24 @@ We use [Break Versioning][breakver]. The version numbers follow a `<major>.<mino
|
||||||
[metosin/jsonista "0.2.3"] is available but we use "0.2.2"
|
[metosin/jsonista "0.2.3"] is available but we use "0.2.2"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `reitit-core`
|
||||||
|
|
||||||
|
* Add support for explixit selection of router path-parameter `:syntax`, fixes [#276](https://github.com/metosin/reitit/issues/276)
|
||||||
|
|
||||||
|
```clj
|
||||||
|
(require '[reitit.core :as r])
|
||||||
|
|
||||||
|
(-> (r/router
|
||||||
|
["http://localhost:8080/api/user/{id}" ::user-by-id]
|
||||||
|
{:syntax :bracket})
|
||||||
|
(r/match-by-path "http://localhost:8080/api/user/123"))
|
||||||
|
;#Match{:template "http://localhost:8080/api/user/{id}",
|
||||||
|
; :data {:name :user/user-by-id},
|
||||||
|
; :result nil,
|
||||||
|
; :path-params {:id "123"},
|
||||||
|
; :path "http://localhost:8080/api/user/123"}
|
||||||
|
```
|
||||||
|
|
||||||
## 0.3.7 (2019-05-25)
|
## 0.3.7 (2019-05-25)
|
||||||
|
|
||||||
### `reitit-pedestal`
|
### `reitit-pedestal`
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,13 @@
|
||||||
|
|
||||||
Routers can be configured via options. The following options are available for the `reitit.core/router`:
|
Routers can be configured via options. The following options are available for the `reitit.core/router`:
|
||||||
|
|
||||||
| key | description |
|
| key | description
|
||||||
|--------------|-------------|
|
|--------------|-------------
|
||||||
| `:path` | Base-path for routes
|
| `:path` | Base-path for routes
|
||||||
| `:routes` | Initial resolved routes (default `[]`)
|
| `:routes` | Initial resolved routes (default `[]`)
|
||||||
| `:data` | Initial route data (default `{}`)
|
| `:data` | Initial route data (default `{}`)
|
||||||
| `:spec` | clojure.spec definition for a route data, see `reitit.spec` on how to use this
|
| `:spec` | clojure.spec definition for a route data, see `reitit.spec` on how to use this
|
||||||
|
| `:syntax` | Path-parameter syntax as keyword or set of keywords (default #{:bracket :colon})
|
||||||
| `:expand` | Function of `arg opts => data` to expand route arg to route data (default `reitit.core/expand`)
|
| `:expand` | Function of `arg opts => data` to expand route arg to route data (default `reitit.core/expand`)
|
||||||
| `:coerce` | Function of `route opts => route` to coerce resolved route, can throw or return `nil`
|
| `:coerce` | Function of `route opts => route` to coerce resolved route, can throw or return `nil`
|
||||||
| `:compile` | Function of `route opts => result` to compile a route handler
|
| `:compile` | Function of `route opts => result` to compile a route handler
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ Routes are defined as vectors of String path and optional (non-sequential) route
|
||||||
|
|
||||||
Routes can be wrapped in vectors and lists and `nil` routes are ignored.
|
Routes can be wrapped in vectors and lists and `nil` routes are ignored.
|
||||||
|
|
||||||
Paths can have path-parameters (`:id`) or catch-all-parameters (`*path`). Since version `0.3.0`, parameters can also be wrapped in brackets, enabling use of qualified keywords `{user/id}`, `{*user/path}`. The non-bracket syntax might be deprecated later.
|
Paths can have path-parameters (`:id`) or catch-all-parameters (`*path`). Parameters can also be wrapped in brackets, enabling use of qualified keywords `{user/id}`, `{*user/path}`. By default, both syntaxes are supported, see [configuring routers](../advanced/configuring_routers.md) on how to change this.
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
|
|
@ -129,3 +129,23 @@ Routes are just data, so it's easy to create them programmatically:
|
||||||
; ["/add-user" {:post {:interceptors [add-user]}}]
|
; ["/add-user" {:post {:interceptors [add-user]}}]
|
||||||
; ["/add-order" {:post {:interceptors [add-order]}}])]
|
; ["/add-order" {:post {:interceptors [add-order]}}])]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Explicit path-parameter syntax
|
||||||
|
|
||||||
|
Router options `:syntax` allows the path-parameter syntax to be explicitely defined. It takes a keyword or set of keywords as a value. Valid values are `:colon` and `:bracket`. Default value is `#{:colon :bracket}`.
|
||||||
|
|
||||||
|
Supporting only `:bracket` syntax:
|
||||||
|
|
||||||
|
```clj
|
||||||
|
(require '[reitit.core :as r])
|
||||||
|
|
||||||
|
(-> (r/router
|
||||||
|
["http://localhost:8080/api/user/{id}" ::user-by-id]
|
||||||
|
{:syntax :bracket})
|
||||||
|
(r/match-by-path "http://localhost:8080/api/user/123"))
|
||||||
|
;#Match{:template "http://localhost:8080/api/user/{id}",
|
||||||
|
; :data {:name :user/user-by-id},
|
||||||
|
; :result nil,
|
||||||
|
; :path-params {:id "123"},
|
||||||
|
; :path "http://localhost:8080/api/user/123"}
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@
|
||||||
names (impl/find-names compiled-routes opts)
|
names (impl/find-names compiled-routes opts)
|
||||||
[pl nl] (reduce
|
[pl nl] (reduce
|
||||||
(fn [[pl nl] [p {:keys [name] :as data} result]]
|
(fn [[pl nl] [p {:keys [name] :as data} result]]
|
||||||
(let [{:keys [path-params] :as route} (impl/parse p)
|
(let [{:keys [path-params] :as route} (impl/parse p opts)
|
||||||
f #(if-let [path (impl/path-for route %)]
|
f #(if-let [path (impl/path-for route %)]
|
||||||
(->Match p data result (impl/url-decode-coll %) path)
|
(->Match p data result (impl/url-decode-coll %) path)
|
||||||
(->PartialMatch p data result (impl/url-decode-coll %) path-params))]
|
(->PartialMatch p data result (impl/url-decode-coll %) path-params))]
|
||||||
|
|
@ -131,7 +131,7 @@
|
||||||
([compiled-routes]
|
([compiled-routes]
|
||||||
(lookup-router compiled-routes {}))
|
(lookup-router compiled-routes {}))
|
||||||
([compiled-routes opts]
|
([compiled-routes opts]
|
||||||
(when-let [wilds (seq (filter impl/wild-route? compiled-routes))]
|
(when-let [wilds (seq (filter (impl/->wild-route? opts) compiled-routes))]
|
||||||
(exception/fail!
|
(exception/fail!
|
||||||
(str "can't create :lookup-router with wildcard routes: " wilds)
|
(str "can't create :lookup-router with wildcard routes: " wilds)
|
||||||
{:wilds wilds
|
{:wilds wilds
|
||||||
|
|
@ -184,7 +184,7 @@
|
||||||
names (impl/find-names compiled-routes opts)
|
names (impl/find-names compiled-routes opts)
|
||||||
[pl nl] (reduce
|
[pl nl] (reduce
|
||||||
(fn [[pl nl] [p {:keys [name] :as data} result]]
|
(fn [[pl nl] [p {:keys [name] :as data} result]]
|
||||||
(let [{:keys [path-params] :as route} (impl/parse p)
|
(let [{:keys [path-params] :as route} (impl/parse p opts)
|
||||||
f #(if-let [path (impl/path-for route %)]
|
f #(if-let [path (impl/path-for route %)]
|
||||||
(->Match p data result (impl/url-decode-coll %) path)
|
(->Match p data result (impl/url-decode-coll %) path)
|
||||||
(->PartialMatch p data result (impl/url-decode-coll %) path-params))]
|
(->PartialMatch p data result (impl/url-decode-coll %) path-params))]
|
||||||
|
|
@ -227,7 +227,7 @@
|
||||||
([compiled-routes]
|
([compiled-routes]
|
||||||
(single-static-path-router compiled-routes {}))
|
(single-static-path-router compiled-routes {}))
|
||||||
([compiled-routes opts]
|
([compiled-routes opts]
|
||||||
(when (or (not= (count compiled-routes) 1) (some impl/wild-route? compiled-routes))
|
(when (or (not= (count compiled-routes) 1) (some (impl/->wild-route? opts) compiled-routes))
|
||||||
(exception/fail!
|
(exception/fail!
|
||||||
(str ":single-static-path-router requires exactly 1 static route: " compiled-routes)
|
(str ":single-static-path-router requires exactly 1 static route: " compiled-routes)
|
||||||
{:routes compiled-routes}))
|
{:routes compiled-routes}))
|
||||||
|
|
@ -266,7 +266,7 @@
|
||||||
([compiled-routes]
|
([compiled-routes]
|
||||||
(mixed-router compiled-routes {}))
|
(mixed-router compiled-routes {}))
|
||||||
([compiled-routes opts]
|
([compiled-routes opts]
|
||||||
(let [{wild true, lookup false} (group-by impl/wild-route? compiled-routes)
|
(let [{wild true, lookup false} (group-by (impl/->wild-route? opts) compiled-routes)
|
||||||
->static-router (if (= 1 (count lookup)) single-static-path-router lookup-router)
|
->static-router (if (= 1 (count lookup)) single-static-path-router lookup-router)
|
||||||
wildcard-router (trie-router wild opts)
|
wildcard-router (trie-router wild opts)
|
||||||
static-router (->static-router lookup opts)
|
static-router (->static-router lookup opts)
|
||||||
|
|
@ -301,7 +301,7 @@
|
||||||
([compiled-routes]
|
([compiled-routes]
|
||||||
(quarantine-router compiled-routes {}))
|
(quarantine-router compiled-routes {}))
|
||||||
([compiled-routes opts]
|
([compiled-routes opts]
|
||||||
(let [conflicting-paths (-> compiled-routes impl/path-conflicting-routes impl/conflicting-paths)
|
(let [conflicting-paths (-> compiled-routes (impl/path-conflicting-routes opts) impl/conflicting-paths)
|
||||||
conflicting? #(contains? conflicting-paths (first %))
|
conflicting? #(contains? conflicting-paths (first %))
|
||||||
{conflicting true, non-conflicting false} (group-by conflicting? compiled-routes)
|
{conflicting true, non-conflicting false} (group-by conflicting? compiled-routes)
|
||||||
linear-router (linear-router conflicting opts)
|
linear-router (linear-router conflicting opts)
|
||||||
|
|
@ -347,12 +347,13 @@
|
||||||
Selects implementation based on route details. The following options
|
Selects implementation based on route details. The following options
|
||||||
are available:
|
are available:
|
||||||
|
|
||||||
| key | description |
|
| key | description
|
||||||
| -------------|-------------|
|
| -------------|-------------
|
||||||
| `:path` | Base-path for routes
|
| `:path` | Base-path for routes
|
||||||
| `:routes` | Initial resolved routes (default `[]`)
|
| `:routes` | Initial resolved routes (default `[]`)
|
||||||
| `:data` | Initial route data (default `{}`)
|
| `:data` | Initial route data (default `{}`)
|
||||||
| `:spec` | clojure.spec definition for a route data, see `reitit.spec` on how to use this
|
| `:spec` | clojure.spec definition for a route data, see `reitit.spec` on how to use this
|
||||||
|
| `:syntax` | Path-parameter syntax as keyword or set of keywords (default #{:bracket :colon})
|
||||||
| `:expand` | Function of `arg opts => data` to expand route arg to route data (default `reitit.core/expand`)
|
| `:expand` | Function of `arg opts => data` to expand route arg to route data (default `reitit.core/expand`)
|
||||||
| `:coerce` | Function of `route opts => route` to coerce resolved route, can throw or return `nil`
|
| `:coerce` | Function of `route opts => route` to coerce resolved route, can throw or return `nil`
|
||||||
| `:compile` | Function of `route opts => result` to compile a route handler
|
| `:compile` | Function of `route opts => result` to compile a route handler
|
||||||
|
|
@ -366,11 +367,11 @@
|
||||||
(let [{:keys [router] :as opts} (merge (default-router-options) opts)]
|
(let [{:keys [router] :as opts} (merge (default-router-options) opts)]
|
||||||
(try
|
(try
|
||||||
(let [routes (impl/resolve-routes raw-routes opts)
|
(let [routes (impl/resolve-routes raw-routes opts)
|
||||||
path-conflicting (impl/path-conflicting-routes routes)
|
path-conflicting (impl/path-conflicting-routes routes opts)
|
||||||
name-conflicting (impl/name-conflicting-routes routes)
|
name-conflicting (impl/name-conflicting-routes routes)
|
||||||
compiled-routes (impl/compile-routes routes opts)
|
compiled-routes (impl/compile-routes routes opts)
|
||||||
wilds? (boolean (some impl/wild-route? compiled-routes))
|
wilds? (boolean (some (impl/->wild-route? opts) compiled-routes))
|
||||||
all-wilds? (every? impl/wild-route? compiled-routes)
|
all-wilds? (every? (impl/->wild-route? opts) compiled-routes)
|
||||||
router (cond
|
router (cond
|
||||||
router router
|
router router
|
||||||
(and (= 1 (count compiled-routes)) (not wilds?)) single-static-path-router
|
(and (= 1 (count compiled-routes)) (not wilds?)) single-static-path-router
|
||||||
|
|
|
||||||
|
|
@ -11,18 +11,19 @@
|
||||||
(java.util HashMap Map)
|
(java.util HashMap Map)
|
||||||
(java.net URLEncoder URLDecoder))))
|
(java.net URLEncoder URLDecoder))))
|
||||||
|
|
||||||
(defrecord Route [path path-parts path-params])
|
(defn parse [path opts]
|
||||||
|
(let [path #?(:clj (.intern ^String (trie/normalize path opts)) :cljs (trie/normalize path opts))
|
||||||
(defn parse [path]
|
path-parts (trie/split-path path opts)
|
||||||
(let [path #?(:clj (.intern ^String (trie/normalize path)) :cljs (trie/normalize path))
|
|
||||||
path-parts (trie/split-path path)
|
|
||||||
path-params (->> path-parts (remove string?) (map :value) set)]
|
path-params (->> path-parts (remove string?) (map :value) set)]
|
||||||
(map->Route {:path-params path-params
|
{:path-params path-params
|
||||||
:path-parts path-parts
|
:path-parts path-parts
|
||||||
:path path})))
|
:path path}))
|
||||||
|
|
||||||
(defn wild-route? [[path]]
|
(defn wild-path? [path opts]
|
||||||
(-> path parse :path-params seq boolean))
|
(-> path (parse opts) :path-params seq boolean))
|
||||||
|
|
||||||
|
(defn ->wild-route? [opts]
|
||||||
|
(fn [[path]] (-> path (parse opts) :path-params seq boolean)))
|
||||||
|
|
||||||
(defn maybe-map-values
|
(defn maybe-map-values
|
||||||
"Applies a function to every value of a map, updates the value if not nil.
|
"Applies a function to every value of a map, updates the value if not nil.
|
||||||
|
|
@ -74,14 +75,11 @@
|
||||||
(cond->> (->> (walk raw-routes opts) (map-data merge-data))
|
(cond->> (->> (walk raw-routes opts) (map-data merge-data))
|
||||||
coerce (into [] (keep #(coerce % opts)))))
|
coerce (into [] (keep #(coerce % opts)))))
|
||||||
|
|
||||||
(defn conflicting-routes? [route1 route2]
|
(defn path-conflicting-routes [routes opts]
|
||||||
(trie/conflicting-paths? (first route1) (first route2)))
|
|
||||||
|
|
||||||
(defn path-conflicting-routes [routes]
|
|
||||||
(-> (into {}
|
(-> (into {}
|
||||||
(comp (map-indexed (fn [index route]
|
(comp (map-indexed (fn [index route]
|
||||||
[route (into #{}
|
[route (into #{}
|
||||||
(filter (partial conflicting-routes? route))
|
(filter #(trie/conflicting-paths? (first route) (first %) opts))
|
||||||
(subvec routes (inc index)))]))
|
(subvec routes (inc index)))]))
|
||||||
(filter (comp seq second)))
|
(filter (comp seq second)))
|
||||||
routes)
|
routes)
|
||||||
|
|
@ -114,7 +112,7 @@
|
||||||
(defn uncompile-routes [routes]
|
(defn uncompile-routes [routes]
|
||||||
(mapv (comp vec (partial take 2)) routes))
|
(mapv (comp vec (partial take 2)) routes))
|
||||||
|
|
||||||
(defn path-for [^Route route path-params]
|
(defn path-for [route path-params]
|
||||||
(if (:path-params route)
|
(if (:path-params route)
|
||||||
(if-let [parts (reduce
|
(if-let [parts (reduce
|
||||||
(fn [acc part]
|
(fn [acc part]
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,12 @@
|
||||||
#?(:clj (:import [reitit Trie Trie$Match Trie$Matcher]
|
#?(:clj (:import [reitit Trie Trie$Match Trie$Matcher]
|
||||||
(java.net URLDecoder))))
|
(java.net URLDecoder))))
|
||||||
|
|
||||||
|
(defn ^:no-doc into-set [x]
|
||||||
|
(cond
|
||||||
|
(or (set? x) (sequential? x)) (set x)
|
||||||
|
(nil? x) #{}
|
||||||
|
:else (conj #{} x)))
|
||||||
|
|
||||||
(defrecord Wild [value])
|
(defrecord Wild [value])
|
||||||
(defrecord CatchAll [value])
|
(defrecord CatchAll [value])
|
||||||
(defrecord Match [params data])
|
(defrecord Match [params data])
|
||||||
|
|
@ -51,25 +57,36 @@
|
||||||
(keyword (subs s 0 i) (subs s (inc i)))
|
(keyword (subs s 0 i) (subs s (inc i)))
|
||||||
(keyword s)))
|
(keyword s)))
|
||||||
|
|
||||||
(defn split-path [s]
|
(defn split-path [s {:keys [syntax] :or {syntax #{:bracket :colon}}}]
|
||||||
(let [-static (fn [from to] (if-not (= from to) [(subs s from to)]))
|
(let [bracket? (-> syntax (into-set) :bracket)
|
||||||
|
colon? (-> syntax (into-set) :colon)
|
||||||
|
-static (fn [from to] (if-not (= from to) [(subs s from to)]))
|
||||||
-wild (fn [from to] [(->Wild (-keyword (subs s (inc from) to)))])
|
-wild (fn [from to] [(->Wild (-keyword (subs s (inc from) to)))])
|
||||||
-catch-all (fn [from to] [(->CatchAll (keyword (subs s (inc from) to)))])]
|
-catch-all (fn [from to] [(->CatchAll (keyword (subs s (inc from) to)))])]
|
||||||
(loop [ss nil, from 0, to 0]
|
(loop [ss nil, from 0, to 0]
|
||||||
(if (= to (count s))
|
(if (= to (count s))
|
||||||
(concat ss (-static from to))
|
(concat ss (-static from to))
|
||||||
(case (get s to)
|
(let [c (get s to)]
|
||||||
\{ (let [to' (or (str/index-of s "}" to) (ex/fail! ::unclosed-brackets {:path s}))]
|
(cond
|
||||||
|
|
||||||
|
(and bracket? (= \{ c))
|
||||||
|
(let [to' (or (str/index-of s "}" to) (ex/fail! ::unclosed-brackets {:path s}))]
|
||||||
(if (= \* (get s (inc to)))
|
(if (= \* (get s (inc to)))
|
||||||
(recur (concat ss (-static from to) (-catch-all (inc to) to')) (long (inc to')) (long (inc to')))
|
(recur (concat ss (-static from to) (-catch-all (inc to) to')) (long (inc to')) (long (inc to')))
|
||||||
(recur (concat ss (-static from to) (-wild to to')) (long (inc to')) (long (inc to')))))
|
(recur (concat ss (-static from to) (-wild to to')) (long (inc to')) (long (inc to')))))
|
||||||
\: (let [to' (or (str/index-of s "/" to) (count s))]
|
|
||||||
|
(and colon? (= \: c))
|
||||||
|
(let [to' (or (str/index-of s "/" to) (count s))]
|
||||||
(if (= 1 (- to' to))
|
(if (= 1 (- to' to))
|
||||||
(recur ss from (inc to))
|
(recur ss from (inc to))
|
||||||
(recur (concat ss (-static from to) (-wild to to')) (long to') (long to'))))
|
(recur (concat ss (-static from to) (-wild to to')) (long to') (long to'))))
|
||||||
\* (let [to' (count s)]
|
|
||||||
|
(and colon? (= \* c))
|
||||||
|
(let [to' (count s)]
|
||||||
(recur (concat ss (-static from to) (-catch-all to to')) (long to') (long to')))
|
(recur (concat ss (-static from to) (-catch-all to to')) (long to') (long to')))
|
||||||
(recur ss from (inc to)))))))
|
|
||||||
|
:else
|
||||||
|
(recur ss from (inc to))))))))
|
||||||
|
|
||||||
(defn join-path [xs]
|
(defn join-path [xs]
|
||||||
(reduce
|
(reduce
|
||||||
|
|
@ -80,8 +97,8 @@
|
||||||
(instance? CatchAll x) (str "{*" (-> x :value str (subs 1)) "}"))))
|
(instance? CatchAll x) (str "{*" (-> x :value str (subs 1)) "}"))))
|
||||||
"" xs))
|
"" xs))
|
||||||
|
|
||||||
(defn normalize [s]
|
(defn normalize [s opts]
|
||||||
(-> s (split-path) (join-path)))
|
(-> s (split-path opts) (join-path)))
|
||||||
|
|
||||||
;;
|
;;
|
||||||
;; Conflict Resolution
|
;; Conflict Resolution
|
||||||
|
|
@ -115,9 +132,9 @@
|
||||||
(concat [(subs x i)] xs)
|
(concat [(subs x i)] xs)
|
||||||
xs)))
|
xs)))
|
||||||
|
|
||||||
(defn conflicting-paths? [path1 path2]
|
(defn conflicting-paths? [path1 path2 opts]
|
||||||
(loop [parts1 (split-path path1)
|
(loop [parts1 (split-path path1 opts)
|
||||||
parts2 (split-path path2)]
|
parts2 (split-path path2 opts)]
|
||||||
(let [[[s1 & ss1] [s2 & ss2]] (-slice-start parts1 parts2)]
|
(let [[[s1 & ss1] [s2 & ss2]] (-slice-start parts1 parts2)]
|
||||||
(cond
|
(cond
|
||||||
(= s1 s2 nil) true
|
(= s1 s2 nil) true
|
||||||
|
|
@ -314,10 +331,10 @@
|
||||||
node routes))
|
node routes))
|
||||||
([node path data]
|
([node path data]
|
||||||
(insert node path data nil))
|
(insert node path data nil))
|
||||||
([node path data {::keys [parameters] :or {parameters map-parameters}}]
|
([node path data {::keys [parameters] :or {parameters map-parameters} :as opts}]
|
||||||
(let [parts (split-path path)
|
(let [parts (split-path path opts)
|
||||||
params (parameters (->> parts (remove string?) (map :value)))]
|
params (parameters (->> parts (remove string?) (map :value)))]
|
||||||
(-insert (or node (-node {})) (split-path path) path params data))))
|
(-insert (or node (-node {})) (split-path path opts) path params data))))
|
||||||
|
|
||||||
(defn compiler
|
(defn compiler
|
||||||
"Returns a default [[TrieCompiler]]."
|
"Returns a default [[TrieCompiler]]."
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
(ns reitit.swagger
|
(ns reitit.swagger
|
||||||
(:require [reitit.core :as r]
|
(:require [reitit.core :as r]
|
||||||
[reitit.impl :as impl]
|
|
||||||
[meta-merge.core :refer [meta-merge]]
|
[meta-merge.core :refer [meta-merge]]
|
||||||
[clojure.spec.alpha :as s]
|
[clojure.spec.alpha :as s]
|
||||||
[clojure.set :as set]
|
[clojure.set :as set]
|
||||||
|
|
@ -65,8 +64,8 @@
|
||||||
{:name ::swagger
|
{:name ::swagger
|
||||||
:spec ::spec})
|
:spec ::spec})
|
||||||
|
|
||||||
(defn- swagger-path [path]
|
(defn- swagger-path [path opts]
|
||||||
(-> path trie/normalize (str/replace #"\{\*" "{")))
|
(-> path (trie/normalize opts) (str/replace #"\{\*" "{")))
|
||||||
|
|
||||||
(defn create-swagger-handler []
|
(defn create-swagger-handler []
|
||||||
"Create a ring handler to emit swagger spec. Collects all routes from router which have
|
"Create a ring handler to emit swagger spec. Collects all routes from router which have
|
||||||
|
|
@ -74,15 +73,14 @@
|
||||||
(fn create-swagger
|
(fn create-swagger
|
||||||
([{:keys [::r/router ::r/match :request-method]}]
|
([{:keys [::r/router ::r/match :request-method]}]
|
||||||
(let [{:keys [id] :or {id ::default} :as swagger} (-> match :result request-method :data :swagger)
|
(let [{:keys [id] :or {id ::default} :as swagger} (-> match :result request-method :data :swagger)
|
||||||
->set (fn [x] (if (or (set? x) (sequential? x)) (set x) (conj #{} x)))
|
ids (trie/into-set id)
|
||||||
ids (->set id)
|
|
||||||
strip-top-level-keys #(dissoc % :id :info :host :basePath :definitions :securityDefinitions)
|
strip-top-level-keys #(dissoc % :id :info :host :basePath :definitions :securityDefinitions)
|
||||||
strip-endpoint-keys #(dissoc % :id :parameters :responses :summary :description)
|
strip-endpoint-keys #(dissoc % :id :parameters :responses :summary :description)
|
||||||
swagger (->> (strip-endpoint-keys swagger)
|
swagger (->> (strip-endpoint-keys swagger)
|
||||||
(merge {:swagger "2.0"
|
(merge {:swagger "2.0"
|
||||||
:x-id ids}))
|
:x-id ids}))
|
||||||
accept-route (fn [route]
|
accept-route (fn [route]
|
||||||
(-> route second :swagger :id (or ::default) ->set (set/intersection ids) seq))
|
(-> route second :swagger :id (or ::default) (trie/into-set) (set/intersection ids) seq))
|
||||||
transform-endpoint (fn [[method {{:keys [coercion no-doc swagger] :as data} :data
|
transform-endpoint (fn [[method {{:keys [coercion no-doc swagger] :as data} :data
|
||||||
middleware :middleware
|
middleware :middleware
|
||||||
interceptors :interceptors}]]
|
interceptors :interceptors}]]
|
||||||
|
|
@ -97,7 +95,7 @@
|
||||||
(strip-top-level-keys swagger))]))
|
(strip-top-level-keys swagger))]))
|
||||||
transform-path (fn [[p _ c]]
|
transform-path (fn [[p _ c]]
|
||||||
(if-let [endpoint (some->> c (keep transform-endpoint) (seq) (into {}))]
|
(if-let [endpoint (some->> c (keep transform-endpoint) (seq) (into {}))]
|
||||||
[(swagger-path p) endpoint]))]
|
[(swagger-path p (r/options router)) endpoint]))]
|
||||||
(let [map-in-order #(->> % (apply concat) (apply array-map))
|
(let [map-in-order #(->> % (apply concat) (apply array-map))
|
||||||
paths (->> router (r/compiled-routes) (filter accept-route) (map transform-path) map-in-order)]
|
paths (->> router (r/compiled-routes) (filter accept-route) (map transform-path) map-in-order)]
|
||||||
{:status 200
|
{:status 200
|
||||||
|
|
|
||||||
|
|
@ -292,7 +292,7 @@
|
||||||
(let [routes (impl/resolve-routes data (r/default-router-options))
|
(let [routes (impl/resolve-routes data (r/default-router-options))
|
||||||
conflicts (-> routes
|
conflicts (-> routes
|
||||||
(impl/resolve-routes (r/default-router-options))
|
(impl/resolve-routes (r/default-router-options))
|
||||||
(impl/path-conflicting-routes))]
|
(impl/path-conflicting-routes nil))]
|
||||||
(if conflicting? (seq conflicts) (nil? conflicts)))
|
(if conflicting? (seq conflicts) (nil? conflicts)))
|
||||||
|
|
||||||
true [["/a"]
|
true [["/a"]
|
||||||
|
|
@ -328,7 +328,7 @@
|
||||||
["/c" {}] #{["/*d" {}]}}
|
["/c" {}] #{["/*d" {}]}}
|
||||||
(-> [["/a"] ["/:b"] ["/c"] ["/*d"]]
|
(-> [["/a"] ["/:b"] ["/c"] ["/*d"]]
|
||||||
(impl/resolve-routes (r/default-router-options))
|
(impl/resolve-routes (r/default-router-options))
|
||||||
(impl/path-conflicting-routes)))))
|
(impl/path-conflicting-routes nil)))))
|
||||||
|
|
||||||
(testing "router with conflicting routes"
|
(testing "router with conflicting routes"
|
||||||
(testing "throws by default"
|
(testing "throws by default"
|
||||||
|
|
|
||||||
|
|
@ -2,27 +2,6 @@
|
||||||
(:require [clojure.test :refer [deftest testing is are]]
|
(:require [clojure.test :refer [deftest testing is are]]
|
||||||
[reitit.impl :as impl]))
|
[reitit.impl :as impl]))
|
||||||
|
|
||||||
(deftest conflicting-route-test
|
|
||||||
(are [c? p1 p2]
|
|
||||||
(is (= c? (impl/conflicting-routes? [p1] [p2])))
|
|
||||||
|
|
||||||
true "/a" "/a"
|
|
||||||
true "/a" "/:a"
|
|
||||||
true "/a/:b" "/:a/b"
|
|
||||||
true "/ab/:b" "/:a/ba"
|
|
||||||
true "/*a" "/:a/ba/ca"
|
|
||||||
|
|
||||||
true "/a" "/{a}"
|
|
||||||
true "/a/{b}" "/{a}/b"
|
|
||||||
true "/ab/{b}" "/{a}/ba"
|
|
||||||
true "/{*a}" "/{a}/ba/ca"
|
|
||||||
|
|
||||||
false "/a" "/:a/b"
|
|
||||||
false "/a" "/:a/b"
|
|
||||||
|
|
||||||
false "/a" "/{a}/b"
|
|
||||||
false "/a" "/{a}/b"))
|
|
||||||
|
|
||||||
(deftest strip-nils-test
|
(deftest strip-nils-test
|
||||||
(is (= {:a 1, :c false} (impl/strip-nils {:a 1, :b nil, :c false}))))
|
(is (= {:a 1, :c false} (impl/strip-nils {:a 1, :b nil, :c false}))))
|
||||||
|
|
||||||
|
|
@ -188,8 +167,7 @@
|
||||||
"%2B632+905+123+4567" "+632 905 123 4567"))
|
"%2B632+905+123+4567" "+632 905 123 4567"))
|
||||||
|
|
||||||
(deftest parse-test
|
(deftest parse-test
|
||||||
(is (= (impl/map->Route
|
(is (= {:path "https://google.com"
|
||||||
{:path "https://google.com"
|
|
||||||
:path-parts ["https://google.com"]
|
:path-parts ["https://google.com"]
|
||||||
:path-params #{}})
|
:path-params #{}}
|
||||||
(impl/parse "https://google.com"))))
|
(impl/parse "https://google.com" nil))))
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,92 @@
|
||||||
(:require [clojure.test :refer [deftest testing is are]]
|
(:require [clojure.test :refer [deftest testing is are]]
|
||||||
[reitit.trie :as trie]))
|
[reitit.trie :as trie]))
|
||||||
|
|
||||||
|
(deftest into-set-test
|
||||||
|
(is (= #{} (trie/into-set nil)))
|
||||||
|
(is (= #{} (trie/into-set [])))
|
||||||
|
(is (= #{1} (trie/into-set 1)))
|
||||||
|
(is (= #{1 2} (trie/into-set [1 2 1]))))
|
||||||
|
|
||||||
|
(deftest conflicting-paths?-test
|
||||||
|
(are [c? p1 p2]
|
||||||
|
(is (= c? (trie/conflicting-paths? p1 p2 nil)))
|
||||||
|
|
||||||
|
true "/a" "/a"
|
||||||
|
true "/a" "/:a"
|
||||||
|
true "/a/:b" "/:a/b"
|
||||||
|
true "/ab/:b" "/:a/ba"
|
||||||
|
true "/*a" "/:a/ba/ca"
|
||||||
|
|
||||||
|
true "/a" "/{a}"
|
||||||
|
true "/a/{b}" "/{a}/b"
|
||||||
|
true "/ab/{b}" "/{a}/ba"
|
||||||
|
true "/{*a}" "/{a}/ba/ca"
|
||||||
|
|
||||||
|
false "/a" "/:a/b"
|
||||||
|
false "/a" "/:a/b"
|
||||||
|
|
||||||
|
false "/a" "/{a}/b"
|
||||||
|
false "/a" "/{a}/b"))
|
||||||
|
|
||||||
|
(deftest split-path-test
|
||||||
|
(testing "colon"
|
||||||
|
(doseq [syntax [:colon #{:colon}]]
|
||||||
|
(are [path expected]
|
||||||
|
(is (= expected (trie/split-path path {:syntax syntax})))
|
||||||
|
|
||||||
|
"/olipa/:kerran/avaruus", ["/olipa/" (trie/->Wild :kerran) "/avaruus"]
|
||||||
|
"/olipa/{kerran}/avaruus", ["/olipa/{kerran}/avaruus"]
|
||||||
|
"/olipa/{a.b/c}/avaruus", ["/olipa/{a.b/c}/avaruus"]
|
||||||
|
"/olipa/kerran/*avaruus", ["/olipa/kerran/" (trie/->CatchAll :avaruus)]
|
||||||
|
"/olipa/kerran/{*avaruus}", ["/olipa/kerran/{" (trie/->CatchAll (keyword "avaruus}"))]
|
||||||
|
"/olipa/kerran/{*valtavan.suuri/avaruus}", ["/olipa/kerran/{" (trie/->CatchAll (keyword "valtavan.suuri/avaruus}"))])))
|
||||||
|
|
||||||
|
(testing "bracket"
|
||||||
|
(doseq [syntax [:bracket #{:bracket}]]
|
||||||
|
(are [path expected]
|
||||||
|
(is (= expected (trie/split-path path {:syntax syntax})))
|
||||||
|
|
||||||
|
"/olipa/:kerran/avaruus", ["/olipa/:kerran/avaruus"]
|
||||||
|
"/olipa/{kerran}/avaruus", ["/olipa/" (trie/->Wild :kerran) "/avaruus"]
|
||||||
|
"/olipa/{a.b/c}/avaruus", ["/olipa/" (trie/->Wild :a.b/c) "/avaruus"]
|
||||||
|
"/olipa/kerran/*avaruus", ["/olipa/kerran/*avaruus"]
|
||||||
|
"/olipa/kerran/{*avaruus}", ["/olipa/kerran/" (trie/->CatchAll :avaruus)]
|
||||||
|
"/olipa/kerran/{*valtavan.suuri/avaruus}", ["/olipa/kerran/" (trie/->CatchAll :valtavan.suuri/avaruus)])))
|
||||||
|
|
||||||
|
(testing "both"
|
||||||
|
(doseq [syntax [#{:bracket :colon}]]
|
||||||
|
(are [path expected]
|
||||||
|
(is (= expected (trie/split-path path {:syntax syntax})))
|
||||||
|
|
||||||
|
"/olipa/:kerran/avaruus", ["/olipa/" (trie/->Wild :kerran) "/avaruus"]
|
||||||
|
"/olipa/{kerran}/avaruus", ["/olipa/" (trie/->Wild :kerran) "/avaruus"]
|
||||||
|
"/olipa/{a.b/c}/avaruus", ["/olipa/" (trie/->Wild :a.b/c) "/avaruus"]
|
||||||
|
"/olipa/kerran/*avaruus", ["/olipa/kerran/" (trie/->CatchAll :avaruus)]
|
||||||
|
"/olipa/kerran/{*avaruus}", ["/olipa/kerran/" (trie/->CatchAll :avaruus)]
|
||||||
|
"/olipa/kerran/{*valtavan.suuri/avaruus}", ["/olipa/kerran/" (trie/->CatchAll :valtavan.suuri/avaruus)])))
|
||||||
|
|
||||||
|
(testing "nil"
|
||||||
|
(doseq [syntax [nil]]
|
||||||
|
(are [path expected]
|
||||||
|
(is (= expected (trie/split-path path {:syntax syntax})))
|
||||||
|
|
||||||
|
"/olipa/:kerran/avaruus", ["/olipa/:kerran/avaruus"]
|
||||||
|
"/olipa/{kerran}/avaruus", ["/olipa/{kerran}/avaruus"]
|
||||||
|
"/olipa/{a.b/c}/avaruus", ["/olipa/{a.b/c}/avaruus"]
|
||||||
|
"/olipa/kerran/*avaruus", ["/olipa/kerran/*avaruus"]
|
||||||
|
"/olipa/kerran/{*avaruus}", ["/olipa/kerran/{*avaruus}"]
|
||||||
|
"/olipa/kerran/{*valtavan.suuri/avaruus}", ["/olipa/kerran/{*valtavan.suuri/avaruus}"]))))
|
||||||
|
|
||||||
(deftest normalize-test
|
(deftest normalize-test
|
||||||
(are [path expected]
|
(are [path expected]
|
||||||
(is (= expected (trie/normalize path)))
|
(is (= expected (trie/normalize path nil)))
|
||||||
|
|
||||||
"/olipa/:kerran/avaruus", "/olipa/{kerran}/avaruus"
|
"/olipa/:kerran/avaruus", "/olipa/{kerran}/avaruus"
|
||||||
"/olipa/{kerran}/avaruus", "/olipa/{kerran}/avaruus"
|
"/olipa/{kerran}/avaruus", "/olipa/{kerran}/avaruus"
|
||||||
"/olipa/{a.b/c}/avaruus", "/olipa/{a.b/c}/avaruus"
|
"/olipa/{a.b/c}/avaruus", "/olipa/{a.b/c}/avaruus"
|
||||||
"/olipa/kerran/*avaruus", "/olipa/kerran/{*avaruus}"
|
"/olipa/kerran/*avaruus", "/olipa/kerran/{*avaruus}"
|
||||||
"/olipa/kerran/{*avaruus}", "/olipa/kerran/{*avaruus}"
|
"/olipa/kerran/{*avaruus}", "/olipa/kerran/{*avaruus}"
|
||||||
"/olipa/kerran/{*valvavan.suuri/avaruus}", "/olipa/kerran/{*valvavan.suuri/avaruus}"))
|
"/olipa/kerran/{*valtavan.suuri/avaruus}", "/olipa/kerran/{*valtavan.suuri/avaruus}"))
|
||||||
|
|
||||||
(deftest tests
|
(deftest tests
|
||||||
(is (= (trie/->Match {} {:a 1})
|
(is (= (trie/->Match {} {:a 1})
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue