@ikitommi, I’m picking up on some of the stuff @psalaberria002 was pestering you with.
So I’ve got the following:
(def my-date-time-conforming
(st/type-conforming
(assoc conform/string-type-conforming
:date-time
(fn [_ value]
(DateTime. value)))))
(spec/def ::id int?)
(spec/def ::name string?)
(spec/def ::date (st/spec (partial instance? DateTime) {:type :date-time}))
(spec/def ::id-name-date (spec/keys :req-un [::id ::name ::date]))
(context "/spec-test" []
:coercion :spec
(GET "/foo" []
:return ::id-name-date
(ok {:id 1 :name "foo" :date (DateTime.)}))
(POST "/foo" []
:return ::id-name-date
:body [b ::id-name-date]
(ok (do (println b) b))))
The GET
request seems to work perfectly, whereas I get an exception on the POST, indicating that somewhere along the line, something doesn’t know how to convert a DateTime
to a string:
Caused by: com.fasterxml.jackson.core.JsonGenerationException: Cannot JSON encode object of class: class java.lang.Class: class org.joda.time.DateTime
That’s one of my problems.
The second problem is that swagger understands that I have a field that’s named “date” but it doesn’t understand what kind of field it is, so it presents it as an object:
{
"id": 0,
"name": "string",
"date": {}
}
So how do I inform swagger/compojure-api/spec-tools that anything with the type :date-time
should have an example value like "2017-10-16T11:59:04.180Z"
So it seems like there is something wrong with coercion on the way in.
any way to use defroutes with the new :spec coercion?
(GET "/api/company/:id" req :path-params [id :- spec/int?]
returns: No implementation of method: :spec of protocol: #'schema.core/Schema found for class: spec_tools.core.Spec
@slipset hi, compojure-api separates three different stages for coercion: request
, response
and string
. You need to add the conformer also to reponse
to make it work.
also, spec conforming is a one-way pipeline, see https://gist.github.com/ikitommi/8a97080357fd7de06882ba99f0df974c.
to get DateTime into a String, you should add a cheshire encoder. (or jsonista encoder if you use that).
I think we need something simpler for the two-way transformations. Good ideas welcome.
(GET "/api/company/:id" req
:coercion :spec
:path-params [id :- spec/int?]
:coercion
can be set to api
, context
and all endpoints. And resource
too.
hmm
I tried setting :coercion on the defroutes, but that didn't work
and you can explicitely say :coercion compojure.api.coercion.spec/SpecCoercion
. All multimethods can break in develop with wild use of tools-refresh.
thanks for the individual route hints
it also works after I changed the whole thing from defroutes to defapi... is there any difference there?
defroutes doesn’t allow that… you could have a empty context
?
defapi
mounts all the standard middleware, so you shoudn’t use defapi
(or api
) under a defapi
(or api
).
hmmmmmmm ok
there is a sample routing app in https://github.com/metosin/c2
(gotta go now)
@ikitommi it worked!
I got
(defn str->date-time [_ value]
(try
(DateTime. value)
(catch Exception e
value)))
(def my-date-time-conforming
(st/type-conforming
(assoc conform/string-type-conforming
:date-time
str->date-time)))
(def custom-coercion
(-> compojure.api.coercion.spec/default-options
(assoc-in
[:body :formats "application/json"]
(st/type-conforming
(merge
conform/json-type-conforming
{:date-time str->date-time}
conform/strip-extra-keys-type-conforming)))
compojure.api.coercion.spec/create-coercion))
(spec/def ::id int?)
(spec/def ::name string?)
(spec/def ::date (st/spec (partial instance? DateTime) {:type :date-time
:json-schema/default "2017-10-12T05:04:57.585Z"}))
(spec/def ::id-name-date (spec/keys :req-un [::id ::name ::date]))
and
(context "/spec-test" []
:coercion custom-coercion
(GET "/foo" []
:return ::id-name-date
(ok {:id 1 :name "foo" :date (DateTime.)}))
(POST "/foo" []
:return ::id-name-date
:body [b ::id-name-date]
(ok (do (println b) b #_(assoc b :date "lol")))))
and swagger shows me
{
"id": 0,
"name": "string",
"date": "2017-10-12T05:04:57.585Z"
}
Only problem now is that if I pass an unparsable date, eg 2017-lol-01
either in the request or the response, Muuntaja/jackson becomes upset.
So it seems like str->date-time
needs to return some value (other than an exception) to indicate that it failed to parse the string.
I would have thought that something like
(defn str->date-time [_ value]
(try
(DateTime. value)
(catch Exception e
::spec/invalid)))
would solve it, but alas.