Fork me on GitHub
#malli
<
2022-12-11
>
Erik Colson22:12:52

hi. I am trying out malli. What is the usual approach to validate a date string with malli?

Ben Sless03:12:32

Coerce it first to the expected date type using a decoder

dharrigan07:12:32

Here's a little example that I have used (there may be other approaches)

dharrigan07:12:01

(def ^:private iso8601-message "is not in ISO DATE TIME format, e.g. 2020-07-03T10:15:30+01:00 or 2020-07-03T10:15:30Z")

;; ISO_OFFSET_DATE_TIME format, e.g., 2011-12-03T10:15:30+01:00 or 2011-12-03T10:15:30Z
(defn ^:private date-time-parser
  [date-time]
  (try
    (.parse DateTimeFormatter/ISO_OFFSET_DATE_TIME date-time)
    (catch DateTimeParseException _)))

(def date-range [:map
                 [:from {:optional true} [:fn {:swagger/type "string" :swagger/format "date-time" :error/message iso8601-message} date-time-parser]]
                 [:to {:optional true} [:fn {:swagger/type "string" :swagger/format "date-time" :error/message iso8601-message} date-time-parser]]])

Ben Sless07:12:09

It's a bit draconian but I came to adopt the approach of never using a :fn schema if I can help it. Instead, all data coming into the process should go through a coercer. In reitit coercion is built in for you, otherwise just roll your own: https://clojurians.slack.com/archives/CLDK6MFMK/p1636145457067000?thread_ts=1636057867.062800&amp;cid=CLDK6MFMK Then just say your date is an inst?

eskos07:12:46

I used to do what @U11EL3P9U does and it works well enough IMO. While not immediately apparent, I also created https://github.com/esuomi/muotti/ partly because of the complexities related to this problem, and in fact its source has a tidbit which may also be inspiration, if nothing else: https://github.com/esuomi/muotti/blob/d6e4e3df5f70bf1d26d035b034d9306445aea423/src/main/clj/muotti/malli.clj#L181-L199

Erik Colson11:12:23

thanks for the examples. I am going to use @U11EL3P9U’s approach on this. @UK0810AQ2 what is the reason you prefer not to use :fn ?

Ben Sless11:12:55

• fn schemas are opaque • values can't be generated • they hide the real type you're interested in. For example, if you have a time stamp, you might want to compare it against things (such as other dates), meaning you'll have to parse it anyway. You want the real type on your hands • Schemas specify ought, not is. you use decoding to make the best effort to bring what is to ought, then validate. You can often find an actual type which is closer to what you want than a fn

Ben Sless11:12:38

I can give you a real example - I have code which takes date over as string and needs to calculate N days ago from it, an API which returns numbers in JSON as strings. I don't want to do manual parsing. Once everything is past the gate of coercion and validation, I want my data to be the types I actually use, not what some random stressed out developer at $COMPANY crammed over a poorly thought out API

Erik Colson15:12:44

@UK0810AQ2 That seems to make sense. Thanks for your explanation!