Fork me on GitHub
#malli
<
2023-05-05
>
adamfrey17:05:59

Is there a way to add a generator to an existing :malli.core/schema object stored in a var in another namespace?

escherize17:05:16

You could alter-var-root to the var, and use u/update-properties to assoc in the generator key of your choice

adamfrey17:05:06

m.u/update-properties is what I was loopking for, thank you

馃憤 2
escherize17:05:33

(mg/generate
  (mu/update-properties [:vector int?] assoc :gen/elements [[3] [4]]))

escherize17:05:50

gives [3] or [4]

DrLj贸tsson18:05:53

Is there a way to get pretty printed and concise error messages from failed (reitit) coercion and instrumentation? I have instrumented db queries and coerced api routes that return large amounts of data and it鈥檚 difficult to see what the exact offending data is from the exception. If it鈥檚 only 1 record it would be great to see only that record, and if it's hundreds, it would be nice to only see a few of them

escherize18:05:07

I would ask in #C7YF1SBT3 if you havn鈥檛 already.

DrLj贸tsson18:05:16

Thanks @U6N4HSMFW , I will look at that! @U051GFP2V , I was also wondering if one can get more legible exceptions from instrumented functions (not reitit-related)

valtteri19:05:51

There鈥檚 similar pretty reporter for malli. I don鈥檛 know how it reports instrumented functions but probably quickest way to find out is to try 馃槈

valtteri19:05:02

Also since it鈥檚 Tommi-code it鈥檚 probably extensible

escherize19:05:25

Oh, I know how that works actually, @UGDTSFM4M.

escherize19:05:36

There鈥檚 a wrapped humanize function you can pass.

escherize19:05:50

it鈥檚 mentioned on the tips and tricks page, iirc

DrLj贸tsson19:05:22

For instrumentation?

escherize19:05:49

yeah for any humanize call

escherize19:05:30

what you want to do is plug in your :report function when you call instrument! (or -instument)

escherize19:05:08

and then, you may want to call me/humanize, and plug in some kind of wrap function for it

DrLj贸tsson19:05:40

Oh, wow. This looks amazing. I need to wrap my head around how this can be pieced together but shouldn鈥檛 be that hard.

escherize19:05:16

let me know if u have any more questions

馃檹 1
Akiz10:05:44

I could use it too, I have a couple of :sequential schemes that return thousands of complex maps and overwhelm Emacs. Isn't there already a similar reporting function? Not to reinvent the wheel 馃槈

DrLj贸tsson12:05:20

I am working on a solution for instrumented functions. Happy to show my solution next week and get feedback!

DrLj贸tsson21:05:55

So this is what I managed to achieve. For coercion errors, I added the following custom exception handler to my reitit routes

{:reitit.coercion/response-coercion
     (fn [exception request]
       (log/error "Response coercion error"
                  (with-out-str
                    (pprint/pprint
                     (malli-error/humanize
                      (ex-data exception)
                      :request))))
       {:status 500
        :body {:message "Response coercion error"
               :uri (:uri request)}})
,,,}
This logs the humanized coercion error. Although it prints all failing records in a large dataset, they will probably all follow the same pattern and it's pretty easy to spot what the actual error is. For instrumentation I added this function as my :report option to instrument!
(defn instrument-error!
  [type data]
  (let [{:keys [input args output value arity]} data
        [cause humanized] (cond
                            input ["Invalid input" (malli-error/humanize (m/explain input args))]
                            output ["Invalid output" (malli-error/humanize (m/explain output value))]
                            arity ["Invalid arity" "Too few or too many args"])] 
    (throw (ex-info
            (str cause "\n" (with-out-str (pprint/pprint humanized)))
            {:type type :data data}))))
(I added arity errors for completeness but I guess that Clojure will catch arity errors) When developing in the REPL, I get the instrumentation error printed as the exception message, which is handy. I also added this to my reitit custom exception handling
(defn instrument-error
  [exception request]
  (log/error (ex-message exception))
  {:status 500
   :body {:message "Application error"
          :uri (:uri request)}})

;; Custom exception handling map
{,,,
 ::m/invalid-output instrument-error
 ::m/invalid-input instrument-error
,,,}
So I get instrumentation errors in a request logged. I am sure there are better ways to do this so any feedback is welcome. Also, it would be great if the instrumentation error message could include the name of the failing function. I can't locate it in the data received by the :report function but I guess that could be extracted from the stack trace? I have no idea how to do that though.

Akiz12:06:15

It looks good to me, I haven鈥檛 come up with anything better.

DrLj贸tsson13:06:27

Any idea on how to get the name of the instrumented function that failed?

Akiz07:06:08

It is part of the data, you should see it in the exception info

Akiz07:06:49

So something like this for example:

(defn instrument-error!
  [type data]
  (let [{:keys [input args output value arity]} data
        [cause humanized] (cond
                            input ["Invalid input" (malli-error/humanize (m/explain input args))]
                            output ["Invalid output" (malli-error/humanize (m/explain output value))]
                            arity ["Invalid arity" "Too few or too many args"])]
    (throw (ex-info
            (str cause " in " (:fn-name data) "\n" (with-out-str (pprint/pprint humanized)))
            {:type type :data data}))))