Fork me on GitHub
#beginners
<
2020-07-30
>
hoopes01:07:41

Hi, I’m trying to create a POST route with compojure-api using spec. My login-params spec has login and password as strings. This: :body-params [params :- ::login-params] gives me this as a request body in swagger:

{
 "params": {
  "login": "string",
  "password": "string"
 }
}
I’d like to remove that params key, and make login and password the root values of the POST body, but I can’t seem to find the incantation to do it. Am I missing something obvious? I’m using this as an example of more complex specs as POST body later, so I’d prefer not to put the login and password directly in the vector. Thanks so much!

seancorfield01:07:56

@hoopes I know nothing about compojure-api but I wonder if you could just say :body-params ::login-params ?

hoopes01:07:05

yeah, that was the first thing i tried 🙂 java.lang.UnsupportedOperationException: count not supported on this type: Keyword I’ve tried a bunch of different permutations, but all result in one error or another except the line above

seancorfield01:07:03

Sorry... ¯\(ツ)

hoopes01:07:08

is there a better channel to ask in? it seems a little beginner-y to open an issue on the repo, unless it might cause a doc update

seancorfield01:07:18

@hoopes I don't know. There's doesn't seem to be a channel for Compojure API although some of the Metosin projects have their own channels...

seancorfield01:07:43

They're based in Europe so maybe asking during a time they're online (on a weekday, perhaps)... and maybe tagging one of the main contributors or maintainers?

hoopes02:07:34

Will do. Thanks! Appreciate it greatly

ikitommi07:07:45

@hoopes @seancorfield I recall it's: :body [params ::login-params], the macro generates a let statement out of it. And there is #ring-swagger. Compojure-api would desperately need someone to write proper docs.

hoopes16:07:27

Hi @U055NJ5CC - i get the error

Caused by: java.lang.IllegalArgumentException: Binding is not valid, please refer to 
 for more information.

      binding: [params :my-ns/login-params]

did you mean: [params :- :my-ns/login-params]
Which, in turn, puts the params key in my POST body, which i'd like to avoid. I eventually found:
(resource                                                                                                                                                                                                                                
     {:coercion :spec                                                                                                                                                                                                                        
      :post {:parameters {:body-params ::tok-spec/login-params}                                                                                                                                                                              
             :responses {200 {:schema ::tok-spec/token-resp}}                                                                                                                                                                                
             :handler create-token}})))
Which gives me the clean login and password as top-level keys in the POST body.

hoopes16:07:58

But does not auto-coerce the response map to the given spec. I can do that by-hand, though.

ikitommi07:07:11

there is a nice "full" compojure-api example here to look for tips: https://github.com/yogthos/memory-hole

dehli13:07:01

Hello, is there some reason in my code that these two functions have very different performances (this is in Cljs)?

(def test-file (fs/readFileSync "/tmp/test-file" "utf8"))

(let [start-time (js/Date.now)
      x (map js/JSON.parse (string/split-lines test-file))]
  (prn (str "Clj - Count: " (count x) ", Time: " (- (js/Date.now) start-time)))) ;; Clj - Count: 985, Time: 10254

(let [start-time (js/Date.now)
      x ((js/eval "x => x.split('\n').filter(x => !!x).map(JSON.parse)")
         test-file)]
  (prn (str "JS - Count: " (count x) ", Time: " (- (js/Date.now) start-time)))) ;; JS - Count: 985, Time: 774

dehli14:07:06

It looks like clojure.string/split has different performance (https://cljs.github.io/api/clojure.string/split) so I’m just going to call out to JavaScript’s (.split test-file) instead

mafcocinco20:07:26

Suppose I have a map of functions like {:fn1 my.functions/fn1 :fn2 my.functions/fn2 ...} . Is there Clojure function that will invoke one of those functions in a thread first context? For example (-> (my-function-map :fn1) <invoke> (assoc :number 42)

mafcocinco20:07:56

<invoke> would call the function that resulted from the function map and, presumably, returns a map which is then threaded into the assoc.

seancorfield21:07:54

@mafcocinco I think it would just be easier to wrap extra (..) around that first expression to invoke it.

seancorfield21:07:09

(-> ((my-function-map :fn1)) (assoc :number 42)

mafcocinco21:07:00

that will do it. I could also bind it to a name with a let. Was more curious than anything so figured i would ask. Binding it to a name might actually be the best way from a readability stand-point, but opinions may vary.

seancorfield21:07:21

But if you really want it in the threaded expression, I think this would do it: (-> (my-function-map :fn1) (#(%)) (assoc :number 42)) -- I just don't think that's very clear.

seancorfield21:07:47

Or (-> (my-function-map :fn1) (as-> f (assoc (f) :number 42)))

seancorfield21:07:31

as-> is useful inside a -> pipeline for naming something and using it in a non-first-argument way.

jsn21:07:27

@mafcocinco (-> {:a (fn [] {:str "string"})} :a (apply nil) (assoc :number 42)) ?

seancorfield21:07:16

Ooh, (apply nil) that's a nice, sneaky way of doing it!

jsn21:07:49

I actually expected apply to accept 1 arg, there's no reason for it not to

mafcocinco21:07:59

yeah, trying not be clever. I think it is clearer to either invoke it on the part of the threading macro or, if that is not possible, bind it to a name.

jsn21:07:10

but since it doesn't, apply nil will have to do