metosin / reitit

A fast data-driven routing library for Clojure/Script
https://cljdoc.org/d/metosin/reitit/
Eclipse Public License 1.0
1.43k stars 257 forks source link

reitit.ring.coercion/coerce-request-middleware can't coerce specs defined with s/or #494

Open ehigham opened 3 years ago

ehigham commented 3 years ago

Hi! I hope you can help me with the following. I'm using clojure.spec.alpha/or to define a route that takes one of two maps as the request body. The maps don't have the same key set. I've found that when I send it a map of the second type, the :val being coerced is empty. I've included a small reproduction example below.

(require '[clojure.spec.alpha :as s])
(require '[reitit.ring :as ring])
(require '[reitit.ring.coercion])
(require '[reitit.coercion.spec])

(s/def ::a string?)
(s/def ::A (s/keys :req-un [::a]))

(s/def ::b vector?)
(s/def ::B (s/keys :req-un [::b]))

(def app
  (ring/ring-handler
    (ring/router
      [["/test" {:post {:coercion   reitit.coercion.spec/coercion
                        :parameters {:body (s/or :foo ::A :bar ::B)}
                        :responses  {200 {}}
                        :handler    (constantly true)}}]]
      {:data {:middleware [reitit.ring.coercion/coerce-exceptions-middleware
                           reitit.ring.coercion/coerce-request-middleware
                           reitit.ring.coercion/coerce-response-middleware]}})))

(app {:request-method :post
      :uri            "/test"
      :body-params    {:a ""}})
;=>
;true

(app {:request-method :post
      :uri            "/test"
      :body-params    {:b []}})
;=>
;{:status 400,
; :body {:spec "(spec-tools.core/spec {:spec (clojure.spec.alpha/or :foo :wfl.unit.spec-test/A :bar :wfl.unit.spec-test/B), :type [:or [:map]], :leaf? true})",
;        :problems [{:path [:foo],
;                    :pred "(clojure.core/fn [%] (clojure.core/contains? % :a))",
;                    :val {},
;                    :via [:wfl.unit.spec-test/A],
;                    :in []}
;                   {:path [:bar],
;                    :pred "(clojure.core/fn [%] (clojure.core/contains? % :b))",
;                    :val {},
;                    :via [:wfl.unit.spec-test/B],
;                    :in []}],
;        :type :reitit.coercion/request-coercion,
;        :coercion :spec,
;        :value {:b []},
;        :in [:request :body-params]}}

deps:

:deps
{org.clojure/clojure        {:mvn/version "1.10.3"}
 metosin/reitit             {:mvn/version "0.5.13" :exclusions [metosin/ring-swagger-ui]}
 metosin/ring-http-response {:mvn/version "0.9.2"}
 ring-oauth2/ring-oauth2    {:mvn/version "0.1.5"}
 ring/ring-core             {:mvn/version "1.9.3"}
 ring/ring-defaults         {:mvn/version "0.3.2"}
 ring/ring-devel            {:mvn/version "1.9.3"}
 ring/ring-jetty-adapter    {:mvn/version "1.9.3"}
 ring/ring-json             {:mvn/version "0.5.1"}
 ring/ring-servlet          {:mvn/version "1.9.3"}}

Thanks very much!

opqdonut commented 1 month ago

Either this wasn't fixed properly, or there's been a regression. The reproduction from the first message of this issue fails again. The metosin/spec-tools#255 issue has a repro on the spec-tools level.