Validate routes for duplicates (fixes #23)

This commit is contained in:
Tommi Reiman 2017-08-21 08:44:51 +03:00
parent 0befddf72c
commit 851e35ef52
3 changed files with 60 additions and 1 deletions

View file

@ -60,6 +60,12 @@
(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 first-conflicting-routes [routes]
(loop [[r & rest] routes]
(if (seq rest)
(or (some #(if (impl/conflicting-routes? r %) [r %]) rest)
(recur rest)))))
(defn name-lookup [[_ {:keys [name]}] opts] (defn name-lookup [[_ {:keys [name]}] opts]
(if name #{name})) (if name #{name}))

View file

@ -121,6 +121,25 @@
:matcher #(if (= path %) {}) :matcher #(if (= path %) {})
:handler handler}))) :handler handler})))
(defn segments [path]
(let [ss (-> (str/split path #"/") rest vec)]
(if (str/ends-with? path "/")
(conj ss "") ss)))
(defn- catch-all? [segment]
(= \* (first segment)))
(defn conflicting-routes? [[p1 :as route1] [p2 :as route2]]
(loop [[s1 & ss1] (segments p1)
[s2 & ss2] (segments p2)]
(cond
(= s1 s2 nil) true
(or (nil? s1) (nil? s2)) false
(or (catch-all? s1) (catch-all? s2)) true
(or (wild? s1) (wild? s2)) (recur ss1 ss2)
(not= s1 s2) false
:else (recur ss1 ss2))))
(defn path-for [^Route route params] (defn path-for [^Route route params]
(if-let [required (:params route)] (if-let [required (:params route)]
(if (every? #(contains? params %) required) (if (every? #(contains? params %) required)

View file

@ -1,5 +1,5 @@
(ns reitit.core-test (ns reitit.core-test
(:require [clojure.test :refer [deftest testing is]] (:require [clojure.test :refer [deftest testing is are]]
[reitit.core :as reitit #?@(:cljs [:refer [Match]])]) [reitit.core :as reitit #?@(:cljs [:refer [Match]])])
#?(:clj #?(:clj
(:import (reitit.core Match) (:import (reitit.core Match)
@ -138,3 +138,37 @@
: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"))))))
(deftest first-conflicting-routes-test
(are [conflicting? data]
(let [routes (reitit/resolve-routes data {})]
(= (if conflicting? routes)
(reitit/first-conflicting-routes
(reitit/resolve-routes routes {}))))
true [["/a"]
["/a"]]
true [["/a"]
["/:b"]]
true [["/a"]
["/*b"]]
true [["/a/1/2"]
["/*b"]]
false [["/a"]
["/a/"]]
false [["/a"]
["/a/1"]]
false [["/a"]
["/a/:b"]]
false [["/a"]
["/a/*b"]]
true [["/v2/public/messages/dataset/bulk"]
["/v2/public/messages/dataset/:dataset-id"]]))