Add minor improvements to common.schema ns

This commit is contained in:
Andrey Antukh 2024-06-20 14:35:55 +02:00
parent 7be79c10fd
commit 0fa8aca6e2
2 changed files with 97 additions and 19 deletions

View file

@ -21,6 +21,7 @@
[cuerdas.core :as str] [cuerdas.core :as str]
[malli.core :as m] [malli.core :as m]
[malli.dev.pretty :as mdp] [malli.dev.pretty :as mdp]
[malli.dev.virhe :as v]
[malli.error :as me] [malli.error :as me]
[malli.generator :as mg] [malli.generator :as mg]
[malli.registry :as mr] [malli.registry :as mr]
@ -104,6 +105,10 @@
[exp] [exp]
(malli.error/error-value exp {:malli.error/mask-valid-values '...})) (malli.error/error-value exp {:malli.error/mask-valid-values '...}))
(defn optional-keys
[schema]
(mu/optional-keys schema default-options))
(def default-transformer (def default-transformer
(let [default-decoder (let [default-decoder
{:compile (fn [s _registry] {:compile (fn [s _registry]
@ -190,9 +195,10 @@
(fn [v] (@vfn v)))) (fn [v] (@vfn v))))
(defn lazy-decoder (defn lazy-decoder
[s transformer] ([s] (lazy-decoder s default-transformer))
([s transformer]
(let [vfn (delay (decoder (if (delay? s) (deref s) s) transformer))] (let [vfn (delay (decoder (if (delay? s) (deref s) s) transformer))]
(fn [v] (@vfn v)))) (fn [v] (@vfn v)))))
(defn humanize-explain (defn humanize-explain
[{:keys [schema errors value]} & {:keys [length level]}] [{:keys [schema errors value]} & {:keys [length level]}]
@ -207,9 +213,29 @@
:level (d/nilv level 8) :level (d/nilv level 8)
:length (d/nilv length 12)}))))) :length (d/nilv length 12)})))))
(defmethod v/-format ::schemaless-explain
[_ {:keys [schema] :as explanation} printer]
{:body [:group
(v/-block "Value" (v/-visit (me/error-value explanation printer) printer) printer) :break :break
(v/-block "Errors" (v/-visit (me/humanize (me/with-spell-checking explanation)) printer) printer) :break :break
(v/-block "Schema" (v/-visit schema printer) printer)]})
(defmethod v/-format ::explain
[_ {:keys [schema] :as explanation} printer]
{:body [:group
(v/-block "Value" (v/-visit (me/error-value explanation printer) printer) printer) :break :break
(v/-block "Errors" (v/-visit (me/humanize (me/with-spell-checking explanation)) printer) printer) :break :break
(v/-block "Schema" (v/-visit schema printer) printer)]})
(defn pretty-explain (defn pretty-explain
[s d] [explain & {:keys [variant message]
(mdp/explain (schema s) d)) :or {variant ::explain
message "Validation Error"}}]
(let [explain (fn [] (me/with-error-messages explain))]
((mdp/prettifier variant message explain default-options))))
(defmacro ignoring (defmacro ignoring
[expr] [expr]
@ -297,7 +323,7 @@
(throw (ex-info hint options)))))) (throw (ex-info hint options))))))
(defn validate-fn (defn validate-fn
"Create a predefined validate function" "Create a predefined validate function that raises an expception"
[s] [s]
(let [schema (if (lazy-schema? s) s (define s))] (let [schema (if (lazy-schema? s) s (define s))]
(partial fast-validate! schema))) (partial fast-validate! schema)))
@ -317,6 +343,7 @@
hint (get options :hint "schema validation error")] hint (get options :hint "schema validation error")]
(throw (ex-info hint options))))))) (throw (ex-info hint options)))))))
;; FIXME: revisit
(defn conform! (defn conform!
[schema value] [schema value]
(assert (lazy-schema? schema) "expected `schema` to satisfy ILazySchema protocol") (assert (lazy-schema? schema) "expected `schema` to satisfy ILazySchema protocol")
@ -476,11 +503,14 @@
(define! ::set (define! ::set
{:type :set {:type :set
:min 0
:max 1
:compile :compile
(fn [{:keys [coerce kind max min] :as props} _ _] (fn [{:keys [coerce kind max min] :as props} children _]
(let [xform (if coerce (let [xform (if coerce
(comp non-empty-strings-xf (map coerce)) (comp non-empty-strings-xf (map coerce))
non-empty-strings-xf) non-empty-strings-xf)
kind (or (last children) kind)
pred (cond pred (cond
(fn? kind) kind (fn? kind) kind
(nil? kind) any? (nil? kind) any?
@ -509,7 +539,8 @@
(every? pred value)))) (every? pred value))))
:else :else
pred)] (fn [value]
(every? pred value)))]
{:pred pred {:pred pred
:type-properties :type-properties
@ -525,6 +556,64 @@
(let [v (if (string? v) (str/split v #"[\s,]+") v)] (let [v (if (string? v) (str/split v #"[\s,]+") v)]
(into #{} xform v)))}}))}) (into #{} xform v)))}}))})
(define! ::vec
{:type :vector
:min 0
:max 1
:compile
(fn [{:keys [coerce kind max min] :as props} children _]
(let [xform (if coerce
(comp non-empty-strings-xf (map coerce))
non-empty-strings-xf)
kind (or (last children) kind)
pred (cond
(fn? kind) kind
(nil? kind) any?
:else (validator kind))
pred (cond
(and max min)
(fn [value]
(let [size (count value)]
(and (set? value)
(<= min size max)
(every? pred value))))
min
(fn [value]
(let [size (count value)]
(and (set? value)
(<= min size)
(every? pred value))))
max
(fn [value]
(let [size (count value)]
(and (set? value)
(<= size max)
(every? pred value))))
:else
(fn [value]
(every? pred value)))]
{:pred pred
:type-properties
{:title "set"
:description "Set of Strings"
:error/message "should be a set of strings"
:gen/gen (-> kind sg/generator sg/set)
::oapi/type "array"
::oapi/format "set"
::oapi/items {:type "string"}
::oapi/unique-items true
::oapi/decode (fn [v]
(let [v (if (string? v) (str/split v #"[\s,]+") v)]
(into [] xform v)))}}))})
(define! ::set-of-strings (define! ::set-of-strings
{:type ::set-of-strings {:type ::set-of-strings
:pred #(and (set? %) (every? string? %)) :pred #(and (set? %) (every? string? %))

View file

@ -9,7 +9,6 @@
(:require (:require
[app.common.logging :as log] [app.common.logging :as log]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.spec :as us]
[app.config :as cf] [app.config :as cf]
[app.main.data.messages :as msg] [app.main.data.messages :as msg]
[app.main.data.users :as du] [app.main.data.users :as du]
@ -25,7 +24,6 @@
[app.util.keyboard :as k] [app.util.keyboard :as k]
[app.util.router :as rt] [app.util.router :as rt]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[cljs.spec.alpha :as s]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(def show-alt-login-buttons? (def show-alt-login-buttons?
@ -64,14 +62,6 @@
:else :else
(st/emit! (msg/error (tr "errors.generic")))))))) (st/emit! (msg/error (tr "errors.generic"))))))))
(s/def ::email ::us/email)
(s/def ::password ::us/not-empty-string)
(s/def ::invitation-token ::us/not-empty-string)
(s/def ::login-form
(s/keys :req-un [::email ::password]
:opt-un [::invitation-token]))
(def ^:private schema:login-form (def ^:private schema:login-form
[:map {:title "LoginForm"} [:map {:title "LoginForm"}
[:email [::sm/email {:error/code "errors.invalid-email"}]] [:email [::sm/email {:error/code "errors.invalid-email"}]]
@ -84,7 +74,6 @@
(let [initial (mf/with-memo [params] params) (let [initial (mf/with-memo [params] params)
error (mf/use-state false) error (mf/use-state false)
form (fm/use-form :schema schema:login-form form (fm/use-form :schema schema:login-form
;; :validators [handle-error-messages]
:initial initial) :initial initial)
on-error on-error