mirror of
https://github.com/metosin/reitit.git
synced 2026-01-29 17:20:36 +00:00
commit
b8720f581a
13 changed files with 196 additions and 83 deletions
13
README.md
13
README.md
|
|
@ -64,21 +64,22 @@ A Ring routing app with input & output coercion using [data-specs](https://githu
|
||||||
```clj
|
```clj
|
||||||
(require '[reitit.ring :as ring])
|
(require '[reitit.ring :as ring])
|
||||||
(require '[reitit.coercion.spec])
|
(require '[reitit.coercion.spec])
|
||||||
(require '[reitit.ring.coercion-middleware :as mw])
|
(require '[reitit.ring.coercion :as rrc])
|
||||||
|
|
||||||
(def app
|
(def app
|
||||||
(ring/ring-handler
|
(ring/ring-handler
|
||||||
(ring/router
|
(ring/router
|
||||||
["/api"
|
["/api"
|
||||||
["/math" {:get {:coercion reitit.coercion.spec/coercion
|
["/math" {:get {:parameters {:query {:x int?, :y int?}}
|
||||||
:parameters {:query {:x int?, :y int?}}
|
|
||||||
:responses {200 {:schema {:total pos-int?}}}
|
:responses {200 {:schema {:total pos-int?}}}
|
||||||
:handler (fn [{{{:keys [x y]} :query} :parameters}]
|
:handler (fn [{{{:keys [x y]} :query} :parameters}]
|
||||||
{:status 200
|
{:status 200
|
||||||
:body {:total (+ x y)}})}}]]
|
:body {:total (+ x y)}})}}]]
|
||||||
{:data {:middleware [mw/coerce-exceptions-middleware
|
;; router data effecting all routes
|
||||||
mw/coerce-request-middleware
|
{:data {:coercion reitit.coercion.spec/coercion
|
||||||
mw/coerce-response-middleware]}})))
|
:middleware [rrc/coerce-exceptions-middleware
|
||||||
|
rrc/coerce-request-middleware
|
||||||
|
rrc/coerce-response-middleware]}})))
|
||||||
```
|
```
|
||||||
|
|
||||||
Valid request:
|
Valid request:
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ The coerced parameters can be read under `:parameters` key in the request.
|
||||||
|
|
||||||
## Coercion Middleware
|
## Coercion Middleware
|
||||||
|
|
||||||
Defining a coercion for a route data doesn't do anything, as it's just data. We have to attach some code to apply the actual coercion. We can use the middleware from `reitit.ring.coercion-middleware`:
|
Defining a coercion for a route data doesn't do anything, as it's just data. We have to attach some code to apply the actual coercion. We can use the middleware from `reitit.ring.coercion`:
|
||||||
|
|
||||||
* `coerce-request-middleware` for the parameter coercion
|
* `coerce-request-middleware` for the parameter coercion
|
||||||
* `coerce-response-middleware` for the response coercion
|
* `coerce-response-middleware` for the response coercion
|
||||||
|
|
@ -57,7 +57,7 @@ Defining a coercion for a route data doesn't do anything, as it's just data. We
|
||||||
Here's an full example for applying coercion with Reitit, Ring and Schema:
|
Here's an full example for applying coercion with Reitit, Ring and Schema:
|
||||||
|
|
||||||
```clj
|
```clj
|
||||||
(require '[reitit.ring.coercion-middleware :as mw])
|
(require '[reitit.ring.coercion :as rrc])
|
||||||
(require '[reitit.coercion.schema])
|
(require '[reitit.coercion.schema])
|
||||||
(require '[reitit.ring :as ring])
|
(require '[reitit.ring :as ring])
|
||||||
(require '[schema.core :as s])
|
(require '[schema.core :as s])
|
||||||
|
|
@ -84,9 +84,9 @@ Here's an full example for applying coercion with Reitit, Ring and Schema:
|
||||||
(-> parameters :path :z))]
|
(-> parameters :path :z))]
|
||||||
{:status 200
|
{:status 200
|
||||||
:body {:total total}}))}}]]
|
:body {:total total}}))}}]]
|
||||||
{:data {:middleware [mw/coerce-exceptions-middleware
|
{:data {:middleware [rrc/coerce-exceptions-middleware
|
||||||
mw/coerce-request-middleware
|
rrc/coerce-request-middleware
|
||||||
mw/coerce-response-middleware]}})))
|
rrc/coerce-response-middleware]}})))
|
||||||
```
|
```
|
||||||
|
|
||||||
Valid request:
|
Valid request:
|
||||||
|
|
|
||||||
|
|
@ -47,15 +47,17 @@ To demonstrate the two approaches, below are response coercion middleware writte
|
||||||
* Route information is provided via a closure
|
* Route information is provided via a closure
|
||||||
* Pre-compiled coercers
|
* Pre-compiled coercers
|
||||||
* Mounts only if `:coercion` and `:responses` are defined for the route
|
* Mounts only if `:coercion` and `:responses` are defined for the route
|
||||||
|
* Also defines spec for the route data `:responses` for the [route data validation](route_data_validation.md).
|
||||||
|
|
||||||
```clj
|
```clj
|
||||||
(require '[reitit.middleware :as middleware])
|
(require '[reitit.spec :as rs])
|
||||||
|
|
||||||
(def coerce-response-middleware
|
(def coerce-response-middleware
|
||||||
"Middleware for pluggable response coercion.
|
"Middleware for pluggable response coercion.
|
||||||
Expects a :coercion of type `reitit.coercion/Coercion`
|
Expects a :coercion of type `reitit.coercion/Coercion`
|
||||||
and :responses from route data, otherwise does not mount."
|
and :responses from route data, otherwise does not mount."
|
||||||
{:name ::coerce-response
|
{:name ::coerce-response
|
||||||
|
:spec ::rs/responses
|
||||||
:compile (fn [{:keys [coercion responses]} opts]
|
:compile (fn [{:keys [coercion responses]} opts]
|
||||||
(if (and coercion responses)
|
(if (and coercion responses)
|
||||||
(let [coercers (coercion/response-coercers coercion responses opts)]
|
(let [coercers (coercion/response-coercers coercion responses opts)]
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
(ns example.server
|
(ns example.server
|
||||||
(:require [ring.adapter.jetty :as jetty]
|
(:require [ring.adapter.jetty :as jetty]
|
||||||
[reitit.middleware :as middleware]
|
[reitit.middleware :as middleware]
|
||||||
[reitit.ring.coercion-middleware :as coercion-middleware]))
|
[reitit.ring.coercion :as rrc]))
|
||||||
|
|
||||||
(defonce ^:private server (atom nil))
|
(defonce ^:private server (atom nil))
|
||||||
|
|
||||||
|
|
@ -10,9 +10,9 @@
|
||||||
;; to be set with :extract-request-format and extract-response-format
|
;; to be set with :extract-request-format and extract-response-format
|
||||||
(defn wrap-coercion [handler resource]
|
(defn wrap-coercion [handler resource]
|
||||||
(middleware/chain
|
(middleware/chain
|
||||||
[coercion-middleware/coerce-request-middleware
|
[rrc/coerce-request-middleware
|
||||||
coercion-middleware/coerce-response-middleware
|
rrc/coerce-response-middleware
|
||||||
coercion-middleware/coerce-exceptions-middleware]
|
rrc/coerce-exceptions-middleware]
|
||||||
handler
|
handler
|
||||||
resource))
|
resource))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
[ring.middleware.params]
|
[ring.middleware.params]
|
||||||
[muuntaja.middleware]
|
[muuntaja.middleware]
|
||||||
[reitit.ring :as ring]
|
[reitit.ring :as ring]
|
||||||
[reitit.ring.coercion-middleware :as coercion-middleware]
|
[reitit.ring.coercion :as rrc]
|
||||||
[example.dspec]
|
[example.dspec]
|
||||||
[example.schema]
|
[example.schema]
|
||||||
[example.spec]))
|
[example.spec]))
|
||||||
|
|
@ -18,9 +18,9 @@
|
||||||
example.spec/routes]
|
example.spec/routes]
|
||||||
{:data {:middleware [ring.middleware.params/wrap-params
|
{:data {:middleware [ring.middleware.params/wrap-params
|
||||||
muuntaja.middleware/wrap-format
|
muuntaja.middleware/wrap-format
|
||||||
coercion-middleware/coerce-exceptions-middleware
|
rrc/coerce-exceptions-middleware
|
||||||
coercion-middleware/coerce-request-middleware
|
rrc/coerce-request-middleware
|
||||||
coercion-middleware/coerce-response-middleware]}})))
|
rrc/coerce-response-middleware]}})))
|
||||||
|
|
||||||
(defn restart []
|
(defn restart []
|
||||||
(swap! server (fn [x]
|
(swap! server (fn [x]
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
(ns reitit.spec
|
(ns reitit.spec
|
||||||
(:require [clojure.spec.alpha :as s]
|
(:require [clojure.spec.alpha :as s]
|
||||||
[clojure.spec.gen.alpha :as gen]
|
[clojure.spec.gen.alpha :as gen]
|
||||||
[clojure.string :as str]
|
|
||||||
[reitit.core :as reitit]))
|
[reitit.core :as reitit]))
|
||||||
|
|
||||||
;;
|
;;
|
||||||
|
|
@ -71,9 +70,42 @@
|
||||||
:ret ::router)
|
:ret ::router)
|
||||||
|
|
||||||
;;
|
;;
|
||||||
;; Route data validator
|
;; coercion
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
(s/def :reitit.core.coercion/kw-map (s/map-of keyword? any?))
|
||||||
|
|
||||||
|
(s/def :reitit.core.coercion/query :reitit.core.coercion/kw-map)
|
||||||
|
(s/def :reitit.core.coercion/body :reitit.core.coercion/kw-map)
|
||||||
|
(s/def :reitit.core.coercion/form :reitit.core.coercion/kw-map)
|
||||||
|
(s/def :reitit.core.coercion/header :reitit.core.coercion/kw-map)
|
||||||
|
(s/def :reitit.core.coercion/path :reitit.core.coercion/kw-map)
|
||||||
|
(s/def :reitit.core.coercion/parameters
|
||||||
|
(s/keys :opt-un [:reitit.core.coercion/query
|
||||||
|
:reitit.core.coercion/body
|
||||||
|
:reitit.core.coercion/form
|
||||||
|
:reitit.core.coercion/header
|
||||||
|
:reitit.core.coercion/path]))
|
||||||
|
|
||||||
|
(s/def ::parameters
|
||||||
|
(s/keys :opt-un [:reitit.core.coercion/parameters]))
|
||||||
|
|
||||||
|
(s/def :reitit.core.coercion/status
|
||||||
|
(s/or :number number? :default #{:default}))
|
||||||
|
(s/def :reitit.core.coercion/schema any?)
|
||||||
|
(s/def :reitit.core.coercion/description string?)
|
||||||
|
(s/def :reitit.core.coercion/response
|
||||||
|
(s/keys :opt-un [:reitit.core.coercion/schema
|
||||||
|
:reitit.core.coercion/description]))
|
||||||
|
(s/def :reitit.core.coercion/responses
|
||||||
|
(s/map-of :reitit.core.coercion/status :reitit.core.coercion/response))
|
||||||
|
|
||||||
|
(s/def ::responses
|
||||||
|
(s/keys :opt-un [:reitit.core.coercion/responses]))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Route data validator
|
||||||
|
;;
|
||||||
|
|
||||||
(defrecord Problem [path scope data spec problems])
|
(defrecord Problem [path scope data spec problems])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
(ns reitit.ring.coercion-middleware
|
(ns reitit.ring.coercion
|
||||||
(:require [reitit.coercion :as coercion]
|
(:require [reitit.coercion :as coercion]
|
||||||
|
[reitit.spec :as rs]
|
||||||
[reitit.impl :as impl]))
|
[reitit.impl :as impl]))
|
||||||
|
|
||||||
(defn handle-coercion-exception [e respond raise]
|
(defn handle-coercion-exception [e respond raise]
|
||||||
|
|
@ -22,6 +23,7 @@
|
||||||
Expects a :coercion of type `reitit.coercion/Coercion`
|
Expects a :coercion of type `reitit.coercion/Coercion`
|
||||||
and :parameters from route data, otherwise does not mount."
|
and :parameters from route data, otherwise does not mount."
|
||||||
{:name ::coerce-request
|
{:name ::coerce-request
|
||||||
|
:spec ::rs/parameters
|
||||||
:compile (fn [{:keys [coercion parameters]} opts]
|
:compile (fn [{:keys [coercion parameters]} opts]
|
||||||
(if (and coercion parameters)
|
(if (and coercion parameters)
|
||||||
(let [coercers (coercion/request-coercers coercion parameters opts)]
|
(let [coercers (coercion/request-coercers coercion parameters opts)]
|
||||||
|
|
@ -39,6 +41,7 @@
|
||||||
Expects a :coercion of type `reitit.coercion/Coercion`
|
Expects a :coercion of type `reitit.coercion/Coercion`
|
||||||
and :responses from route data, otherwise does not mount."
|
and :responses from route data, otherwise does not mount."
|
||||||
{:name ::coerce-response
|
{:name ::coerce-response
|
||||||
|
:spec ::rs/responses
|
||||||
:compile (fn [{:keys [coercion responses]} opts]
|
:compile (fn [{:keys [coercion responses]} opts]
|
||||||
(if (and coercion responses)
|
(if (and coercion responses)
|
||||||
(let [coercers (coercion/response-coercers coercion responses opts)]
|
(let [coercers (coercion/response-coercers coercion responses opts)]
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
[muuntaja.core :as m]
|
[muuntaja.core :as m]
|
||||||
[muuntaja.format.jsonista :as jsonista-format]
|
[muuntaja.format.jsonista :as jsonista-format]
|
||||||
[jsonista.core :as j]
|
[jsonista.core :as j]
|
||||||
[reitit.ring.coercion-middleware :as coercion-middleware]
|
[reitit.ring.coercion :as rrc]
|
||||||
[reitit.coercion.spec :as spec]
|
[reitit.coercion.spec :as spec]
|
||||||
[reitit.coercion.schema :as schema]
|
[reitit.coercion.schema :as schema]
|
||||||
[reitit.coercion :as coercion]
|
[reitit.coercion :as coercion]
|
||||||
|
|
@ -36,14 +36,14 @@
|
||||||
(s/def ::k (s/keys :req-un [::x ::y]))
|
(s/def ::k (s/keys :req-un [::x ::y]))
|
||||||
|
|
||||||
(let [spec (spec/into-spec {:x int?, :y int?} ::jeah)
|
(let [spec (spec/into-spec {:x int?, :y int?} ::jeah)
|
||||||
coercers (#'coercion-middleware/request-coercers spec/coercion {:body spec})
|
coercers (#'rrc/request-coercers spec/coercion {:body spec})
|
||||||
params {:x "1", :y "2"}
|
params {:x "1", :y "2"}
|
||||||
request {:body-params {:x "1", :y "2"}}]
|
request {:body-params {:x "1", :y "2"}}]
|
||||||
|
|
||||||
;; 4600ns
|
;; 4600ns
|
||||||
(bench!
|
(bench!
|
||||||
"coerce-parameters"
|
"coerce-parameters"
|
||||||
(#'coercion-middleware/coerce-request-middleware coercers request))
|
(#'rrc/coerce-request-middleware coercers request))
|
||||||
|
|
||||||
;; 2700ns
|
;; 2700ns
|
||||||
(bench!
|
(bench!
|
||||||
|
|
@ -102,24 +102,24 @@
|
||||||
app (ring/ring-handler
|
app (ring/ring-handler
|
||||||
(ring/router
|
(ring/router
|
||||||
routes
|
routes
|
||||||
{:data {:middleware [coercion-middleware/coerce-request-middleware]
|
{:data {:middleware [rrc/coerce-request-middleware]
|
||||||
:coercion coercion}}))
|
:coercion coercion}}))
|
||||||
app2 (ring/ring-handler
|
app2 (ring/ring-handler
|
||||||
(ring/router
|
(ring/router
|
||||||
routes
|
routes
|
||||||
{:data {:middleware [coercion-middleware/coerce-request-middleware]
|
{:data {:middleware [rrc/coerce-request-middleware]
|
||||||
:coercion coercion}}))
|
:coercion coercion}}))
|
||||||
app3 (ring/ring-handler
|
app3 (ring/ring-handler
|
||||||
(ring/router
|
(ring/router
|
||||||
routes
|
routes
|
||||||
{:data {:middleware [coercion-middleware/coerce-request-middleware
|
{:data {:middleware [rrc/coerce-request-middleware
|
||||||
coercion-middleware/wrap-coerce-response]
|
rrc/wrap-coerce-response]
|
||||||
:coercion coercion}}))
|
:coercion coercion}}))
|
||||||
app4 (ring/ring-handler
|
app4 (ring/ring-handler
|
||||||
(ring/router
|
(ring/router
|
||||||
routes
|
routes
|
||||||
{:data {:middleware [coercion-middleware/coerce-request-middleware
|
{:data {:middleware [rrc/coerce-request-middleware
|
||||||
coercion-middleware/coerce-response-middleware]
|
rrc/coerce-response-middleware]
|
||||||
:coercion coercion}}))
|
:coercion coercion}}))
|
||||||
req {:request-method :get
|
req {:request-method :get
|
||||||
:uri "/api/ping"
|
:uri "/api/ping"
|
||||||
|
|
@ -156,8 +156,8 @@
|
||||||
:get {:handler (fn [{{{:keys [x y]} :body} :parameters}]
|
:get {:handler (fn [{{{:keys [x y]} :body} :parameters}]
|
||||||
{:status 200
|
{:status 200
|
||||||
:body {:total (+ x y)}})}}]]
|
:body {:total (+ x y)}})}}]]
|
||||||
{:data {:middleware [coercion-middleware/coerce-request-middleware
|
{:data {:middleware [rrc/coerce-request-middleware
|
||||||
coercion-middleware/coerce-response-middleware]
|
rrc/coerce-response-middleware]
|
||||||
:coercion spec/coercion}})))
|
:coercion spec/coercion}})))
|
||||||
|
|
||||||
(app
|
(app
|
||||||
|
|
@ -205,8 +205,8 @@
|
||||||
(let [body (-> request :parameters :body)]
|
(let [body (-> request :parameters :body)]
|
||||||
{:status 200, :body {:result (+ (:x body) (:y body))}}))}}]
|
{:status 200, :body {:result (+ (:x body) (:y body))}}))}}]
|
||||||
{:data {:middleware [[mm/wrap-format m]
|
{:data {:middleware [[mm/wrap-format m]
|
||||||
coercion-middleware/coerce-request-middleware
|
rrc/coerce-request-middleware
|
||||||
coercion-middleware/coerce-response-middleware]
|
rrc/coerce-response-middleware]
|
||||||
:coercion schema/coercion}}))
|
:coercion schema/coercion}}))
|
||||||
request {:request-method :post
|
request {:request-method :post
|
||||||
:uri "/plus"
|
:uri "/plus"
|
||||||
|
|
@ -229,8 +229,8 @@
|
||||||
:handler (fn [request]
|
:handler (fn [request]
|
||||||
(let [body (-> request :parameters :body)]
|
(let [body (-> request :parameters :body)]
|
||||||
{:status 200, :body {:result (+ (:x body) (:y body))}}))}}]
|
{:status 200, :body {:result (+ (:x body) (:y body))}}))}}]
|
||||||
{:data {:middleware [coercion-middleware/coerce-request-middleware
|
{:data {:middleware [rrc/coerce-request-middleware
|
||||||
coercion-middleware/coerce-response-middleware]
|
rrc/coerce-response-middleware]
|
||||||
:coercion schema/coercion}}))
|
:coercion schema/coercion}}))
|
||||||
request {:request-method :post
|
request {:request-method :post
|
||||||
:uri "/plus"
|
:uri "/plus"
|
||||||
|
|
@ -253,8 +253,8 @@
|
||||||
:handler (fn [request]
|
:handler (fn [request]
|
||||||
(let [body (-> request :parameters :body)]
|
(let [body (-> request :parameters :body)]
|
||||||
{:status 200, :body {:result (+ (:x body) (:y body))}}))}}]
|
{:status 200, :body {:result (+ (:x body) (:y body))}}))}}]
|
||||||
{:data {:middleware [coercion-middleware/coerce-request-middleware
|
{:data {:middleware [rrc/coerce-request-middleware
|
||||||
coercion-middleware/coerce-response-middleware]
|
rrc/coerce-response-middleware]
|
||||||
:coercion spec/coercion}}))
|
:coercion spec/coercion}}))
|
||||||
request {:request-method :post
|
request {:request-method :post
|
||||||
:uri "/plus"
|
:uri "/plus"
|
||||||
|
|
@ -282,8 +282,8 @@
|
||||||
:handler (fn [request]
|
:handler (fn [request]
|
||||||
(let [body (-> request :parameters :body)]
|
(let [body (-> request :parameters :body)]
|
||||||
{:status 200, :body {:result (+ (:x body) (:y body))}}))}}]
|
{:status 200, :body {:result (+ (:x body) (:y body))}}))}}]
|
||||||
{:data {:middleware [coercion-middleware/coerce-request-middleware
|
{:data {:middleware [rrc/coerce-request-middleware
|
||||||
coercion-middleware/coerce-response-middleware]
|
rrc/coerce-response-middleware]
|
||||||
:coercion spec/coercion}}))
|
:coercion spec/coercion}}))
|
||||||
request {:request-method :post
|
request {:request-method :post
|
||||||
:uri "/plus"
|
:uri "/plus"
|
||||||
|
|
|
||||||
|
|
@ -48,4 +48,3 @@
|
||||||
(is (= nil (coercion/coerce! m))))
|
(is (= nil (coercion/coerce! m))))
|
||||||
(let [m (r/match-by-path r "/none/kikka/abba")]
|
(let [m (r/match-by-path r "/none/kikka/abba")]
|
||||||
(is (= nil (coercion/coerce! m))))))))
|
(is (= nil (coercion/coerce! m))))))))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
(is (= name (r/router-name router)))
|
(is (= name (r/router-name router)))
|
||||||
(is (= [["/api/ipa/:size" {:name ::beer} nil]]
|
(is (= [["/api/ipa/:size" {:name ::beer} nil]]
|
||||||
(r/routes router)))
|
(r/routes router)))
|
||||||
(is (= true (map? (r/options router))))
|
(is (map? (r/options router)))
|
||||||
(is (= (r/map->Match
|
(is (= (r/map->Match
|
||||||
{:template "/api/ipa/:size"
|
{:template "/api/ipa/:size"
|
||||||
:data {:name ::beer}
|
:data {:name ::beer}
|
||||||
|
|
@ -39,7 +39,7 @@
|
||||||
:required #{:size}
|
:required #{:size}
|
||||||
:params nil})
|
:params nil})
|
||||||
(r/match-by-name router ::beer)))
|
(r/match-by-name router ::beer)))
|
||||||
(is (= true (r/partial-match? (r/match-by-name router ::beer))))
|
(is (r/partial-match? (r/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\}$"
|
||||||
|
|
@ -72,7 +72,7 @@
|
||||||
(is (= name (r/router-name router)))
|
(is (= name (r/router-name router)))
|
||||||
(is (= [["/api/ipa/large" {:name ::beer} nil]]
|
(is (= [["/api/ipa/large" {:name ::beer} nil]]
|
||||||
(r/routes router)))
|
(r/routes router)))
|
||||||
(is (= true (map? (r/options router))))
|
(is (map? (r/options router)))
|
||||||
(is (= (r/map->Match
|
(is (= (r/map->Match
|
||||||
{:template "/api/ipa/large"
|
{:template "/api/ipa/large"
|
||||||
:data {:name ::beer}
|
:data {:name ::beer}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
(:require [clojure.test :refer [deftest testing is]]
|
(:require [clojure.test :refer [deftest testing is]]
|
||||||
[schema.core :as s]
|
[schema.core :as s]
|
||||||
[reitit.ring :as ring]
|
[reitit.ring :as ring]
|
||||||
[reitit.ring.coercion-middleware :as coercion-middleware]
|
[reitit.ring.coercion :as rrc]
|
||||||
[reitit.coercion.spec :as spec]
|
[reitit.coercion.spec :as spec]
|
||||||
[reitit.coercion.schema :as schema])
|
[reitit.coercion.schema :as schema])
|
||||||
#?(:clj
|
#?(:clj
|
||||||
|
|
@ -53,8 +53,8 @@
|
||||||
:coercion spec/coercion}})))]
|
:coercion spec/coercion}})))]
|
||||||
|
|
||||||
(testing "withut exception handling"
|
(testing "withut exception handling"
|
||||||
(let [app (create [coercion-middleware/coerce-request-middleware
|
(let [app (create [rrc/coerce-request-middleware
|
||||||
coercion-middleware/coerce-response-middleware])]
|
rrc/coerce-response-middleware])]
|
||||||
|
|
||||||
(testing "all good"
|
(testing "all good"
|
||||||
(is (= {:status 200
|
(is (= {:status 200
|
||||||
|
|
@ -74,9 +74,9 @@
|
||||||
(app invalid-request2))))))
|
(app invalid-request2))))))
|
||||||
|
|
||||||
(testing "with exception handling"
|
(testing "with exception handling"
|
||||||
(let [app (create [coercion-middleware/coerce-exceptions-middleware
|
(let [app (create [rrc/coerce-exceptions-middleware
|
||||||
coercion-middleware/coerce-request-middleware
|
rrc/coerce-request-middleware
|
||||||
coercion-middleware/coerce-response-middleware])]
|
rrc/coerce-response-middleware])]
|
||||||
|
|
||||||
(testing "all good"
|
(testing "all good"
|
||||||
(is (= {:status 200
|
(is (= {:status 200
|
||||||
|
|
@ -108,8 +108,8 @@
|
||||||
:coercion schema/coercion}})))]
|
:coercion schema/coercion}})))]
|
||||||
|
|
||||||
(testing "withut exception handling"
|
(testing "withut exception handling"
|
||||||
(let [app (create [coercion-middleware/coerce-request-middleware
|
(let [app (create [rrc/coerce-request-middleware
|
||||||
coercion-middleware/coerce-response-middleware])]
|
rrc/coerce-response-middleware])]
|
||||||
|
|
||||||
(testing "all good"
|
(testing "all good"
|
||||||
(is (= {:status 200
|
(is (= {:status 200
|
||||||
|
|
@ -129,9 +129,9 @@
|
||||||
(app invalid-request2))))
|
(app invalid-request2))))
|
||||||
|
|
||||||
(testing "with exception handling"
|
(testing "with exception handling"
|
||||||
(let [app (create [coercion-middleware/coerce-exceptions-middleware
|
(let [app (create [rrc/coerce-exceptions-middleware
|
||||||
coercion-middleware/coerce-request-middleware
|
rrc/coerce-request-middleware
|
||||||
coercion-middleware/coerce-response-middleware])]
|
rrc/coerce-response-middleware])]
|
||||||
|
|
||||||
(testing "all good"
|
(testing "all good"
|
||||||
(is (= {:status 200
|
(is (= {:status 200
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
(: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]
|
||||||
|
[reitit.ring.coercion :as rrc]
|
||||||
|
[reitit.coercion.spec]
|
||||||
[clojure.spec.alpha :as s]
|
[clojure.spec.alpha :as s]
|
||||||
[reitit.core :as r])
|
[reitit.core :as r])
|
||||||
#?(:clj
|
#?(:clj
|
||||||
|
|
@ -12,9 +14,9 @@
|
||||||
|
|
||||||
(deftest route-data-validation-test
|
(deftest route-data-validation-test
|
||||||
(testing "validation is turned off by default"
|
(testing "validation is turned off by default"
|
||||||
(is (true? (r/router?
|
(is (r/router?
|
||||||
(r/router
|
(r/router
|
||||||
["/api" {:handler "identity"}])))))
|
["/api" {:handler "identity"}]))))
|
||||||
|
|
||||||
(testing "with default spec validates :name, :handler and :middleware"
|
(testing "with default spec validates :name, :handler and :middleware"
|
||||||
(is (thrown-with-msg?
|
(is (thrown-with-msg?
|
||||||
|
|
@ -40,11 +42,11 @@
|
||||||
{:validate rrs/validate-spec!}))))
|
{:validate rrs/validate-spec!}))))
|
||||||
|
|
||||||
(testing "spec can be overridden"
|
(testing "spec can be overridden"
|
||||||
(is (true? (r/router?
|
(is (r/router?
|
||||||
(ring/router
|
(ring/router
|
||||||
["/api" {:handler "identity"}]
|
["/api" {:handler "identity"}]
|
||||||
{:spec (s/spec any?)
|
{:spec (s/spec any?)
|
||||||
:validate rrs/validate-spec!}))))
|
:validate rrs/validate-spec!})))
|
||||||
|
|
||||||
(testing "predicates are not allowed"
|
(testing "predicates are not allowed"
|
||||||
(is (thrown-with-msg?
|
(is (thrown-with-msg?
|
||||||
|
|
@ -56,15 +58,15 @@
|
||||||
:validate rrs/validate-spec!})))))
|
:validate rrs/validate-spec!})))))
|
||||||
|
|
||||||
(testing "middleware can contribute to specs"
|
(testing "middleware can contribute to specs"
|
||||||
(is (true? (r/router?
|
(is (r/router?
|
||||||
(ring/router
|
(ring/router
|
||||||
["/api" {:get {:handler identity
|
["/api" {:get {:handler identity
|
||||||
:roles #{:admin}}}]
|
:roles #{:admin}}}]
|
||||||
{:validate rrs/validate-spec!
|
{:validate rrs/validate-spec!
|
||||||
:data {:middleware [{:spec (s/keys :opt-un [::roles])
|
:data {:middleware [{:spec (s/keys :opt-un [::roles])
|
||||||
:wrap (fn [handler]
|
:wrap (fn [handler]
|
||||||
(fn [request]
|
(fn [request]
|
||||||
(handler request)))}]}}))))
|
(handler request)))}]}})))
|
||||||
(is (thrown-with-msg?
|
(is (thrown-with-msg?
|
||||||
ExceptionInfo
|
ExceptionInfo
|
||||||
#"Invalid route data"
|
#"Invalid route data"
|
||||||
|
|
@ -76,3 +78,49 @@
|
||||||
:wrap (fn [handler]
|
:wrap (fn [handler]
|
||||||
(fn [request]
|
(fn [request]
|
||||||
(handler request)))}]}})))))
|
(handler request)))}]}})))))
|
||||||
|
|
||||||
|
(deftest coercion-spec-test
|
||||||
|
(is (r/router?
|
||||||
|
(ring/router
|
||||||
|
["/api"
|
||||||
|
["/plus/:e"
|
||||||
|
{:get {:parameters {:query {:a string?}
|
||||||
|
:body {:b string?}
|
||||||
|
:form {:c string?}
|
||||||
|
:header {:d string?}
|
||||||
|
:path {:e string?}}
|
||||||
|
:responses {200 {:schema {:total pos-int?}}}
|
||||||
|
:handler identity}}]]
|
||||||
|
{:data {:middleware [rrc/coerce-exceptions-middleware
|
||||||
|
rrc/coerce-request-middleware
|
||||||
|
rrc/coerce-response-middleware]
|
||||||
|
:coercion reitit.coercion.spec/coercion}
|
||||||
|
:validate rrs/validate-spec!})))
|
||||||
|
|
||||||
|
(is (thrown-with-msg?
|
||||||
|
ExceptionInfo
|
||||||
|
#"Invalid route data"
|
||||||
|
(ring/router
|
||||||
|
["/api"
|
||||||
|
["/plus/:e"
|
||||||
|
{:get {:parameters {:query {"a" string?}}
|
||||||
|
:handler identity}}]]
|
||||||
|
{:data {:middleware [rrc/coerce-exceptions-middleware
|
||||||
|
rrc/coerce-request-middleware
|
||||||
|
rrc/coerce-response-middleware]
|
||||||
|
:coercion reitit.coercion.spec/coercion}
|
||||||
|
:validate rrs/validate-spec!})))
|
||||||
|
|
||||||
|
(is (thrown-with-msg?
|
||||||
|
ExceptionInfo
|
||||||
|
#"Invalid route data"
|
||||||
|
(ring/router
|
||||||
|
["/api"
|
||||||
|
["/plus/:e"
|
||||||
|
{:get {:responses {"200" {}}
|
||||||
|
:handler identity}}]]
|
||||||
|
{:data {:middleware [rrc/coerce-exceptions-middleware
|
||||||
|
rrc/coerce-request-middleware
|
||||||
|
rrc/coerce-response-middleware]
|
||||||
|
:coercion reitit.coercion.spec/coercion}
|
||||||
|
:validate rrs/validate-spec!}))))
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
(testing "route-data"
|
(testing "route-data"
|
||||||
(are [data]
|
(are [data]
|
||||||
(is (= true (r/router? (r/router data))))
|
(is (r/router? (r/router data)))
|
||||||
|
|
||||||
["/api" {}]
|
["/api" {}]
|
||||||
|
|
||||||
|
|
@ -45,12 +45,12 @@
|
||||||
["/ipa"]])))
|
["/ipa"]])))
|
||||||
|
|
||||||
(testing "routes conform to spec (can't spec protocol functions)"
|
(testing "routes conform to spec (can't spec protocol functions)"
|
||||||
(is (= true (s/valid? ::rs/routes (r/routes (r/router ["/ping"]))))))
|
(is (s/valid? ::rs/routes (r/routes (r/router ["/ping"])))))
|
||||||
|
|
||||||
(testing "options"
|
(testing "options"
|
||||||
|
|
||||||
(are [opts]
|
(are [opts]
|
||||||
(is (= true (r/router? (r/router ["/api"] opts))))
|
(is (r/router? (r/router ["/api"] opts)))
|
||||||
|
|
||||||
{:path "/"}
|
{:path "/"}
|
||||||
{:data {}}
|
{:data {}}
|
||||||
|
|
@ -78,8 +78,8 @@
|
||||||
|
|
||||||
(deftest route-data-validation-test
|
(deftest route-data-validation-test
|
||||||
(testing "validation is turned off by default"
|
(testing "validation is turned off by default"
|
||||||
(is (true? (r/router? (r/router
|
(is (r/router? (r/router
|
||||||
["/api" {:handler "identity"}])))))
|
["/api" {:handler "identity"}]))))
|
||||||
|
|
||||||
(testing "with default spec validates :name and :handler"
|
(testing "with default spec validates :name and :handler"
|
||||||
(is (thrown-with-msg?
|
(is (thrown-with-msg?
|
||||||
|
|
@ -96,7 +96,35 @@
|
||||||
{:validate rs/validate-spec!}))))
|
{:validate rs/validate-spec!}))))
|
||||||
|
|
||||||
(testing "spec can be overridden"
|
(testing "spec can be overridden"
|
||||||
(is (true? (r/router? (r/router
|
(is (r/router? (r/router
|
||||||
["/api" {:handler "identity"}]
|
["/api" {:handler "identity"}]
|
||||||
{:spec any?
|
{:spec any?
|
||||||
:validate rs/validate-spec!}))))))
|
:validate rs/validate-spec!})))))
|
||||||
|
|
||||||
|
(deftest parameters-test
|
||||||
|
(is (s/valid?
|
||||||
|
::rs/parameters
|
||||||
|
{:parameters {:query {:a string?}
|
||||||
|
:body {:b string?}
|
||||||
|
:form {:c string?}
|
||||||
|
:header {:d string?}
|
||||||
|
:path {:e string?}}}))
|
||||||
|
|
||||||
|
(is (not (s/valid?
|
||||||
|
::rs/parameters
|
||||||
|
{:parameters {:header {"d" string?}}})))
|
||||||
|
|
||||||
|
(is (s/valid?
|
||||||
|
::rs/responses
|
||||||
|
{:responses {200 {:description "ok", :schema string?}
|
||||||
|
400 {:description "fail"}
|
||||||
|
500 {:schema string?}
|
||||||
|
:default {}}}))
|
||||||
|
|
||||||
|
(is (not (s/valid?
|
||||||
|
::rs/responses
|
||||||
|
{:responses {"200" {:description "ok", :schema string?}}})))
|
||||||
|
|
||||||
|
(is (not (s/valid?
|
||||||
|
::rs/responses
|
||||||
|
{:responses {200 {:description :ok, :schema string?}}}))))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue