mirror of
https://github.com/metosin/reitit.git
synced 2025-12-17 00:11:11 +00:00
Add :on-invalid for custom handling of invalid values in coercer
This commit is contained in:
parent
7520d20f12
commit
3b5100d8a9
3 changed files with 75 additions and 11 deletions
|
|
@ -20,7 +20,8 @@
|
|||
(-decode [this value])
|
||||
(-encode [this value])
|
||||
(-validate [this value])
|
||||
(-explain [this value]))
|
||||
(-explain [this value])
|
||||
(-on-invalid [this value explained]))
|
||||
|
||||
(defprotocol TransformationProvider
|
||||
(-transformer [this options]))
|
||||
|
|
@ -37,18 +38,24 @@
|
|||
(def json-transformer-provider (-provider (mt/json-transformer)))
|
||||
(def default-transformer-provider (-provider nil))
|
||||
|
||||
(defn- -coercer [schema type transformers f {:keys [validate enabled options]}]
|
||||
(defn- -return-coercion-error
|
||||
[value error]
|
||||
(coercion/map->CoercionError (assoc error :transformed value)))
|
||||
|
||||
(defn- -coercer [schema type transformers f {:keys [validate enabled options on-invalid]}]
|
||||
(if schema
|
||||
(let [->coercer (fn [t]
|
||||
(let [decoder (if t (m/decoder schema options t) identity)
|
||||
encoder (if t (m/encoder schema options t) identity)
|
||||
validator (if validate (m/validator schema options) (constantly true))
|
||||
explainer (m/explainer schema options)]
|
||||
explainer (m/explainer schema options)
|
||||
report (or on-invalid -return-coercion-error)]
|
||||
(reify Coercer
|
||||
(-decode [_ value] (decoder value))
|
||||
(-encode [_ value] (encoder value))
|
||||
(-validate [_ value] (validator value))
|
||||
(-explain [_ value] (explainer value)))))
|
||||
(-explain [_ value] (explainer value))
|
||||
(-on-invalid [_ value explained] (report value explained)))))
|
||||
{:keys [formats default]} (transformers type)
|
||||
default-coercer (->coercer default)
|
||||
format-coercers (some->> (for [[f t] formats] [f (->coercer t)]) (filter second) (seq) (into {}))
|
||||
|
|
@ -63,8 +70,7 @@
|
|||
(if (-validate coercer transformed)
|
||||
transformed
|
||||
(let [error (-explain coercer transformed)]
|
||||
(coercion/map->CoercionError
|
||||
(assoc error :transformed transformed)))))
|
||||
(-on-invalid coercer transformed error))))
|
||||
value))
|
||||
;; encode: decode -> validate -> encode
|
||||
(fn [value format]
|
||||
|
|
@ -73,8 +79,7 @@
|
|||
(if (-validate coercer transformed)
|
||||
(-encode coercer transformed)
|
||||
(let [error (-explain coercer transformed)]
|
||||
(coercion/map->CoercionError
|
||||
(assoc error :transformed transformed))))
|
||||
(-on-invalid coercer transformed error)))
|
||||
value))))))))
|
||||
|
||||
(defn- -query-string-coercer
|
||||
|
|
@ -120,6 +125,8 @@
|
|||
:default-values true
|
||||
;; encode-error
|
||||
:encode-error nil
|
||||
;; custom handler for validation errors (vs returning them)
|
||||
:on-invalid nil
|
||||
;; malli options
|
||||
:options nil})
|
||||
|
||||
|
|
|
|||
|
|
@ -192,7 +192,8 @@
|
|||
(is (= 500 status))))))))))
|
||||
|
||||
(deftest malli-coercion-test
|
||||
(let [create (fn [interceptors]
|
||||
(let [most-recent (atom nil)
|
||||
create (fn [interceptors]
|
||||
(http/ring-handler
|
||||
(http/router
|
||||
["/api"
|
||||
|
|
@ -213,6 +214,16 @@
|
|||
{:status 200
|
||||
:body (-> req :parameters :body)})}}]
|
||||
|
||||
["/warn" {:summary "log and return original"
|
||||
:coercion (reitit.coercion.malli/create {:transformers {},
|
||||
:on-invalid (fn [value error]
|
||||
(reset! most-recent error)
|
||||
value)})
|
||||
:post {:parameters {:body [:map [:x int?]]}
|
||||
:responses {200 {:body [:map [:x int?]]}}
|
||||
:handler (fn [req]
|
||||
{:status 200
|
||||
:body (-> req :parameters :body)})}}]
|
||||
["/skip" {:summary "skip"
|
||||
:coercion (reitit.coercion.malli/create {:enabled false})
|
||||
:post {:parameters {:body [:map [:x int?]]}
|
||||
|
|
@ -290,6 +301,19 @@
|
|||
:reitit.interceptor/handler]
|
||||
(mounted-interceptor app "/api/validate" :post))))
|
||||
|
||||
(testing "validation, log on invalid"
|
||||
(is (= 123 (:body (app {:uri "/api/warn"
|
||||
:request-method :post
|
||||
:muuntaja/request {:format "application/edn"}
|
||||
:body-params 123}))))
|
||||
(is (= [:reitit.http.coercion/coerce-exceptions
|
||||
:reitit.http.coercion/coerce-request
|
||||
:reitit.http.coercion/coerce-response
|
||||
:reitit.interceptor/handler]
|
||||
(mounted-interceptor app "/api/warn" :post)))
|
||||
(let [received @most-recent]
|
||||
(is (= 123 (-> @most-recent :errors first :value)))))
|
||||
|
||||
(testing "no tranformation & validation"
|
||||
(is (= 123 (:body (app {:uri "/api/no-op"
|
||||
:request-method :post
|
||||
|
|
|
|||
|
|
@ -245,7 +245,8 @@
|
|||
(reduce custom-meta-merge-checking-parameters left (cons right more))))
|
||||
|
||||
(deftest malli-coercion-test
|
||||
(let [create (fn [middleware routes]
|
||||
(let [most-recent (atom nil)
|
||||
create (fn [middleware routes]
|
||||
(ring/ring-handler
|
||||
(ring/router
|
||||
routes
|
||||
|
|
@ -270,6 +271,17 @@
|
|||
{:status 200
|
||||
:body (-> req :parameters :body)})}}]
|
||||
|
||||
["/warn" {:summary "log and return original"
|
||||
:coercion (reitit.coercion.malli/create {:transformers {},
|
||||
:on-invalid (fn [value error]
|
||||
(reset! most-recent error)
|
||||
value)})
|
||||
:post {:parameters {:body [:map [:x int?]]}
|
||||
:responses {200 {:body [:map [:x int?]]}}
|
||||
:handler (fn [req]
|
||||
{:status 200
|
||||
:body (-> req :parameters :body)})}}]
|
||||
|
||||
["/skip" {:summary "skip"
|
||||
:coercion (reitit.coercion.malli/create {:enabled false})
|
||||
:post {:parameters {:body [:map [:x int?]]}
|
||||
|
|
@ -311,7 +323,16 @@
|
|||
:handler (fn [req]
|
||||
{:status 200
|
||||
:body (-> req :parameters :body)})}}]
|
||||
|
||||
["/warn" {:summary "log and return original"
|
||||
:coercion (reitit.coercion.malli/create {:transformers {}
|
||||
:on-invalid (fn [value error]
|
||||
(reset! most-recent error)
|
||||
value)})
|
||||
:post {:parameters {:body {:x int?}}
|
||||
:responses {200 {:body {:x int?}}}
|
||||
:handler (fn [req]
|
||||
{:status 200
|
||||
:body (-> req :parameters :body)})}}]
|
||||
["/skip" {:summary "skip"
|
||||
:coercion (reitit.coercion.malli/create {:enabled false})
|
||||
:post {:parameters {:body {:x int?}}
|
||||
|
|
@ -397,6 +418,18 @@
|
|||
:reitit.ring.coercion/coerce-response]
|
||||
(mounted-middleware app "/api/no-op" :post))))
|
||||
|
||||
(testing "validate and log when invalid"
|
||||
(reset! most-recent nil)
|
||||
(is (= 123 (:body (app {:uri "/api/warn"
|
||||
:request-method :post
|
||||
:muuntaja/request {:format "application/edn"}
|
||||
:body-params 123}))))
|
||||
(is (= 123 (-> @most-recent :errors first :value)))
|
||||
(is (= [:reitit.ring.coercion/coerce-exceptions
|
||||
:reitit.ring.coercion/coerce-request
|
||||
:reitit.ring.coercion/coerce-response]
|
||||
(mounted-middleware app "/api/warn" :post))))
|
||||
|
||||
(testing "skipping coercion"
|
||||
(is (= nil (:body (app {:uri "/api/skip"
|
||||
:request-method :post
|
||||
|
|
|
|||
Loading…
Reference in a new issue