mirror of
https://github.com/metosin/reitit.git
synced 2025-12-17 08:21:11 +00:00
Middleware can also contribute to router specs
This commit is contained in:
parent
70209aabce
commit
9273f99806
3 changed files with 55 additions and 18 deletions
|
|
@ -6,7 +6,7 @@
|
||||||
(defprotocol IntoMiddleware
|
(defprotocol IntoMiddleware
|
||||||
(into-middleware [this data opts]))
|
(into-middleware [this data opts]))
|
||||||
|
|
||||||
(defrecord Middleware [name wrap])
|
(defrecord Middleware [name wrap spec])
|
||||||
(defrecord Endpoint [data handler middleware])
|
(defrecord Endpoint [data handler middleware])
|
||||||
|
|
||||||
(def ^:dynamic *max-compile-depth* 10)
|
(def ^:dynamic *max-compile-depth* 10)
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,13 @@
|
||||||
(ns reitit.ring.spec
|
(ns reitit.ring.spec
|
||||||
(:require [clojure.spec.alpha :as s]
|
(:require [clojure.spec.alpha :as s]
|
||||||
[reitit.middleware #?@(:cljs [:refer [Middleware]])]
|
[reitit.middleware :as middleware]
|
||||||
[reitit.spec :as rs])
|
[reitit.spec :as rs]))
|
||||||
#?(:clj
|
|
||||||
(:import (reitit.middleware Middleware))))
|
|
||||||
|
|
||||||
;;
|
;;
|
||||||
;; Specs
|
;; Specs
|
||||||
;;
|
;;
|
||||||
|
|
||||||
(s/def ::middleware (s/coll-of (partial instance? Middleware)))
|
(s/def ::middleware (s/coll-of (partial satisfies? middleware/IntoMiddleware)))
|
||||||
|
|
||||||
(s/def ::data
|
(s/def ::data
|
||||||
(s/keys :req-un [::rs/handler]
|
(s/keys :req-un [::rs/handler]
|
||||||
|
|
@ -19,10 +17,22 @@
|
||||||
;; Validator
|
;; Validator
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
(defn merge-specs [specs]
|
||||||
|
(when-let [non-specs (seq (remove #(or (s/spec? %) (s/get-spec %)) specs))]
|
||||||
|
(throw
|
||||||
|
(ex-info
|
||||||
|
(str "Not all specs satisfy the Spec protocol: " non-specs)
|
||||||
|
{:specs specs
|
||||||
|
:non-specs non-specs})))
|
||||||
|
(s/merge-spec-impl (vec specs) (vec specs) nil))
|
||||||
|
|
||||||
(defn- validate-ring-route-data [routes spec]
|
(defn- validate-ring-route-data [routes spec]
|
||||||
(->> (for [[p _ c] routes
|
(->> (for [[p _ c] routes
|
||||||
[method {:keys [data] :as endpoint}] c
|
[method {:keys [data middleware] :as endpoint}] c
|
||||||
:when endpoint]
|
:when endpoint
|
||||||
|
:let [mw-specs (seq (keep :spec middleware))
|
||||||
|
specs (keep identity (into [spec] mw-specs))
|
||||||
|
spec (merge-specs specs)]]
|
||||||
(when-let [problems (and spec (s/explain-data spec data))]
|
(when-let [problems (and spec (s/explain-data spec data))]
|
||||||
(rs/->Problem p method data spec problems)))
|
(rs/->Problem p method data spec problems)))
|
||||||
(keep identity) (seq)))
|
(keep identity) (seq)))
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,14 @@
|
||||||
(:require [clojure.test :refer [deftest testing is]]
|
(:require [clojure.test :refer [deftest testing is]]
|
||||||
[reitit.ring :as ring]
|
[reitit.ring :as ring]
|
||||||
[reitit.ring.spec :as rrs]
|
[reitit.ring.spec :as rrs]
|
||||||
|
[clojure.spec.alpha :as s]
|
||||||
[reitit.core :as r]
|
[reitit.core :as r]
|
||||||
[reitit.spec :as rs])
|
[expound.alpha :as e])
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(:import (clojure.lang ExceptionInfo))))
|
(:import (clojure.lang ExceptionInfo))))
|
||||||
|
|
||||||
|
(s/def ::role #{:admin :user})
|
||||||
|
(s/def ::roles (s/and (s/coll-of ::role :into #{}) set?))
|
||||||
|
|
||||||
(deftest route-data-validation-test
|
(deftest route-data-validation-test
|
||||||
(testing "validation is turned off by default"
|
(testing "validation is turned off by default"
|
||||||
|
|
@ -27,13 +30,6 @@
|
||||||
(ring/router
|
(ring/router
|
||||||
["/api" {:handler identity
|
["/api" {:handler identity
|
||||||
:name "kikka"}]
|
:name "kikka"}]
|
||||||
{:validate rrs/validate-spec!})))
|
|
||||||
(is (thrown-with-msg?
|
|
||||||
ExceptionInfo
|
|
||||||
#"Invalid route data"
|
|
||||||
(ring/router
|
|
||||||
["/api" {:handler identity
|
|
||||||
:middleware [{}]}]
|
|
||||||
{:validate rrs/validate-spec!}))))
|
{:validate rrs/validate-spec!}))))
|
||||||
|
|
||||||
(testing "all endpoints are validated"
|
(testing "all endpoints are validated"
|
||||||
|
|
@ -48,5 +44,36 @@
|
||||||
(is (true? (r/router?
|
(is (true? (r/router?
|
||||||
(ring/router
|
(ring/router
|
||||||
["/api" {:handler "identity"}]
|
["/api" {:handler "identity"}]
|
||||||
{:spec any?
|
{:spec (s/spec any?)
|
||||||
:validate rrs/validate-spec!}))))))
|
:validate rrs/validate-spec!}))))
|
||||||
|
|
||||||
|
(testing "predicates are not allowed"
|
||||||
|
(is (thrown-with-msg?
|
||||||
|
ExceptionInfo
|
||||||
|
#"Not all specs satisfy the Spec protocol"
|
||||||
|
(ring/router
|
||||||
|
["/api" {:handler "identity"}]
|
||||||
|
{:spec any?
|
||||||
|
:validate rrs/validate-spec!})))))
|
||||||
|
|
||||||
|
(testing "middleware can contribute to specs"
|
||||||
|
(is (true? (r/router?
|
||||||
|
(ring/router
|
||||||
|
["/api" {:get {:handler identity
|
||||||
|
:roles #{:admin}}}]
|
||||||
|
{:validate rrs/validate-spec!
|
||||||
|
:data {:middleware [{:spec (s/keys :opt-un [::roles])
|
||||||
|
:wrap (fn [handler]
|
||||||
|
(fn [request]
|
||||||
|
(handler request)))}]}}))))
|
||||||
|
(is (thrown-with-msg?
|
||||||
|
ExceptionInfo
|
||||||
|
#"Invalid route data"
|
||||||
|
(ring/router
|
||||||
|
["/api" {:get {:handler identity
|
||||||
|
:roles #{:adminz}}}]
|
||||||
|
{:validate rrs/validate-spec!
|
||||||
|
:data {:middleware [{:spec (s/keys :opt-un [::roles])
|
||||||
|
:wrap (fn [handler]
|
||||||
|
(fn [request]
|
||||||
|
(handler request)))}]}})))))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue