fix: response coercion for unlisted http statuses, when no default

fixes #742
This commit is contained in:
Joel Kaasinen 2025-05-27 12:09:38 +03:00
parent 55c30af979
commit 5025ca3a75
2 changed files with 42 additions and 22 deletions

View file

@ -178,8 +178,9 @@
(let [format->coercer (or (status->format->coercer (:status response)) (let [format->coercer (or (status->format->coercer (:status response))
(status->format->coercer :default)) (status->format->coercer :default))
format (extract-response-format request response) format (extract-response-format request response)
coercer (or (format->coercer format) coercer (when format->coercer
(format->coercer :default))] (or (format->coercer format)
(format->coercer :default)))]
(if-not coercer (if-not coercer
response response
(let [value (:body response) (let [value (:body response)

View file

@ -695,15 +695,22 @@
(testing (str coercion) (testing (str coercion)
(let [app (ring/ring-handler (let [app (ring/ring-handler
(ring/router (ring/router
["/foo" {:post {:responses {200 {:content {:default {:schema schema-200}}} [["/foo" {:post {:responses {200 {:content {:default {:schema schema-200}}}
201 {:content {"application/edn" {:schema schema-200}}} 201 {:content {"application/edn" {:schema schema-200}}}
202 {:description "status code and content-type explicitly mentioned, but no :schema" 202 {:description "status code and content-type explicitly mentioned, but no :schema"
:content {"application/edn" {} :content {"application/edn" {}
"application/json" {}}} "application/json" {}}}
:default {:content {"application/json" {:schema schema-default}}}} :default {:content {"application/json" {:schema schema-default}}}}
:handler (fn [req] :handler (fn [req]
{:status (-> req :body-params :status) {:status (-> req :body-params :status)
:body (-> req :body-params :response)})}}] :body (-> req :body-params :response)})}}]
["/bar" {:post {:responses {200 {:content {:default {:schema schema-200}}}}
:handler (fn [req]
{:status (-> req :body-params :status)
:body (-> req :body-params :response)})}}]
["/quux" {:post {:handler (fn [req]
{:status (-> req :body-params :status)
:body (-> req :body-params :response)})}}]]
{:validate reitit.ring.spec/validate {:validate reitit.ring.spec/validate
:data {:middleware [rrc/coerce-request-middleware :data {:middleware [rrc/coerce-request-middleware
rrc/coerce-response-middleware] rrc/coerce-response-middleware]
@ -713,40 +720,52 @@
(app request) (app request)
(catch ExceptionInfo e (catch ExceptionInfo e
(select-keys (ex-data e) [:type :in])))) (select-keys (ex-data e) [:type :in]))))
request (fn [body] request (fn [uri body]
{:request-method :post {:request-method :post
:uri "/foo" :uri uri
:muuntaja/request {:format "application/json"} :muuntaja/request {:format "application/json"}
:muuntaja/response {:format (:format body "application/json")} :muuntaja/response {:format (:format body "application/json")}
:body-params body})] :body-params body})]
(testing "explicit response schema" (testing "explicit response schema"
(is (= {:status 200 :body {:a 1}} (is (= {:status 200 :body {:a 1}}
(call (request {:status 200 :response {:a 1}}))) (call (request "/foo" {:status 200 :response {:a 1}})))
"valid response") "valid response")
(is (= {:type :reitit.coercion/response-coercion, :in [:response :body]} (is (= {:type :reitit.coercion/response-coercion, :in [:response :body]}
(call (request {:status 200 :response {:b 1}}))) (call (request "/foo" {:status 200 :response {:b 1}})))
"invalid response") "invalid response")
(is (= {:type :reitit.coercion/response-coercion, :in [:response :body]} (is (= {:type :reitit.coercion/response-coercion, :in [:response :body]}
(call (request {:status 200 :response {:b 1} :format "application/edn"}))) (call (request "/foo" {:status 200 :response {:b 1} :format "application/edn"})))
"invalid response, different content-type")) "invalid response, different content-type"))
(testing "explicit response schema, but for the wrong content-type" (testing "explicit response schema, but for the wrong content-type"
(is (= {:status 201 :body "anything goes!"} (is (= {:status 201 :body "anything goes!"}
(call (request {:status 201 :response "anything goes!"}))) (call (request "/foo" {:status 201 :response "anything goes!"})))
"no coercion applied")) "no coercion applied"))
(testing "response config without :schema" (testing "response config without :schema"
(is (= {:status 202 :body "anything goes!"} (is (= {:status 202 :body "anything goes!"}
(call (request {:status 202 :response "anything goes!"}))) (call (request "/foo" {:status 202 :response "anything goes!"})))
"no coercion applied")) "no coercion applied"))
(testing "default response schema" (testing "default response schema"
(is (= {:status 300 :body {:b 2}} (is (= {:status 300 :body {:b 2}}
(call (request {:status 300 :response {:b 2}}))) (call (request "/foo" {:status 300 :response {:b 2}})))
"valid response") "valid response")
(is (= {:type :reitit.coercion/response-coercion, :in [:response :body]} (is (= {:type :reitit.coercion/response-coercion, :in [:response :body]}
(call (request {:status 300 :response {:a 2}}))) (call (request "/foo" {:status 300 :response {:a 2}})))
"invalid response") "invalid response")
(is (= {:status 300 :body "anything goes!"} (is (= {:status 300 :body "anything goes!"}
(call (request {:status 300 :response "anything goes!" :format "application/edn"}))) (call (request "/foo" {:status 300 :response "anything goes!" :format "application/edn"})))
"no coercion applied due to content-type"))))))) "no coercion applied due to content-type"))
(testing "no default"
(is (= {:status 200 :body {:a 1}}
(call (request "/bar" {:status 200 :response {:a 1}})))
"valid response")
(testing "unlisted response code"
(is (= {:status 202 :body "anything goes!"}
(call (request "/bar" {:status 202 :response "anything goes!"})))
"no coercion applied")))
(testing "no response coercion"
(is (= {:status 200 :body "anything goes!"}
(call (request "/quux" {:status 200 :response "anything goes!"})))
"no coercion applied")))))))
#?(:clj #?(:clj
(deftest muuntaja-test (deftest muuntaja-test