What am I doing wrong? I'm expecting true
as the result:
(s/def ::active
(st/spec
{:spec boolean?
:decode/string (partial = "YES")}))
(st/coerce ::active "YES" st/string-transformer)
=> false
Oh, ok. Got it. When using :decode/string
it's the second parameter passed to the function which actually contains the value being coerced
Meaning this spec works correctly:
(s/def ::active
(st/spec
{:spec boolean?
:decode/string #(= "YES" %2)}))
yes, exposing spec to the transformer allows to do nice things like:
(defn strip-extra-keys [{:keys [::parse/keys]} x]
(if (and keys (map? x))
(select-keys x keys)
x))
Now if only json-transformer
would do anything...
Any quick tips why it returns the given data structure as-is back?
I can't recall ever getting json-transformer
to work but I'll be damned if I'll give up this time
(st/coerce keyword? "kikka" st/json-transformer)
; :kikka
seems to work ok?
If you have a failing minimal example, please share
(s/def ::bar integer?)
(s/def ::foo (s/keys :req [::bar]))
(st/coerce ::foo {::bar "2"} st/json-transformer)
=> {::bar "2"}
That's as minimal as I can get it
Most likely I'm doing something wrong but that's how I've understood it should work
End result doesn't pass validation for the very same spec, if I do the coercion "myself" by replacing "2"
with 2
it passes validations like expected.
JSON can represent ints, so there is no string->int conversion.
Right. Well, same thing happens when explicitly defining :decode/string
(s/def ::bar keyword?)
(s/def ::foo (s/keys :req [::bar]))
(st/coerce ::foo {::bar "2"} st/json-transformer)
; #:user{:bar :2}
see https://github.com/metosin/spec-tools/blob/master/src/spec_tools/transform.cljc#L183-L199
oh, the spec keys don’t derive
you need to define :decode/json
for that.
documentation of this all is buried somewhere….
And boom, my problem disappeared by replacing :decode/string
to :decode/json
Not exactly obvious
I suggest adding that somewhere in the documentation in such a way that it is easy to spot. For example here https://github.com/metosin/spec-tools/blob/master/docs/01_coercion.md
But thanks, that solved my problem 👍
https://github.com/metosin/spec-tools/commit/9e3131a054d5dffa4ae0634b6f7e1f6a9f857d15
your welcome
Hey, one more question: shouldn't compojure-api use automatically :encode/json
transformers with json-transformer if one has applied :coercion :spec
into api definition?
Assuming that the Content-Type
is application/json
of course
As it is now it doesn't seem to do this so I'm just checking if I'm assuming wrong before I dig deeper
It would kinda make sense if it did not do that since otherwise generating correct api-docs would get pretty hard
So based on that, I guess I need to create a separate spec for compojure-api if I want the end result JSON to be encoded to different format
(in this case I want to handle certain data as namespaced keywords within the app, but when I expose it via JSON API I want to drop the namespaces before writing the keywords as values since clients do not know or care about Clojure keywords)
Another question which is related to the same thing, any easy way to drop the namespaces from keywords in compojure-api when doing the JSON serialization?
Right. I'm stupid. Just write your own coercion handler for compojure-api and be done with it...
Ok. This is embarassing. It seems like compojure-api isn't even calling my coercion handler. No matter what I pass there as value it doesn't care
I must be missing something
If I pass just something like :coercion "what"
then it gives me a very clear error
so it is doing something 🙂
@niklas.collin the spec coercion calls :decode/json
for incoming json. For response bodies, the default spec coercion is "just validate", not "encode to json". Idea was that you should return valid edn from the endpoint and the json encoder will format that.
I think it's a bad design as the :encode/json
doesn't work with it.
One can set the response encoding on via custom coercion, but I think the default should be changed so that it's called :json
so that the custom :encode/json
things would work
Yeah
I've been debugging my custom coercer and I think I found the reason why it didn't work. I'm just a bad programmer writing bugs, that's all
https://github.com/metosin/compojure-api/blob/master/src/compojure/api/coercion/spec.clj#L136-L142
Yeah, I've read through the source codes several times by now 😄
My main problem initially was that I named my type-transformer into something descriptive what it was under :name
but that kinda screwed up finding the decoders and encoders
Since they were written with :json/encode
etc
But, before I realized that I managed to typo a keyword-value pair outside the options map of type-transformer
...
It compiled happily since it's parameter definition is [& options-or-transformers]
Oh, the latest json & string are named :strict-string
and :strict-json
, will not work
But it just didn't work - obviously
Would you like to do a PR?
Maybe at some point. Right now I need to get this working what I'm doing and head to home before my wife kills me 😄
good reason :)
I can fix those later today, 15min max
Still doesn't call my encoder/decoder functions which I've declared in my type-transformer
but it's starting to make more sense
For some reason when I pass the spec-coercion-handler
into api
under :coercion
it never calls the pass-through
function
Been debugging this for several hours now
And obviously this pass-through
stuff is there just so that I can confirm compojure-api is calling my coercion functions
(and yeah, I've tried without that when
block too)
I must be missing something
pushed out [metosin/compojure-api "2.0.0-alpha30"]
with updated deps, including new spec-tools, muuntaja & ring-swagger.
@niklas.collin the response encoding needs some extra care, but don’t have extra time any time soon for that. Quick analysis: the spec-tools/coerce
should have an optional 4th arg, on which function to call, would default to st/-decode
, could be set to st/-encode
.
now, it just calls -decode
on response body, which is kinda nonsense.
Roger