mirror of
https://github.com/metosin/reitit.git
synced 2025-12-17 08:21:11 +00:00
spell-spec
This commit is contained in:
parent
dc92f6f48e
commit
674b60a124
13 changed files with 100 additions and 47 deletions
|
|
@ -112,6 +112,7 @@
|
|||
:body {:total (- x y)}})}}]]]
|
||||
|
||||
{;;:reitit.interceptor/transform dev/print-context-diffs
|
||||
;;:wrap-spec reitit.dev.pretty/closed-keys
|
||||
:validate spec/validate
|
||||
:exception pretty/exception
|
||||
:data {:coercion spec-coercion/coercion
|
||||
|
|
|
|||
|
|
@ -39,7 +39,8 @@
|
|||
|
||||
(s/def ::name keyword?)
|
||||
(s/def ::handler fn?)
|
||||
(s/def ::default-data (s/keys :opt-un [::name ::handler]))
|
||||
(s/def ::no-doc boolean?)
|
||||
(s/def ::default-data (s/keys :opt-un [::name ::handler ::no-doc]))
|
||||
|
||||
;;
|
||||
;; router
|
||||
|
|
@ -75,6 +76,8 @@
|
|||
;; coercion
|
||||
;;
|
||||
|
||||
(s/def :reitit.core.coercion/coercion any?)
|
||||
|
||||
(s/def :reitit.core.coercion/model any?)
|
||||
|
||||
(s/def :reitit.core.coercion/query :reitit.core.coercion/model)
|
||||
|
|
@ -90,7 +93,8 @@
|
|||
:reitit.core.coercion/path]))
|
||||
|
||||
(s/def ::parameters
|
||||
(s/keys :opt-un [:reitit.core.coercion/parameters]))
|
||||
(s/keys :opt-un [:reitit.core.coercion/coercion
|
||||
:reitit.core.coercion/parameters]))
|
||||
|
||||
(s/def :reitit.core.coercion/status
|
||||
(s/or :number number? :default #{:default}))
|
||||
|
|
@ -103,7 +107,8 @@
|
|||
(s/map-of :reitit.core.coercion/status :reitit.core.coercion/response))
|
||||
|
||||
(s/def ::responses
|
||||
(s/keys :opt-un [:reitit.core.coercion/responses]))
|
||||
(s/keys :opt-un [:reitit.core.coercion/coercion
|
||||
:reitit.core.coercion/responses]))
|
||||
|
||||
;;
|
||||
;; Route data validator
|
||||
|
|
@ -111,14 +116,14 @@
|
|||
|
||||
(defrecord Problem [path scope data spec problems])
|
||||
|
||||
(defn validate-route-data [routes spec]
|
||||
(defn validate-route-data [routes wrap-spec spec]
|
||||
(some->> (for [[p d _] routes]
|
||||
(when-let [problems (and spec (s/explain-data spec d))]
|
||||
(when-let [problems (and spec (s/explain-data (wrap-spec spec) d))]
|
||||
(->Problem p nil d spec problems)))
|
||||
(keep identity) (seq) (vec)))
|
||||
|
||||
(defn validate [routes {:keys [spec] :or {spec ::default-data}}]
|
||||
(when-let [problems (validate-route-data routes spec)]
|
||||
(defn validate [routes {:keys [spec wrap-spec] :or {spec ::default-data, wrap-spec identity}}]
|
||||
(when-let [problems (validate-route-data routes wrap-spec spec)]
|
||||
(exception/fail!
|
||||
::invalid-route-data
|
||||
{:problems problems})))
|
||||
|
|
|
|||
|
|
@ -9,5 +9,6 @@
|
|||
:parent-project {:path "../../project.clj"
|
||||
:inherit [:deploy-repositories :managed-dependencies]}
|
||||
:dependencies [[metosin/reitit-core]
|
||||
[com.bhauman/spell-spec]
|
||||
[expound]
|
||||
[fipp]])
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@
|
|||
[clojure.spec.alpha :as s]
|
||||
[reitit.exception :as exception]
|
||||
[arrangement.core]
|
||||
;; spell-spec
|
||||
[spec-tools.spell :as spell]
|
||||
[spell-spec.expound]
|
||||
;; expound
|
||||
[expound.ansi]
|
||||
[expound.alpha]
|
||||
|
|
@ -178,7 +181,7 @@
|
|||
(if (and (not= 1 line))
|
||||
(let [file-name (str/replace file #"(.*?)\.\S[^\.]+" "$1")
|
||||
target-name (name target)
|
||||
ns (str (subs target-name 0 (str/index-of target-name (str "user" "$"))) file-name)]
|
||||
ns (str (subs target-name 0 (or (str/index-of target-name (str file-name "$")) 0)) file-name)]
|
||||
(str ns ":" line))
|
||||
"repl")
|
||||
(catch #?(:clj Exception, :cljs js/Error) _
|
||||
|
|
@ -227,6 +230,9 @@
|
|||
:cljs "unknown")]
|
||||
(ex-info (exception-str message source (printer)) (assoc (or data {}) ::exception/cause e))))
|
||||
|
||||
;; FIXME
|
||||
(def closed-keys spec-tools.spell/closed-keys)
|
||||
|
||||
(defn de-expound-colors [^String s mappings]
|
||||
(let [s' (reduce
|
||||
(fn [s [from to]]
|
||||
|
|
@ -316,12 +322,12 @@
|
|||
(into
|
||||
[:group]
|
||||
(map
|
||||
(fn [{:keys [data path spec]}]
|
||||
(fn [{:keys [data path spec scope]}]
|
||||
[:group
|
||||
[:span (color :grey "-- On route -----------------------")]
|
||||
[:break]
|
||||
[:break]
|
||||
(text path)
|
||||
(text path) (if scope [:span " " (text scope)])
|
||||
[:break]
|
||||
[:break]
|
||||
(-> (s/explain-data spec data)
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@
|
|||
(:require [meta-merge.core :refer [meta-merge]]
|
||||
[reitit.interceptor :as interceptor]
|
||||
[reitit.ring :as ring]
|
||||
[reitit.core :as r]
|
||||
[reitit.impl :as impl]))
|
||||
[reitit.core :as r]))
|
||||
|
||||
(defrecord Endpoint [data interceptors queue handler path method])
|
||||
|
||||
|
|
@ -16,6 +15,9 @@
|
|||
|
||||
(defn compile-result [[path data] {:keys [::default-options-handler] :as opts}]
|
||||
(let [[top childs] (ring/group-keys data)
|
||||
childs (cond-> childs
|
||||
(and (not (:options childs)) default-options-handler)
|
||||
(assoc :options {:no-doc true, :handler default-options-handler}))
|
||||
compile (fn [[path data] opts scope]
|
||||
(interceptor/compile-result [path data] opts scope))
|
||||
->endpoint (fn [p d m s]
|
||||
|
|
@ -29,12 +31,7 @@
|
|||
(fn [acc method]
|
||||
(cond-> acc
|
||||
any? (assoc method (->endpoint path data method nil))))
|
||||
(ring/map->Methods
|
||||
{:options
|
||||
(if default-options-handler
|
||||
(->endpoint path (assoc data
|
||||
:handler default-options-handler
|
||||
:no-doc true) :options nil))})
|
||||
(ring/map->Methods {})
|
||||
ring/http-methods))]
|
||||
(if-not (seq childs)
|
||||
(->methods true top)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,13 @@
|
|||
{:name ::coerce-request
|
||||
:spec ::rs/parameters
|
||||
:compile (fn [{:keys [coercion parameters]} opts]
|
||||
(if (and coercion parameters)
|
||||
(cond
|
||||
;; no coercion, skip
|
||||
(not coercion) nil
|
||||
;; just coercion, don't mount
|
||||
(not parameters) {}
|
||||
;; mount
|
||||
:else
|
||||
(let [coercers (coercion/request-coercers coercion parameters opts)]
|
||||
{:enter (fn [ctx]
|
||||
(let [request (:request ctx)
|
||||
|
|
@ -27,7 +33,13 @@
|
|||
{:name ::coerce-response
|
||||
:spec ::rs/responses
|
||||
:compile (fn [{:keys [coercion responses]} opts]
|
||||
(if (and coercion responses)
|
||||
(cond
|
||||
;; no coercion, skip
|
||||
(not coercion) nil
|
||||
;; just coercion, don't mount
|
||||
(not responses) {}
|
||||
;; mount
|
||||
:else
|
||||
(let [coercers (coercion/response-coercers coercion responses opts)]
|
||||
{:leave (fn [ctx]
|
||||
(let [request (:request ctx)
|
||||
|
|
|
|||
|
|
@ -12,15 +12,15 @@
|
|||
(s/def ::interceptors (s/coll-of (partial satisfies? interceptor/IntoInterceptor)))
|
||||
|
||||
(s/def ::data
|
||||
(s/keys :opt-un [::rs/handler ::rs/name ::interceptors]))
|
||||
(s/keys :opt-un [::rs/handler ::rs/name ::rs/no-doc ::interceptors]))
|
||||
|
||||
;;
|
||||
;; Validator
|
||||
;;
|
||||
|
||||
(defn validate
|
||||
[routes {:keys [spec] :or {spec ::data}}]
|
||||
(when-let [problems (rrs/validate-route-data routes :interceptors spec)]
|
||||
[routes {:keys [spec wrap-spec] :or {spec ::data, wrap-spec identity}}]
|
||||
(when-let [problems (rrs/validate-route-data routes :interceptors wrap-spec spec)]
|
||||
(exception/fail!
|
||||
::rs/invalid-route-data
|
||||
{:problems problems})))
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@
|
|||
(s/def ::bytes bytes?)
|
||||
(s/def ::size int?)
|
||||
|
||||
(s/def ::multipart :reitit.core.coercion/model)
|
||||
(s/def ::parameters (s/keys :opt-un [::multipart]))
|
||||
|
||||
(def temp-file-part
|
||||
"Spec for file param created by ring.middleware.multipart-params.temp-file store."
|
||||
(st/spec
|
||||
|
|
@ -41,6 +44,7 @@
|
|||
(multipart-interceptor nil))
|
||||
([options]
|
||||
{:name ::multipart
|
||||
:spec ::parameters
|
||||
:compile (fn [{:keys [parameters coercion]} opts]
|
||||
(if-let [multipart (:multipart parameters)]
|
||||
(let [parameter-coercion {:multipart (coercion/->ParameterCoercion
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@
|
|||
|
||||
(defn compile-result [[path data] {:keys [::default-options-handler] :as opts}]
|
||||
(let [[top childs] (group-keys data)
|
||||
childs (cond-> childs
|
||||
(and (not (:options childs)) default-options-handler)
|
||||
(assoc :options {:no-doc true, :handler default-options-handler}))
|
||||
->endpoint (fn [p d m s]
|
||||
(-> (middleware/compile-result [p d] opts s)
|
||||
(map->Endpoint)
|
||||
|
|
@ -40,12 +43,7 @@
|
|||
(fn [acc method]
|
||||
(cond-> acc
|
||||
any? (assoc method (->endpoint path data method nil))))
|
||||
(map->Methods
|
||||
{:options
|
||||
(if default-options-handler
|
||||
(->endpoint path (assoc data
|
||||
:handler default-options-handler
|
||||
:no-doc true) :options nil))})
|
||||
(map->Methods {})
|
||||
http-methods))]
|
||||
(if-not (seq childs)
|
||||
(->methods true top)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,13 @@
|
|||
{:name ::coerce-request
|
||||
:spec ::rs/parameters
|
||||
:compile (fn [{:keys [coercion parameters]} opts]
|
||||
(if (and coercion parameters)
|
||||
(cond
|
||||
;; no coercion, skip
|
||||
(not coercion) nil
|
||||
;; just coercion, don't mount
|
||||
(not parameters) {}
|
||||
;; mount
|
||||
:else
|
||||
(let [coercers (coercion/request-coercers coercion parameters opts)]
|
||||
(fn [handler]
|
||||
(fn
|
||||
|
|
@ -43,7 +49,13 @@
|
|||
{:name ::coerce-response
|
||||
:spec ::rs/responses
|
||||
:compile (fn [{:keys [coercion responses]} opts]
|
||||
(if (and coercion responses)
|
||||
(cond
|
||||
;; no coercion, skip
|
||||
(not coercion) nil
|
||||
;; just coercion, don't mount
|
||||
(not responses) {}
|
||||
;; mount
|
||||
:else
|
||||
(let [coercers (coercion/response-coercers coercion responses opts)]
|
||||
(fn [handler]
|
||||
(fn
|
||||
|
|
|
|||
|
|
@ -9,10 +9,25 @@
|
|||
;;
|
||||
|
||||
(s/def ::middleware (s/coll-of #(satisfies? middleware/IntoMiddleware %)))
|
||||
(s/def ::get map?)
|
||||
(s/def ::head map?)
|
||||
(s/def ::post map?)
|
||||
(s/def ::put map?)
|
||||
(s/def ::delete map?)
|
||||
(s/def ::connect map?)
|
||||
(s/def ::options map?)
|
||||
(s/def ::trace map?)
|
||||
(s/def ::patch map?)
|
||||
|
||||
|
||||
(s/def ::endpoint
|
||||
(s/keys :req-un [::rs/handler]
|
||||
:opt-un [::rs/name ::rs/no-doc ::middleware]))
|
||||
|
||||
(s/def ::data
|
||||
(s/keys :req-un [::rs/handler]
|
||||
:opt-un [::rs/name ::middleware]))
|
||||
(s/merge
|
||||
::endpoint
|
||||
(s/map-of #{:get :head :post :put :delete :connect :options :trace :patch} map?)))
|
||||
|
||||
;;
|
||||
;; Validator
|
||||
|
|
@ -26,21 +41,21 @@
|
|||
:invalid non-specs}))
|
||||
(s/merge-spec-impl (vec specs) (vec specs) nil))
|
||||
|
||||
(defn validate-route-data [routes key spec]
|
||||
(defn validate-route-data [routes key wrap-spec spec]
|
||||
(->> (for [[p _ c] routes
|
||||
[method {:keys [data] :as endpoint}] c
|
||||
:when endpoint
|
||||
:let [target (key endpoint)
|
||||
component-specs (seq (keep :spec target))
|
||||
specs (keep identity (into [spec] component-specs))
|
||||
spec (merge-specs specs)]]
|
||||
spec (wrap-spec (merge-specs specs))]]
|
||||
(when-let [problems (and spec (s/explain-data spec data))]
|
||||
(rs/->Problem p method data spec problems)))
|
||||
(keep identity) (seq)))
|
||||
|
||||
(defn validate
|
||||
[routes {:keys [spec] :or {spec ::data}}]
|
||||
(when-let [problems (validate-route-data routes :middleware spec)]
|
||||
[routes {:keys [spec wrap-spec] :or {spec ::data, wrap-spec identity}}]
|
||||
(when-let [problems (validate-route-data routes :middleware wrap-spec spec)]
|
||||
(exception/fail!
|
||||
::rs/invalid-route-data
|
||||
{:problems problems})))
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
[fipp "0.6.17" :exclusions [org.clojure/core.rrb-vector]]
|
||||
[expound "0.7.2"]
|
||||
[lambdaisland/deep-diff "0.0-47"]
|
||||
[com.bhauman/spell-spec "0.1.1"]
|
||||
[ring/ring-core "1.7.1"]
|
||||
|
||||
[io.pedestal/pedestal.service "0.5.5"]]
|
||||
|
|
@ -80,6 +81,7 @@
|
|||
[metosin/jsonista]
|
||||
[lambdaisland/deep-diff]
|
||||
[meta-merge]
|
||||
[com.bhauman/spell-spec]
|
||||
[expound]
|
||||
[fipp]
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue