3

I would like to be able to generate user-friendly or specify custom error messages for validation errors in these schemas:

(def Uuid (s/constrained String #(re-matches #"^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" (name %))))
(def FirstName s/Str)
(def LastName s/Str)

(s/defschema Person {(s/required-key :id)         Uuid,
                     (s/required-key :first-name) FirstName,
                     (s/required-key :last-name)  LastName})

Valid schema:

{
 :uuid "e143499c-1257-41e4-b951-c9e586994ff9" 
 :first-name "john" 
 :last-name "smith"
}

Invalid schema:

{
 :uuid "" 
 :first-name nil 
 :last-name nil
}

Invalid schema - Errors:

{
 "id" : "(not (app.person/fn--4881 \"\"))",
 "first-name" : "(not (instance? java.lang.String nil))"
 "last-name" : "(not (instance? java.lang.String nil))"
}

I would like to be able to generate something a bit more readable to non-programmers, for example:

{
 "id" : "invalid uuid",
 "first-name" : "must be a string"
 "last-name" : "must be a string"
}
bfontaine
  • 18,169
  • 13
  • 73
  • 107
Freid001
  • 2,580
  • 3
  • 29
  • 60
  • There are libraries in this space: - https://github.com/cddr/integrity - https://github.com/siilisolutions/humanize – nha Mar 09 '18 at 17:17

1 Answers1

3

Funnily exactly this was released as a library a few days ago.

See:

https://github.com/siilisolutions/humanize

First you also need to tag your Uuid schema so you can match it later on:

;; Note the last param I added: 
(def Uuid (sc/constrained
            String
            #(re-matches #"^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
                         (name %))
            'UUID))

(require '[schema.core :as sc]
         '[humanize.schema :as hs])

(#'hs/explain (sc/check Person {:id "foo"
                                :first-name "foo"
                                :last-name 3})
  (fn [x]
    (clojure.core.match/match
      x
      ['not ['UUID xx]]
      (str xx " is not a valid UUID")

      :else x)))

Results in:

=> {:id "foo is not a valid UUID", :last-name "'3' is not a string but it should be."}

Note, it needed a little trick since hs/explain is private unfortunately.

ClojureMostly
  • 4,652
  • 2
  • 22
  • 24
  • I've tried using the integrity library before, but `lein uberjar` would not build my project with integrity as a dependancy for some reason. However, give the commit was three years ago and the build is failing I kind of gave up trying to use integrity. So it's great to see that there is another library out there. – Freid001 Mar 12 '18 at 12:00
  • Your solution is almost what I am looking for. However, as you have eluded to it is not very elegant given `hs/explain` is private. Therefore, is there any other alternative? I tried to use `sh/ex->err` but it keeps returning nil or something like this: `{:unknown {:type :schema.core/error, :schema {:id (constrained Str UUID)}, :value {:id nil}, :error {:id (not (instance? java.lang.String nil))}}}` – Freid001 Mar 12 '18 at 12:03
  • You can also just use the gist: https://gist.github.com/rauhs/cfdb55a8314e0d3f4862 – ClojureMostly Mar 12 '18 at 12:35
  • 1
    @Freid001 & @ClojureMostly Oh wow, did not expect to see this here - humanize wraps originally André Rauh's schema translator into a small library. If you have any issues with the library, please do file an issue - I actually do have some contractual freedom to work on this library on my work time as we use it internally as well :) Also `explain` is no longer private for this very reason, I was just anxious originally to expose to much of the internals. – Esko Oct 13 '18 at 15:26