malli 2024-09-20

Ok, I'm trying to start using Malli and I'm trying to define my first, simple schema. It's a hexagon map storing a cubic address. Cubic addresses are integers (p,q,r), such that -p-q = r .

How do I capture that last requirement?

[:and [:map [:p :int] [:q :int] [:r :int]]
      [:fn #(= (:r %) (- (+ (:p %) (:q %)))))]]

or something like that, I didn't actually run that šŸ˜‡

Lol, I suspect that's close enough that I can repl my way to the correct answer.

That was correct, except it has an extra parens. Thank you!

I haven't yet had a situation where I'm getting "invalid" hexes, so I don't think I need a schema for those, but it's the place where it's small enough that I can write the schema and understand all the interaction points (I think)

There's something I don't understand in https://github.com/metosin/reitit/blob/master/doc/ring/coercion.md#ring-coercion This works fine

(app {:request-method :post
      :uri "/api/plus/3"
      :query-params {"x" "1"}
      :body-params {:y 2}})
; {:status 200, :body {:total 6}}
So does this (Changing the value for x)
(app {:request-method :post
      :uri "/api/plus/3"
      :query-params {"x" 1}
      :body-params {:y 2}})
; {:status 200, :body {:total 6}}
But :body-params throws an error and requests the value to be an Integer, does this not defeat the purpose of coercion where the request can be strings? or am I missing something?
(app {:request-method :post
      :uri "/api/plus/3"
      :query-params {"x" "1"}
      :body-params {:y "2"}})
;; => {:status 400,
;;     :body
;;     {:schema {:y "Int"},
;;      :errors {:y "(not (integer? \"2\"))"},
;;      :type :reitit.coercion/request-coercion,
;;      :coercion :schema,
;;      :value {:y "2"},
;;      :in [:request :body-params]}}

Reitit uses different transformers for query and body parameters. This is because query parameters are always strings (they're part of the URL!), but body parameters (when represented as JSON, EDN or Transit) have separate integer and string types.

You should be able to configure the transformers if you want to coerce strings to ints in your request bodies.

You can do this with something like..

:coercion (reitit.coercion.malli/create (assoc reitit.coercion.malli/default-options :transformers {:default reitit.coercion.malli/string-transformer-provider}))

or if you only want this behaviour for some keys in your bodies, you can use malli properties

[:map
 [:y [:int {:decode/json mt/-string->long}]]]

I’m having a hard time finding this so far. Is there any support in malli to ā€œunstrumentā€ function calls just during the callstack? Something like with-redefs behavior - I’m expecting that this can’t be isolated to a single thread due to the registry not being thread-bound etc probably. But even if it affects all thread, I just was curious if there was something that can ā€œunstrumentā€ during the call then put it back after. Basically, the same idea as from plumatic schema lib’s schema.core/without-fn-validation https://github.com/plumatic/schema/blob/8fcb57f87ffe74185e1f1da72be4933bd4aad4d2/src/cljc/schema/core.cljc#L1234

I don't think so, but {in,un}strument is idempotent so you can try something like this:

(defn without-fn-validation* [f]
  (let [vs (unstrument)]
    (try (f)
         (finally (instrument {:data (select-keys (m/function-schemas) (map symbol vs))})

Thanks, thought I may just try doing something like that. I have a dev flow where we want better perf and schema are causing it to slow down too much. I want schema instrumentation generally on for most things (even for the same functions), just not in this particular fn call event.

I did similar back when I used schema more as well and would occasionally do the with/without fn validation stuff.