This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-12-15
Channels
- # adventofcode (80)
- # beginners (94)
- # biff (19)
- # cider (74)
- # clj-kondo (11)
- # cljs-dev (7)
- # clojure (110)
- # clojure-austin (3)
- # clojure-australia (1)
- # clojure-belgium (1)
- # clojure-china (1)
- # clojure-europe (83)
- # clojure-filipino (1)
- # clojure-hk (1)
- # clojure-indonesia (1)
- # clojure-japan (1)
- # clojure-korea (1)
- # clojure-my (1)
- # clojure-nl (1)
- # clojure-norway (4)
- # clojure-sg (1)
- # clojure-taiwan (1)
- # clojure-uk (2)
- # cursive (3)
- # data-science (8)
- # datalevin (8)
- # emacs (18)
- # etaoin (5)
- # graalvm (1)
- # holy-lambda (3)
- # honeysql (1)
- # jackdaw (10)
- # java (10)
- # jobs (3)
- # luminus (9)
- # malli (106)
- # off-topic (88)
- # polylith (8)
- # portal (2)
- # re-frame (50)
- # reagent (11)
- # reitit (74)
- # remote-jobs (1)
- # shadow-cljs (46)
- # tools-deps (26)
- # xtdb (49)
another quick question, similar to the last one, why?
(m/properties [:enum "x" "y" {:error/message "a"}]) ;; nil
(m/properties [:string {:error/message "a"}]) ;; #:error{:message "a"}
thanks a lot, yeah silly mistake... but more and more I get more confident I will be able to express any crazy schema + have validating/schema/errors in one shape, thanks for your help
I have a question.
So when i am using spec to describe paramenters
with reitit.ring.middleware.multipart/temp-file-part there is swagger/type which can be shown in the swagger page.
I can't seem to find a way to do this with malli (yes i swapped coercion)
I tried https://github.com/metosin/reitit/blob/master/modules/reitit-malli/src/reitit/ring/malli.cljc this breaks page so that you can't even put params anymore
:parameters {:query [:map {:json-schema {:type "file"}}
[:filename string?]
[:content-type string?]
[:size int?]
[:tempfile [:fn (partial instance? )]]]
this is the gist
Is this solvable or i am stuck with spec?
after some tryouts this worked
[:filename {:swagger/type "file"} string?]
but i am wondering am i making a mistake and this should be done some other way?
edit
https://github.com/metosin/reitit/blob/master/examples/ring-malli-swagger/src/example/server.clj
provided all answers i needed, didn't see it first time i checked examples 🙂
thankful and grateful for the examples 
I guess i didn't find answer to all questions ... So i have custom schema that uses custom function for special case validation.
[:map {:closed true}
[:match-id [:fn #:error{:message "Invalid match-id"} #function[valid-uuid-as-str?]]]
[:date [:fn #:error{:message "Invalid date"} #function[valid-date-as-str?]]]]
But it doesn't work.
I am already using this specialized validation in other places but i was thinking about reusing it all everywhere.
is it possible that reitit parameter validation can be only primitive predicates (from clj.core and malli) ?there are no restrictions on reitit for using malli, it should just work. let me test that.
@ikitommi Thanks! Here is the repo (just reused existing mali reitit example and added on top) https://github.com/StankovicMarko/reitit/blob/custom-validation/examples/ring-malli-swagger/src/example/server.clj You can see what i added on top: https://github.com/StankovicMarko/reitit/commit/8f6634d1f7529aebd23e6527ac738857894b0328 Main issue is that when i put values for GET plus, if they are wrong i can't make a request until i put correct value. picture A And for test route i can add whatever i want and i dont want that. I want to prevent it just like with 'native' types picture B
This was the only i managed to do it
[:map
[:date {:description "Date when match happened"
:json-schema/type "string"
:json-schema/format "date"
:json-schema/default "2022-10-10"}
[:and
[:re #"^\d{4}\-(0[1-9]|1[012])\-(0[1-9]|[12][0-9]|3[01])$"]
[:fn {:error/message "Invalid date"}
v/valid-date-as-str?]]]]
Use regex to enforce format of a string but feels wrong.
shouldn't this already be available?
https://swagger.io/docs/specification/data-models/data-types/#stringHello, I'm in the process of porting over a codebase from spec to malli, and it's very cool! Thanks for all your work. I have a small question that may be a bit stupid so forgive my ignorance, is there a way to pick up the seed or size values from within a :gen/fmap
function?
What I have done, is use a deterministic transformation on the generated value in :gen/fmap
that sounds like it might be along the lines of what I'm looking for, I don't suppose you have a code snippet at all?
(mg/generate
[:and {:gen/fmap #(str "my_" % "_thing")} string?]
{:seed 10, :size 10})
ah I see, I did try something like that at first but it didn't really work for my situation, let me try and write some pseudocode for what I have
(malli/schema [:fn
{:gen/fmap
(fn [_]
(generate-thing {:seed ???}))}
(fn [thing] (validate-thing thing))])
which I'm then calling via (mg/generate schemas/thing {:seed 0})
so I'm not sure that would work here, does that make a bit more sense?
potentially yes, but this isn't actually in test code, so I'd really only rather use clojure.test.check.generators as a last resort since that would mean putting it in the src imports which, feels a bit funky
if it's not a common thing what I'm doing though I guess I'd have to go down that route
not hard but it does take an int seed
and I'm not sure how to pass that through
since otherwise if I call it without the seed in tests, well, it's random 😅
that's a good point, I guess I'm looking for an easy way out as we already have (generate-thing)
aha I wasn't sure if that would work, as if I use that to validate as well will it try and think the input should also be an int?
(mg/generate
[:and {:gen/fmap #(generate-thing %)}
[:or int? [:fn ..check for mything..]]
{:seed 10})
ahh that's interesting
yes very weird, 😅 I guess that's how my brain went to trying to get a seed value in at first
actually.. in:
(malli/schema [:fn
{:gen/fmap
(fn [_]
(generate-thing {:seed ???}))}
(fn [thing] (validate-thing thing))])
ah nothing, but it does work, I ripped it from the malli test code
this is the relevant example:
(is (= 42 (mg/generate [:re
{:gen/fmap (fn [_] 42)
:gen/schema :int}
#"abc"]))
(https://github.com/metosin/malli/blob/master/test/malli/generator_test.cljc#L171)
thanks very much for your help btw, I'm about to finish today but you've given me some very interesting ideas
Nice! yeah I am starting on porting a giant codebase from spec and schema (with plenty of custom macros) to malli. Fun times!
I'm in somewhat of a similar situation, it's fun till it isn't has been my experience so far 😅
as an update on this, I ended up writing a custom generator using the clojure test check generators in the end, thanks again for all your help
I am unable to find a solution to what should be simple thing. How do i convert date string to LocalDateTime object automatically? (i am assuming this is request coercion) This is my muuntaja instance
(def muuntaja-instance
(m/create
(-> m/default-options
(assoc-in [:formats "application/json" :decoder-opts] {:decode-key-fn csk/->kebab-case-keyword
<<???>> #(java.time.LocalDate/parse %)})
(assoc-in [:formats "application/json" :encoder-opts] {:encode-key-fn csk/->camelCaseString
:date-format "yyyy-MM-dd"})
You have to define your own schema type for that, unfortunately, but it's pretty easy
yesterday I wrote an x/defn macro that behaves like Schema’s s/defn. (mx/defn does not throw with incorrect :in or :out.) Is there a library or project to mirror little utilities like that in Schema or Spec?
there is also https://github.com/CrypticButter/snoop
wanted to keep mx/defn
slim. thought of adding the Schema-style :always|never-validate
hints too.
I have a macro emitting the function with an extra {::validate! id} metadata and emitting an instrumentation filter after doing the core/defn call. so that’s a way to do the always-validate behavior
not sure how to guard it, maybe add something into mi/-strument where it filters out on a certain fxn metadata value
relevant bit of :always-validate https://github.com/metabase/metabase/pull/27218/files#diff-e29756aed1e2e2cbce8a26a8e4b2e1cbedfb83cd4d853cf6942455e49516e4d8R44
I assume the 1 arity for coercer decodes or throws, would be nice if we could have 3 arity for on success and on failure continuations, where the on-success takes the decoded value and on-failure takes the explained result
Looks good. Quick comments:
• cljs + bundle size. If you want to use malli.time
, it will brings the generators in, as multimethods don't get DCEd, it's a lot of extra. README defines how to check the bundle size reports. Option to have malli.time
and malli.time.generators
, which sucks too.
• If this is only for Clj now, then not a big issue. Could be then malli.experimental.time
- when temporal/cljs solution comes, it most likely changes a bit.
• Schema types could be namespaced with time
, e.g. :time/local-date
, thinking of doing same for malli.util schemas.
• cljs: not really sure how to approach that so I just punted and did clj only • will move to experimental for now • namespacing: I thought I did
I think it would be OK to merge and maybe even release clj-only version first
Though there is some danger in that case that something in the API will be hard to implement on Cljs side
I could explore using date-io as a facade to different JS date libs, so users can use whatever they want: https://github.com/dmtrKovalenko/date-io#projects
Personally I’m a bit concerned about the longevity of date-io and would appreciate malli.time to be built upon something that will last over time. However I trust @U061V0GG2 ‘s opinion over mine 🙂
Yeah the issue about Temporal support on date-io shows the the API isn't very well designed
But building our own thing to to support different libs isn't much better either
Besides separating out generators code and providing acceptors for json schema, any other things to keep in mind?
Draft MR is ready, your feedback is appreciated https://github.com/metosin/malli/pull/802
@ikitommi I think it's ready for review, besides adding tests the design is pretty settled. Only thing I'm not sure about is the parsing code which exists in the core ns.
Before I build it myself — Is there a way to get a descirption of a schema in malli.core? e.g. [;map [:x int?]]
=> “A map containing: x, an integer”
If it did, Given a malli schema, it should return a string with a description of the shape it expects.
Are there problems with doing that? I figure a call to m/ast, then dispatching on :type should be sufficient. wdyt?
you should use m/walk
+ dispatch on type. Each Schema implements it's own -walk
so all the children are walked correctly, see https://github.com/metosin/malli/blob/master/src/malli/json_schema.cljc on how to do this.
Could you help me understand the benefits of using walk vs ast? Is walk more stable?
All schemas implement the m/-walk
. So you can walk over any schema, even those defined in user space. (m/walk schema (m/schema-walker identity))
works always. With AST, you have to know all schemas ahead of time to know how to handle those. In your use case, I guess you need to handle all schemas anyway, you can do the same with AST. Just that it breaks if someone changes the AST for a schema (each schema controls how it looks). There is a disclaimer in Malli README not to use the AST as a persistence model, could change, no no plans to break it and will avoid doing that. We are using AST walking too in projects. But, walk is always safe.
Here’s a self contained example. I used cond since its easier to fit. I’ll actually use a multimethod though
(mc/walk
[:map [:x :int] [:y :string]]
(fn [schema _path children _options]
(println "-------")
(println "schema:" schema)
(println "children:" children)
(cond
(= (mc/type schema) :int) "INTEGER"
(= (mc/type schema) :string) "STRING"
(= (mc/type schema) :map) (str "map of "
(str/join "" (mapv (fn [[k _ v]] (pr-str [k v]))
children)))
:else schema)))
I don’t think schema-walker is the way to go, since I dont want to ultimately return a schema
maybe I can attach the stringified explaination to the schema as a property, and look for the prop on children?
it's a postwalk, you get the transformed children to your callback, should be straightforward to implement what you want
Looks good. Quick comments:
• cljs + bundle size. If you want to use malli.time
, it will brings the generators in, as multimethods don't get DCEd, it's a lot of extra. README defines how to check the bundle size reports. Option to have malli.time
and malli.time.generators
, which sucks too.
• If this is only for Clj now, then not a big issue. Could be then malli.experimental.time
- when temporal/cljs solution comes, it most likely changes a bit.
• Schema types could be namespaced with time
, e.g. :time/local-date
, thinking of doing same for malli.util schemas.