reitit

https://cljdoc.org/d/metosin/reitit/ https://github.com/metosin/reitit/
jaime 2020-06-13T15:22:18.121400Z

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

ikitommi 2020-06-13T18:11:06.125200Z

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

ikitommi 2020-06-13T18:13:31.127600Z

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

jaime 2020-06-14T19:48:26.129300Z

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

ikitommi 2020-06-14T19:51:24.129500Z

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

👍 1
ikitommi 2020-06-14T19:51:45.129700Z

the coercion impl looks correct

jaime 2020-06-14T19:58:12.129900Z

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

jaime 2020-06-14T19:59:50.130100Z

Yeah it looks like 🙂

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

ikitommi 2020-06-13T18:13:37.127900Z

hope this helps

kitallis 2020-06-13T18:19:58.128700Z

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

kitallis 2020-06-13T18:22:41.129200Z

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