Fork me on GitHub
#malli
<
2022-02-23
>
oly09:02:14

just some thoughts in case its related, I am using transit+json because my keys are namespaces this is so they get preserved over the wire

lambder14:02:51

in my project

lambder14:02:14

I've added zoned-date-time

lambder14:02:31

in some other ns I define schema like:

(def ZDT
  (mt/time-schema :zoned-date-time))

lambder14:02:55

then in the reitit router I have malli coercion set and the endpoint has this:

lambder14:02:24

:parameters
  {:body
   [:map
    [:scrape-request
     [:map
      [:id int?]
      [:from-date
       [:map
        {:registry
         #:my-name-space{:zdt :zoned-date-time}}
        :my-name-space/zdt]]]]]},

lambder14:02:44

but when I try to create the routes I get:

lambder14:02:04

Execution error (ExceptionInfo) at reitit.exception/exception (exception.cljc:19).
:malli.core/invalid-schema {:schema :zoned-date-time}

{:schema :zoned-date-time}

lambder14:02:41

same problem if I don't use registry

lambder14:02:43

:parameters
  {:body
   [:map
    [:scrape-request
     [:map [:id int?] [:from-date :zoned-date-time]]]]},

lambder14:02:05

what am I doing wrong?

lambder14:02:31

I probably son't use the default registry correctly. Any hints please?

pithyless15:02:00

Instead of :zoned-date-time try referring directly to your var ZDT

pithyless15:02:31

If you're using the default registry, you need to make sure it registers :zoned-date-time as a known schema. See: https://github.com/metosin/malli/blob/2398df55ee806e25592fabf4d0c642ee3a2b233f/src/malli/core.cljc#L2371-L2378

lambder15:02:39

@U05476190 thanks, I did small step forward. The route construction no longer fails after I defined the custom registry via :coercion

(defn service-routes []
  ["/api"
   {:coercion   (reitit.coercion.malli/create
                  (merge reitit.coercion.malli/default-options
                    {:options {:registry (merge
                                           (m/default-schemas)
                                           (malli-time/time-schemas))}}))

lambder15:02:53

but , on Swagger UI I'm getting:

lambder15:02:07

the swagger.json is:

lambder15:02:05

I 'believe it should be:

"type": "string",
"format": "date-time" 
no?

👍 1
pithyless15:02:09

Sorry, no idea; probably best to ask outside of this thread - I'm not familiar with swagger/malli integration

lambder15:02:15

malli offers json swagger schema integration

Ronny Løvtangen16:02:32

Thanx @lambder, I was trying to add support for java.time.LocalDate and java.time.Instant to my project, and your post helped me. I added the code from https://github.com/metosin/malli/pull/545/files and imported it to the registry with

(defn service-routes []
  ["/api"
   {:coercion   (reitit.coercion.malli/create
                  {:options {:registry (merge
                                           (m/default-schemas)
                                           (malli-time/time-schemas))}})
(I believe the merge with default-options is not necessary, as reitit.coercion.malli/create already does that) I was then able to coerce a string to java.time.LocalDate by specifying the property as:
[:mydate {:description "My description"
          :json-schema/example "2021-11-05"}
 :local-date]
One thing I noticed, was that if I tried to send a non-parsable value, I got Error: Wrong number of args (2) passed to: slakt-service.malli.time/-&gt;error-reporter/-report--28426 By changing
(defn ->error-reporter
  [parser message]
  (fn -report [value]
to
(defn ->error-reporter
  [parser message]
  (fn -report [value _]
I instead got the message
"Should be local-date or LocalDate"

lambder17:02:09

have you seen the massages in the thread?

lambder17:02:35

one thing I'm missing is to make it work with edn request

lambder17:02:41

it works with json only

lambder17:02:33

I'll do

`(fn -report [value & _]
`

Ronny Løvtangen17:02:36

Yes. I was also wondering about type vs format. Not an expert on json schema, but looks like you are right

lambder17:02:43

just in case some other call needs single arity

👍 1
Ronny Løvtangen17:02:01

I also added :json-schema/example:

:local-date {:class LocalDate :parser -string->local-date :json-schema/type :local-date :json-schema/example "2022-02-23"}

...

:json-schema/example (:json-schema/example props -name)
Then I don’t have to (but could override if I want to) provide example in my schema.

lambder17:02:58

if I don't supply the example for date-time, the SwaggerUI will get one form me

Ronny Løvtangen17:02:21

If I don’t provide json-schema/example, Swagger will show “Unknown Type: local-date”. Maybe your change to

"type": "string",
"format": "date-time" 
is what makes Swagger able to provide an example

lambder17:02:47

local-date is not corret

lambder17:02:01

there is only date and date-time

lambder17:02:34

for local dates you probably want to adapt:

lambder17:02:36

customDate: 
  type: string 
  pattern: '^\d{4}(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])$'
  description: Custom date 
  example: "20210130"

Ronny Løvtangen17:02:08

Isn’t “date” a good fit for java.time.LocalDate? Format yyyy-MM-dd

Ronny Løvtangen17:02:54

With

:local-date {:class LocalDate :parser -string->local-date :json-schema/type :string :json-schema/format "date"}
Swagger provides an example :thumbsup:

lambder17:02:30

do you have your app working with edn encoding?

lambder17:02:35

do I need to provide analog of:

lambder17:02:37

{:transformers {:body {:default default-transformer-provider
                         :formats {"application/json" json-transformer-provider}}
                  :string {:default string-transformer-provider}
                  :response {:default default-transformer-provider
                             :formats {"application/json" json-transformer-provider}}}

Ronny Løvtangen17:02:20

No, sorry, haven't done anything to provide support for edn in our application

lambder18:02:43

when my edn request is:

lambder18:02:46

{
  :scrape-request {
    :id 1
    :from-date #time/zoned-date-time "2022-01-01T16:14:34.447Z"
  }
}

lambder18:02:10

I'm getting:

lambder18:02:15

{:schema "[:map {:closed true} [:scrape-request [:map {:closed true} [:id int?] [:from-date :zoned-date-time]]]]", :errors ({:path [:scrape-request :from-date], :in [:scrape-request :from-date], :schema ":zoned-date-time", :value (. java.time.ZonedDateTime parse "2022-01-01T16:14:34.447Z"), :message "Should be zoned-date-time or ZonedDateTime"}), :value {:scrape-request {:id 1, :from-date (. java.time.ZonedDateTime parse "2022-01-01T16:14:34.447Z")}}, :type :reitit.coercion/request-coercion, :coercion :malli, :in [:request :body-params], :humanized {:scrape-request {:from-date ["Should be zoned-date-time or ZonedDateTime"]}}}

lambder18:02:28

see: :from-date (. java.time.ZonedDateTime parse "2022-01-01T16:14:34.447Z")

lambder18:02:41

how on earth this form gets there are all?

lambder18:02:44

this is added to the registry:

lambder18:02:46

:zoned-date-time #IntoSchema{:type :zoned-date-time},

lambder18:02:10

using this:

lambder18:02:13

(m/-simple-schema
        {:type type
         :type-properties
               (cond-> {:error/fn         error-fn
                        :decode/json      {:enter safe-parser}
                        :encode/json      {:enter #(prn %)}
                        :json-schema/type (:json-schema/type props -name)}
                 (:json-schema/format props -name) (assoc :json-schema/format (:json-schema/format props -name)))
         :pred pred})

lambder18:02:10

the `safe-parser` is:

lambder18:02:20

provided by :

lambder18:02:30

(defn -string->zoned-date-time [x]
  (ZonedDateTime/parse x))

lambder18:02:55

so this :from-date (. java.time.ZonedDateTime parse "2022-01-01T16:14:34.447Z") looks a lot like (ZonedDateTime/parse x)

lambder18:02:23

but instead if being invoked is it passed a a value. How did it happen ?

lambder19:02:59

funny thing the value to decoder is coercion.malli is already wrapped with something like this:

lambder19:02:50

this happens in reitit.coercion by transforming the request to the value

juhoteperi19:02:31

@lambder those decode/json transformations aren't used by coercion here (no coercion really needed for these values) when you are using EDN. As EDN can already represent types, the coercion doesn't do anything. If you have *data-reader* registered for the #time/zoned-date-time tag (or provided :readers option to muuntaja edn format) the value should be correct and the problem must be with the schema validation (the predicate).

lambder19:02:50

hey @juhoteperi no, I don't have the readers supplied to muuntaja sepcially

lambder19:02:56

I got `

:muuntaja   formats/instance

lambder19:02:27

that instance is:

lambder19:02:29

(def instance
  (m/create
    (-> m/default-options
      (update-in
        [:formats "application/transit+json" :decoder-opts]
        (partial merge time/time-deserialization-handlers))
      (update-in
        [:formats "application/transit+json" :encoder-opts]
        (partial merge time/time-serialization-handlers)))))

juhoteperi19:02:33

Muuntaja will use *data-readers*, which is already provided by Clojure if you have the readers in a data_readers.clj file somewhere

lambder19:02:27

I do in my core ns `

(time-literals.read-write/print-time-literals-clj!)

juhoteperi19:02:31

But hmm, it could be problem with those data readers, because :value (. java.time.ZonedDateTime parse "2022-01-01T16:14:34.447Z") seems really strange. Looks like the value isn't the ZonedDateTime instance, but the Clojure form calling that ZonedDateTime/parse method

lambder19:02:39

but not setting it for read

lambder19:02:57

will get back to it in about 1h30m

lambder19:02:01

need to run now

juhoteperi19:02:06

> The library includes the magic file data_readers.cljc which Clojure and the Clojurescript compilers() will look for.

lambder19:02:06

thanks for everything

juhoteperi19:02:36

So just including time-literals in the classpath should add data-readers for those tags

juhoteperi19:02:26

If data-readers are working correctly, if you eval in the REPL {:foo #time/zoned-date-time "2022-01-01T16:14:34.447Z"} you should get the same map back, the tagged value is read to ZonedDateTime instance, and because you have the print method registered, it should be printed out the same way.

lambder19:02:30

In need to add time readers to end/read-string option for it to work

lambder19:02:09

So I guess they are not added to data_readers, but I'll dbl check

lambder23:02:42

@juhoteperi this is just black magic to me