Fork me on GitHub
#reitit
<
2020-10-30
>
geraldodev12:10:03

i'm mirroring https://github.com/metosin/reitit/blob/master/examples/ring-malli-swagger/src/example/server.clj as much as I can but I'm not getting coercion. I'm getting validation , but no coercion of request body.

Eric Ihli14:10:14

I don't know if this is related, but for that second argument to `ring/router`, if I pass `{:compile coercion/compile-request-coercers ,,,`, then I get coercion. If I pass it as `{:data {:compile coercion/compile-request-coercers ,,,` then I get an error that `reitit.ring.Endpoint cannot be cast to clojure.lang.IFn`. Every example I see has :compile nested under :data. So I'm trying to figure out why mine works when not nested and breaks when nested. Are both supposed to be supported? Was there an update and one is legacy?

geraldodev14:10:10

I've tried that (compile on :data) but I've got 405 method not allowed on my requests

Eric Ihli14:10:19

Yeah I've run into that too. But only on requests that don't use coercion. Still trying to figure it out.

(defn router [db]
  (ring/router
   [["/" {:handler user-ctl/default}]
    ["/reset" {:handler user-ctl/reset-changes}]
    ["/user"
     ["/list" {:handler user-ctl/get-users}]
     ["/form" {:handler user-ctl/edit}]
     ["/form/:id" {:parameters {:path {:id int?}}
                   :coercion reitit.coercion.spec/coercion
                   :get {:handler user-ctl/edit}}]
     ["/save" {:post {:handler user-ctl/save}}]
     ["/delete/:id" {:get {:handler user-ctl/delete-by-id}
                     :coercion reitit.coercion.spec/coercion
                     :parameters {:path {:id int?}}}]]]
   {:compile coercion/compile-request-coercers
    :data {:db db
           :middleware [my-middleware
                        coerce-request-middleware
                        parameters/parameters-middleware
                        wrap-keyword-params
                        middleware-db]}}))

Eric Ihli14:10:54

I can do match and coerce /user/form/3 for example. But /reset and / result in a 405.

Eric Ihli14:10:39

It's like that compile key is causing routes with a :coercion key to work but every other route to break.

Eric Ihli14:10:20

Ugh... Ok.

["/list" {:get {:handler user-ctl/get-users}}]

Eric Ihli14:10:01

Adding that :get key to to the route got rid of the 405. So... I guess when your using coercion, you must manually specify methods for routes that don't have a :parameters and :coercion keys? Nope... dirty repl.

ikitommi16:10:41

sounds resolved? great!

Eric Ihli17:10:04

No. Thought it was resolved, but my REPL was in a state where the issue wouldn't present itself.

Eric Ihli23:10:07

Ok. I haven't pinpointed exactly what part of the code causes this to be the case, but I have found a set of options that works.

Eric Ihli23:10:24

My understanding is that when a routers options includes a compile coercion/compile-request-coercers key/value, then any route that doesn't include a {:coercion reitit.coercion.spec/coercion :parameters {,,,}} will get excluded from the router.

Eric Ihli23:10:07

It's like a router that compiles coercion routes must ONLY include coerced routes. Other routes don't get matched against.

geraldodev17:10:14

@ikitommi I've tried the example posting x: "1" y: "1" and it does to coerce body parameters to 1. What can I do to coerce body parameters ?

ikitommi17:10:27

JSON coercion doen't coerce string->number, as JSON supports numbers. If you need that, you have to swap the json- body coercion to use String Coercion. Can be done by creating a coercion impl with options. There should be example out somewhere. Sorry, the docs could be better

👍 3
geraldodev19:10:44

(def custom-coercion (reitit.coercion.malli/create { :transformers {:body {:default reitit.coercion.malli/string-transformer-provider :formats {"application/json" reitit.coercion.malli/string-transformer-provider}} :string {:default reitit.coercion.malli/string-transformer-provider} :response {:default reitit.coercion.malli/default-transformer-provider}} ;; set of keys to include in error messages :error-keys #{#_:type :coercion :in :schema :value :errors :humanized #_:transformed} ;; schema identity function (default: close all map schemas) :compile malli.util/closed-schema ;; strip-extra-keys (effects only predefined transformers) :strip-extra-keys true ;; add/set default values :default-values true ;; malli options :options nil}) ) @ikitommi with the information you provided I've found https://github.com/metosin/reitit/issues/288 and with this custom-coercion it worked.

tjb20:10:20

hey everyone i am new to clj + cljs. i am attempting to use reitit in a cljs project. i have my routes defined in a separate cljs file but i get this error when trying to refer to a route in another file

tjb20:10:16

in my view file i am using this

[:a.button.is-danger {:href (rfe/href ::create-job)} "Post a Job"]
it says that it cannot find ::create-job but in my routes file i have this
(def routes
  [["/"
    {:name ::frontpage
     :view home-view/home-page}]

   ["/create"
    {:name ::create-job
     :view create-job-view/root}]])

Eric Ihli20:10:58

I don't have high confidence that what I'm about to say is related to your issue, but I see you say "new to clj" and I see a namespaced keyword ::create-job and I remember the times I ran into problems due to namespace issues. Are those routes defined in client.home.view? I see the error message say that it can't find client.home.view/create-job. The double colon before a keyword namespaces it. so ::create-job becomes :http://whatever.namespace.its.in/create-job`.

Eric Ihli20:10:29

So if you use ::create-job in two different files (i.e. namespaces (in general)) then it will be two different values.

tjb20:10:23

ohhhhhhhhhhhh

tjb20:10:21

the routes are defined in client.api

tjb20:10:25

(ns client.api
  (:require [client.home.view :as home-view]
            [client.job.create.view :as create-job-view]))

(def routes
  [["/"
    {:name :frontpage
     :view #'home-view/home-page}]

   ["/create"
    {:name :create-job
     :view #'create-job-view/root}]])

tjb20:10:42

(just changing things around rn per you comment and what im seeing on the web)

Eric Ihli20:10:24

Oh. Well I see in that code you just pasted :create-job is not namespaced. Try namespacing it, ::create-job in client.api and then try accessing it from your view as :client.api/create-job.

Eric Ihli20:10:37

Or maybe de-namespace it everywhere.

geraldodev20:10:49

I have http://app.ui.contratualizacao.menu on which I define : ::contrato which expands to http://app.ui.contratualizacao.menu/contrato. On other file i need to refer to that name. I require [http://app.ui.contratualizacao.menu :as menu-contr] and refer as ::menu-contr/contrato.

geraldodev20:10:29

Or you can be explicit like @UJP37GW2K is suggesting

tjb20:10:54

ah i got it!

tjb20:10:03

i router is

(ns client.api
  (:require [client.home.view :as home-view]
            [client.job.create.view :as create-job-view]))

(def routes
  [["/"
    {:name ::frontpage
     :view home-view/home-page}]

   ["/create"
    {:name ::create-job
     :view create-job-view/root}]])

tjb20:10:15

my call for the route is

tjb20:10:16

[:a.button.is-danger {:href (rfe/href :create-job)} "Post a Job"]

tjb20:10:28

prior i had

[:a.button.is-danger {:href (rfe/href ::create-job)} "Post a Job"]

tjb20:10:42

::create-job vs. :create-job in the href

tjb20:10:52

thanks everyone for the help and rubber ducking!