From 1a9583b31bf5651a48d95e4b9cb0f6c955b635e5 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Tue, 26 Dec 2017 22:41:17 +0200 Subject: [PATCH] Support ring-route-data validation --- modules/reitit-ring/src/reitit/ring/spec.cljc | 33 ++++++++++++ test/cljc/reitit/ring_spec_test.cljc | 52 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 modules/reitit-ring/src/reitit/ring/spec.cljc create mode 100644 test/cljc/reitit/ring_spec_test.cljc diff --git a/modules/reitit-ring/src/reitit/ring/spec.cljc b/modules/reitit-ring/src/reitit/ring/spec.cljc new file mode 100644 index 00000000..96c407fa --- /dev/null +++ b/modules/reitit-ring/src/reitit/ring/spec.cljc @@ -0,0 +1,33 @@ +(ns reitit.ring.spec + (:require [clojure.spec.alpha :as s] + [reitit.middleware #?@(:cljs [:refer [Middleware]])] + [reitit.spec :as rs]) + #?(:clj + (:import (reitit.middleware Middleware)))) + +;; +;; Specs +;; + +(s/def ::middleware (s/coll-of (partial instance? Middleware))) + +(s/def ::data + (s/keys :req-un [::rs/handler] + :opt-un [::rs/name ::middleware])) + +;; +;; Validator +;; + +(defn- validate-ring-route-data [routes spec] + (->> (for [[p _ c] routes + [method {:keys [data] :as endpoint}] c + :when endpoint] + (when-let [problems (and spec (s/explain-data spec data))] + (rs/->Problem p method data spec problems))) + (keep identity) (seq))) + +(defn validate-spec! + [routes {:keys [spec ::rs/explain] :or {explain s/explain-str, spec ::data}}] + (when-let [problems (validate-ring-route-data routes spec)] + (rs/throw-on-problems! problems explain))) diff --git a/test/cljc/reitit/ring_spec_test.cljc b/test/cljc/reitit/ring_spec_test.cljc new file mode 100644 index 00000000..8d063d79 --- /dev/null +++ b/test/cljc/reitit/ring_spec_test.cljc @@ -0,0 +1,52 @@ +(ns reitit.ring-spec-test + (:require [clojure.test :refer [deftest testing is]] + [reitit.ring :as ring] + [reitit.ring.spec :as rrs] + [reitit.core :as r] + [reitit.spec :as rs]) + #?(:clj + (:import (clojure.lang ExceptionInfo)))) + + +(deftest route-data-validation-test + (testing "validation is turned off by default" + (is (true? (r/router? + (r/router + ["/api" {:handler "identity"}]))))) + + (testing "with default spec validates :name, :handler and :middleware" + (is (thrown-with-msg? + ExceptionInfo + #"Invalid route data" + (ring/router + ["/api" {:handler "identity"}] + {:validate rrs/validate-spec!}))) + (is (thrown-with-msg? + ExceptionInfo + #"Invalid route data" + (ring/router + ["/api" {:handler identity + :name "kikka"}] + {:validate rrs/validate-spec!}))) + (is (thrown-with-msg? + ExceptionInfo + #"Invalid route data" + (ring/router + ["/api" {:handler identity + :middleware [{}]}] + {:validate rrs/validate-spec!})))) + + (testing "all endpoints are validated" + (is (thrown-with-msg? + ExceptionInfo + #"Invalid route data" + (ring/router + ["/api" {:patch {:handler "identity"}}] + {:validate rrs/validate-spec!})))) + + (testing "spec can be overridden" + (is (true? (r/router? + (ring/router + ["/api" {:handler "identity"}] + {:spec any? + :validate rrs/validate-spec!}))))))