Given I have (s/def ::installment-amount int?)
How to tell reitit, when it receives a :body
with installment-amount
of string, try to convert it to int
(because that is the spec), instead of returning 400. Do I need to create custom transformer?
EDIT:
I'm using the default provided by reitit, but does not seem to work. I'm getting error.
{:problems [{:path [installment-amount], :pred clojure.core/int?, :via [limeray.api.handler/create-request limeray.api.handler/installment-amount], :val 500, :in [installment-amount]} {:path [target-amount], :pred clojure.core/int?, :via [limeray.api.handler/create-request limeray.api.handler/target-amount], :val 20000, :in [target-amount]}], :value {:installment-amount 500, :target-date 2020-04-30, :title hey, :wallet-id 968d8164-b864-441f-804b-7ade37ce112f, :target-amount 20000}, :type reitit.coercion/request-coercion, :spec (spec-tools.core/spec {:spec (clojure.spec.alpha/keys :req-un [:limeray.api.handler/title :limeray.api.handler/target-date :limeray.api.handler/target-amount :limeray.api.handler/installment-amount]), :type :map, :leaf? false}), :coercion spec, :in [request body-params]}
My ring handler setup
(def router-options {:exception pretty/exception
:data {:muuntaja m/instance
:coercion rcs/coercion
:middleware [exception-middleware
muuntaja/format-middleware
rrc/coerce-exceptions-middleware
rrc/coerce-request-middleware
rrc/coerce-response-middleware]}})
(s/def ::title string?)
(s/def ::target-date string?)
(s/def ::target-amount int?)
(s/def ::installment-amount int?)
(s/def ::wallet-id uuid?)
(s/def ::create-request
(s/keys :req-un [::title
::target-date
::target-amount
::installment-amount]))
;; ring handler
(defn make-app []
(ring/ring-handler
(ring/router
[["/swagger.json" {:get (swagger/create-swagger-handler)}]
["/api-docs/*" {:get (swagger-ui/create-swagger-ui-handler)}]
["/api"
["/goals"
{:post {:parameters {:body ::create-request}
:handler (fn [request]
(println "type is " (type (-> request :body-params :installment-amount)))
(let [created-goal (goals/create-goal (:body-params request))]
(-> (resp/created "" created-goal))))}}]]]
router-options)
(ring/create-default-handler)
ring-handler-opts))
@jaime.sangcap numbers are not converted from strings in body params by default, as all formats (json, edn and transit) can represent numbers. If you want to support that, you can change use string-transformer
as default- it does the string->* conversions.
so, you need a custom spec coercion impl, with different options.
Hi, is this what you mean by custom impl?
(def spec-coercion
(rcs/create
(-> rcs/default-options
(assoc-in [:transformers :body :default] rcs/string-transformer)
(assoc-in [:transformers :body :formats "application/json"] rcs/string-transformer))))
(def router-options {:exception pretty/exception
;;:reitit.middleware/transform mwdev/print-request-diffs
:data {:muuntaja m/instance
:coercion spec-coercion
:middleware [exception-middleware
muuntaja/format-middleware
rrc/coerce-exceptions-middleware
rrc/coerce-request-middleware
rrc/coerce-response-middleware]}})
When printing the type in the handler, I'm still getting type String instead of Integer for this spec (s/def ::installment-amount int?)
(println "type is " (type (-> request :body-params :installment-amount)))
the original :body-params
is not changed, the coerced parameters are push into :parameters
, here in [:parameters :body]
.
the coercion impl looks correct
Oh cool! It works now. Thanks a lot. The [:parameters :body]
is injected by rrc/coerce-request-middleware?
Yeah it looks like 🙂
(let [coerced (coercion/coerce-request coercers request)]
(handler (impl/fast-assoc request :parameters coerced))))
hope this helps
is there a standard way to compare a PartialMatch
and a Match
on just the path name without params?
nvm, (-> route :data :name)
wfm