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)))
So far I've found only this solution (which seems to work):
(context "/api" []
,,,
(context "/check" []
(resource
{: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))}})))
@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]}
I think the resource
version is cleaner here.
@ikitommi great, thanks!
@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.
@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.
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.
CLJ-2251 would solve this for good.
But, I'll recheck spec-coerce
, I think it's a great lib too.