Fork me on GitHub
#ring-swagger
<
2019-04-01
>
Empperi08:04:40

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

Empperi08:04:58

Oh, ok. Got it. When using :decode/string it's the second parameter passed to the function which actually contains the value being coerced

Empperi08:04:24

Meaning this spec works correctly:

(s/def ::active
  (st/spec
    {:spec boolean?
     :decode/string #(= "YES" %2)}))

ikitommi08:04:05

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))

Empperi08:04:05

Now if only json-transformer would do anything...

Empperi08:04:37

Any quick tips why it returns the given data structure as-is back?

Empperi08:04:48

I can't recall ever getting json-transformer to work but I'll be damned if I'll give up this time

ikitommi08:04:14

(st/coerce keyword? "kikka" st/json-transformer)
; :kikka

ikitommi08:04:24

seems to work ok?

ikitommi08:04:58

If you have a failing minimal example, please share

Empperi08:04:56

(s/def ::bar integer?)
(s/def ::foo (s/keys :req [::bar]))
(st/coerce ::foo {::bar "2"} st/json-transformer)
=> {::bar "2"}

Empperi08:04:23

That's as minimal as I can get it

Empperi08:04:40

Most likely I'm doing something wrong but that's how I've understood it should work

Empperi08:04:31

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.

ikitommi08:04:24

JSON can represent ints, so there is no string->int conversion.

Empperi08:04:50

Right. Well, same thing happens when explicitly defining :decode/string

ikitommi08:04:56

(s/def ::bar keyword?)
(s/def ::foo (s/keys :req [::bar]))
(st/coerce ::foo {::bar "2"} st/json-transformer)
; #:user{:bar :2}

ikitommi08:04:48

oh, the spec keys don’t derive

ikitommi08:04:59

you need to define :decode/json for that.

ikitommi08:04:58

documentation of this all is buried somewhere….

Empperi08:04:18

And boom, my problem disappeared by replacing :decode/string to :decode/json

Empperi08:04:02

Not exactly obvious

Empperi08:04:24

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

Empperi08:04:03

But thanks, that solved my problem 👍

ikitommi08:04:46

your welcome

Empperi10:04:38

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?

Empperi10:04:01

Assuming that the Content-Type is application/json of course

Empperi10:04:06

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

Empperi10:04:36

It would kinda make sense if it did not do that since otherwise generating correct api-docs would get pretty hard

Empperi10:04:12

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

Empperi10:04:01

(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)

Empperi10:04:34

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?

Empperi10:04:38

Right. I'm stupid. Just write your own coercion handler for compojure-api and be done with it...

Empperi11:04:54

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

Empperi11:04:04

I must be missing something

Empperi11:04:55

If I pass just something like :coercion "what" then it gives me a very clear error

Empperi11:04:03

so it is doing something 🙂

ikitommi13:04:52

@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.

ikitommi13:04:21

I think it's a bad design as the :encode/json doesn't work with it.

ikitommi13:04:18

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

Empperi13:04:59

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

Empperi13:04:51

Yeah, I've read through the source codes several times by now 😄

Empperi13:04:16

My main problem initially was that I named my type-transformer into something descriptive what it was under :name

Empperi13:04:28

but that kinda screwed up finding the decoders and encoders

Empperi13:04:42

Since they were written with :json/encode etc

Empperi13:04:12

But, before I realized that I managed to typo a keyword-value pair outside the options map of type-transformer...

Empperi13:04:37

It compiled happily since it's parameter definition is [& options-or-transformers]

ikitommi13:04:37

Oh, the latest json & string are named :strict-string and :strict-json, will not work

Empperi13:04:44

But it just didn't work - obviously

ikitommi13:04:17

Would you like to do a PR?

Empperi13:04:35

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 😄

ikitommi13:04:08

good reason :)

ikitommi13:04:47

I can fix those later today, 15min max

Empperi13:04:02

Still doesn't call my encoder/decoder functions which I've declared in my type-transformer

Empperi13:04:10

but it's starting to make more sense

Empperi14:04:18

For some reason when I pass the spec-coercion-handler into api under :coercion it never calls the pass-through function

Empperi14:04:45

Been debugging this for several hours now

Empperi14:04:17

And obviously this pass-through stuff is there just so that I can confirm compojure-api is calling my coercion functions

Empperi14:04:41

(and yeah, I've tried without that when block too)

Empperi14:04:50

I must be missing something

ikitommi18:04:44

pushed out [metosin/compojure-api "2.0.0-alpha30"] with updated deps, including new spec-tools, muuntaja & ring-swagger.

ikitommi18:04:30

@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.

ikitommi18:04:06

now, it just calls -decode on response body, which is kinda nonsense.