Fork me on GitHub
#reitit
<
2020-06-13
>
jaime15:06:18

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))

ikitommi18:06:06

@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.

ikitommi18:06:31

so, you need a custom spec coercion impl, with different options.

jaime19:06:26

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)))

ikitommi19:06:24

the original :body-params is not changed, the coerced parameters are push into :parameters, here in [:parameters :body].

👍 3
ikitommi19:06:45

the coercion impl looks correct

jaime19:06:12

Oh cool! It works now. Thanks a lot. The [:parameters :body] is injected by rrc/coerce-request-middleware?

jaime19:06:50

Yeah it looks like 🙂

(let [coerced (coercion/coerce-request coercers request)]
                         (handler (impl/fast-assoc request :parameters coerced))))

ikitommi18:06:37

hope this helps

kitallis18:06:58

is there a standard way to compare a PartialMatch and a Match on just the path name without params?

kitallis18:06:41

nvm, (-> route :data :name) wfm