mirror of
https://github.com/metosin/reitit.git
synced 2025-12-18 00:41:12 +00:00
Parameter & Response Coercion with specs
This commit is contained in:
parent
07bcd2ea59
commit
fb99b4f9fd
4 changed files with 113 additions and 1 deletions
|
|
@ -70,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
|
(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)]
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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!}))))
|
||||||
|
|
|
||||||
|
|
@ -100,3 +100,31 @@
|
||||||
["/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