Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support independent request and response validation in Malli coercer #656

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions modules/reitit-malli/src/reitit/coercion/malli.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
(defprotocol Coercer
(-decode [this value])
(-encode [this value])
(-validate [this value])
(-validate-request [this value])
(-validate-response [this value])
(-explain [this value]))

(defprotocol TransformationProvider
Expand All @@ -36,17 +37,20 @@
(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- -coercer [schema type transformers f {:keys [validate-request validate-response validate enabled options]}]
(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))
validator (m/validator schema options)
request-validator (if (and validate validate-request) validator (constantly true))
response-validator (if (and validate validate-response) validator (constantly true))
explainer (m/explainer schema options)]
(reify Coercer
(-decode [_ value] (decoder value))
(-encode [_ value] (encoder value))
(-validate [_ value] (validator value))
(-validate-request [_ value] (request-validator value))
(-validate-response [_ value] (response-validator value))
(-explain [_ value] (explainer value)))))
{:keys [formats default]} (transformers type)
default-coercer (->coercer default)
Expand All @@ -59,7 +63,7 @@
(fn [value format]
(if-let [coercer (get-coercer format)]
(let [transformed (-decode coercer value)]
(if (-validate coercer transformed)
(if (-validate-request coercer transformed)
transformed
(let [error (-explain coercer transformed)]
(coercion/map->CoercionError
Expand All @@ -69,7 +73,7 @@
(fn [value format]
(let [transformed (-decode default-coercer value)]
(if-let [coercer (get-coercer format)]
(if (-validate coercer transformed)
(if (-validate-response coercer transformed)
(-encode coercer transformed)
(let [error (-explain coercer transformed)]
(coercion/map->CoercionError
Expand All @@ -95,6 +99,8 @@
:compile mu/closed-schema
;; validate request & response
:validate true
:validate-request true
:validate-response true
;; top-level short-circuit to disable request & response coercion
:enabled true
;; strip-extra-keys (affects only predefined transformers)
Expand Down
42 changes: 42 additions & 0 deletions test/clj/reitit/http_coercion_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,22 @@
{:status 200
:body (-> req :parameters :body)})}}]

["/validate-request" {:summary "just request validation"
:coercion (reitit.coercion.malli/create {:transformers {} :validate-response false})
:post {:parameters {:body [:map [:x int?]]}
:responses {200 {:body [:map [:x int?]]}}
:handler (fn [req]
{:status 200
:body "I am do not validate"})}}]

["/validate-response" {:summary "just response validation"
:coercion (reitit.coercion.malli/create {:transformers {} :validate-request false})
:post {:parameters {:body [:map [:x int?]]}
:responses {200 {:body [:map [:x int?]]}}
:handler (fn [req]
{:status 200
:body "I do not validate"})}}]

["/no-op" {:summary "no-operation"
:coercion (reitit.coercion.malli/create {:transformers {}, :validate false})
:post {:parameters {:body [:map [:x int?]]}
Expand Down Expand Up @@ -290,11 +306,37 @@
:reitit.interceptor/handler]
(mounted-interceptor app "/api/validate" :post))))

(testing "just request validation"
(is (= 400 (:status (app {:uri "/api/validate-request"
: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/validate-request" :post))))

(testing "just response validation"
(is (= 500 (:status (app {:uri "/api/validate-response"
:request-method :post
:muuntaja/request {:format "application/edn"}
:body-params "not an integer"}))))
(is (= [:reitit.http.coercion/coerce-exceptions
:reitit.http.coercion/coerce-request
:reitit.http.coercion/coerce-response
:reitit.interceptor/handler]
(mounted-interceptor app "/api/validate-response" :post))))

(testing "no tranformation & validation"
(is (= 123 (:body (app {:uri "/api/no-op"
:request-method :post
:muuntaja/request {:format "application/edn"}
:body-params 123}))))
(is (= "123" (:body (app {:uri "/api/no-op"
: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
Expand Down
Loading