ring-swagger & compojure-api
jumar 2018-05-15T14:11:45.000174Z

I have this compojure-api definition:

(context "/api" []
  :coercion :spec
  (POST "/api/check" []
    :body-params [user :- ::specs/user
                  k :- ::specs/k
                  address :- (s/nilable string?)]
    (do-sth user k address version)))
Now I'd like to add a new optional attribute version (and also fixing existing address to be truly optional). What's the easiest way to do it?
(context "/api" []
  :coercion :spec
  (POST "/api/check" []
    :body-params [user :- ::specs/user
                  k :- ::specs/k
;; s/nilable isn't enough - 400 bad request is returned if address and version are not present in request params
                  address :- (s/nilable string?)
                  version :- (s/nilable string?)]
    (do-sth user k address version)))

jumar 2018-05-15T14:50:31.000852Z

So far I've found only this solution (which seems to work):

(context "/api" []
  (context "/check" []
     {:coercion :spec
      :post {:parameters {:body-params (s/keys :req-un [::specs/user ::specs/k]
                                               :opt-un [::address ::version])}
             :handler (fn [{{:keys [user k address version]} :body-params}]
                        (do-sth user k address version))}})))

ikitommi 2018-05-15T14:55:56.000934Z

@jumar all the -params use the Plumatic fn syntax (https://github.com/plumatic/plumbing#bring-on-defnk). With it, the optional is maked with curly braces. This should work:

(def app
  (context "/api" []
    :coercion :spec
    (POST "/check" []
      :body-params [{address :- (s/nilable string?) nil}
                    {version :- (s/nilable string?) nil}]
      (ok [address version]))))

(app {:request-method :post, :uri "/api/check", :body-params {}})
; {:status 200, :headers {}, :body [nil nil]}

(app {:request-method :post, :uri "/api/check", :body-params {:address "Hämeenkatu"}})
; {:status 200, :headers {}, :body ["Hämeenkatu" nil]}

ikitommi 2018-05-15T14:57:03.000705Z

I think the resource version is cleaner here.

jumar 2018-05-15T14:59:34.000755Z

@ikitommi great, thanks!

ikitommi 2018-05-15T15:00:41.000886Z

@plins Threads should be managed somehow. I would recommend using some lifecycle management solution like Integrant, Mount or Component to start (and stop) those. There is a example of Mount in https://github.com/yogthos/memory-hole


Hey guys sorry for the ignorant question but may I ask why is spec tools build around a separate model instead of using Clojure spec directly? I see this project https://github.com/wilkerlucio/spec-coerce and wonder if there is something that I'm not understanding since I thought that it was not possible to do it based on the current Clojure directly implementation.

ikitommi 2018-05-15T20:12:20.000620Z

@carocad good question! spec-tools actually builds on top of clojure.spec. if the patch in CLJ-2116 would have been accepted, spec-tools would work 100% with normal specs too and st/spec wrapping could be made optional. There is even an issue about spec meta-data, so we could remove the wrapping completely. For now, there are two ways to do this: 1) lean on s/conform* which is implemented for all specs (e.g. works with all regexps too) and wrap the specs to enable overriding it at runtime, e.g. "spec-tools way" or b) copy the spec walking code (the s/conform* impls) into the coercion library. I think spec-coerce has done this, for most common specs, but not all like regexps. Also, parsing spec forms at each invocation is slow.

ikitommi 2018-05-15T20:14:55.000710Z

I hope the next version of spec has something better for 3rd party libaries. I think the api in spec-tools is ok (the new encode & decode and separate transformer like Schema has), but the implementation behind will change a lot when/if spec evolves.

ikitommi 2018-05-15T20:15:39.000024Z

CLJ-2251 would solve this for good.

ikitommi 2018-05-15T20:16:33.000118Z

But, I'll recheck spec-coerce, I think it's a great lib too.