mirror of
https://github.com/penpot/penpot.git
synced 2026-08-01 19:06:18 +00:00
✨ Improve message from schema errors in plugins (#8865)
This commit is contained in:
parent
e8e7900911
commit
6ce2aadfae
@ -13,3 +13,10 @@
|
|||||||
unit tests or backend code for logs or error messages."
|
unit tests or backend code for logs or error messages."
|
||||||
[key & _args]
|
[key & _args]
|
||||||
key)
|
key)
|
||||||
|
|
||||||
|
(defn c
|
||||||
|
"This function will be monkeypatched at runtime with the real function in frontend i18n.
|
||||||
|
Here it just returns the key passed as argument. This way the result can be used in
|
||||||
|
unit tests or backend code for logs or error messages."
|
||||||
|
[x]
|
||||||
|
x)
|
||||||
|
|||||||
105
common/src/app/common/schema/messages.cljc
Normal file
105
common/src/app/common/schema/messages.cljc
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
|
(ns app.common.schema.messages
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.common.i18n :as i18n :refer [tr]]
|
||||||
|
[app.common.schema :as sm]
|
||||||
|
[malli.core :as m]))
|
||||||
|
|
||||||
|
;; --- Handlers Helpers
|
||||||
|
|
||||||
|
(defn- translate-code
|
||||||
|
[code]
|
||||||
|
(if (vector? code)
|
||||||
|
(tr (nth code 0) (i18n/c (nth code 1)))
|
||||||
|
(tr code)))
|
||||||
|
|
||||||
|
(defn- handle-error-fn
|
||||||
|
[props problem]
|
||||||
|
(let [v-fn (:error/fn props)
|
||||||
|
result (v-fn problem)]
|
||||||
|
(if (string? result)
|
||||||
|
{:message result}
|
||||||
|
{:message (or (some-> (get result :code)
|
||||||
|
(translate-code))
|
||||||
|
(get result :message)
|
||||||
|
(tr "errors.invalid-data"))})))
|
||||||
|
|
||||||
|
(defn- handle-error-message
|
||||||
|
[props]
|
||||||
|
{:message (get props :error/message)})
|
||||||
|
|
||||||
|
(defn- handle-error-code
|
||||||
|
[props]
|
||||||
|
(let [code (get props :error/code)]
|
||||||
|
{:message (translate-code code)}))
|
||||||
|
|
||||||
|
(defn interpret-schema-problem
|
||||||
|
[acc {:keys [schema in value type] :as problem}]
|
||||||
|
(let [props (m/properties schema)
|
||||||
|
tprops (m/type-properties schema)
|
||||||
|
field (or (:error/field props)
|
||||||
|
in)
|
||||||
|
field (if (vector? field)
|
||||||
|
field
|
||||||
|
[field])]
|
||||||
|
|
||||||
|
(if (and (= 1 (count field))
|
||||||
|
(contains? acc (first field)))
|
||||||
|
acc
|
||||||
|
(cond
|
||||||
|
(or (nil? field)
|
||||||
|
(empty? field))
|
||||||
|
acc
|
||||||
|
|
||||||
|
(or (= type :malli.core/missing-key)
|
||||||
|
(nil? value))
|
||||||
|
(assoc-in acc field {:message (tr "errors.field-missing")})
|
||||||
|
|
||||||
|
;; --- CHECK on schema props
|
||||||
|
(contains? props :error/fn)
|
||||||
|
(assoc-in acc field (handle-error-fn props problem))
|
||||||
|
|
||||||
|
(contains? props :error/message)
|
||||||
|
(assoc-in acc field (handle-error-message props))
|
||||||
|
|
||||||
|
(contains? props :error/code)
|
||||||
|
(assoc-in acc field (handle-error-code props))
|
||||||
|
|
||||||
|
;; --- CHECK on type props
|
||||||
|
(contains? tprops :error/fn)
|
||||||
|
(assoc-in acc field (handle-error-fn tprops problem))
|
||||||
|
|
||||||
|
(contains? tprops :error/message)
|
||||||
|
(assoc-in acc field (handle-error-message tprops))
|
||||||
|
|
||||||
|
(contains? tprops :error/code)
|
||||||
|
(assoc-in acc field (handle-error-code tprops))
|
||||||
|
|
||||||
|
:else
|
||||||
|
(assoc-in acc field {:message (tr "errors.invalid-data")})))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defn- apply-validators
|
||||||
|
[validators state errors]
|
||||||
|
(reduce (fn [errors validator-fn]
|
||||||
|
(merge errors (validator-fn errors (:data state))))
|
||||||
|
errors
|
||||||
|
validators))
|
||||||
|
|
||||||
|
(defn collect-schema-errors
|
||||||
|
[schema validators state]
|
||||||
|
(let [explain (sm/explain schema (:data state))
|
||||||
|
errors (->> (reduce interpret-schema-problem {} (:errors explain))
|
||||||
|
(apply-validators validators state))]
|
||||||
|
|
||||||
|
(-> (:errors state)
|
||||||
|
(merge errors)
|
||||||
|
(d/without-nils)
|
||||||
|
(not-empty))))
|
||||||
@ -9,14 +9,17 @@
|
|||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
|
[app.common.i18n :as i18n :refer [tr]]
|
||||||
[app.common.schema :as sm]
|
[app.common.schema :as sm]
|
||||||
|
[app.common.schema.messages :as csm]
|
||||||
[app.common.types.component :as ctk]
|
[app.common.types.component :as ctk]
|
||||||
[app.common.types.container :as ctn]
|
[app.common.types.container :as ctn]
|
||||||
[app.common.types.file :as ctf]
|
[app.common.types.file :as ctf]
|
||||||
[app.common.types.tokens-lib :as ctob]
|
[app.common.types.tokens-lib :as ctob]
|
||||||
[app.main.data.helpers :as dsh]
|
[app.main.data.helpers :as dsh]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.util.object :as obj]))
|
[app.util.object :as obj]
|
||||||
|
[cuerdas.core :as str]))
|
||||||
|
|
||||||
(defn locate-file
|
(defn locate-file
|
||||||
[id]
|
[id]
|
||||||
@ -262,6 +265,15 @@
|
|||||||
(let [s (set values)]
|
(let [s (set values)]
|
||||||
(if (= (count s) 1) (first s) "mixed")))
|
(if (= (count s) 1) (first s) "mixed")))
|
||||||
|
|
||||||
|
(defn error-messages
|
||||||
|
[explain]
|
||||||
|
(->> (:errors explain)
|
||||||
|
(reduce csm/interpret-schema-problem {})
|
||||||
|
(mapcat (comp seq val))
|
||||||
|
(map (fn [[field {:keys [message]}]]
|
||||||
|
(tr "plugins.validation.message" (name field) message)))
|
||||||
|
(str/join ". ")))
|
||||||
|
|
||||||
(defn handle-error
|
(defn handle-error
|
||||||
"Function to be used in plugin proxies methods to handle errors and print a readable
|
"Function to be used in plugin proxies methods to handle errors and print a readable
|
||||||
message to the console."
|
message to the console."
|
||||||
@ -269,7 +281,9 @@
|
|||||||
(fn [cause]
|
(fn [cause]
|
||||||
(let [message
|
(let [message
|
||||||
(if-let [explain (-> cause ex-data ::sm/explain)]
|
(if-let [explain (-> cause ex-data ::sm/explain)]
|
||||||
(sm/humanize-explain explain)
|
(do
|
||||||
|
(js/console.error (sm/humanize-explain explain))
|
||||||
|
(error-messages explain))
|
||||||
(ex-data cause))]
|
(ex-data cause))]
|
||||||
(js/console.log (.-stack cause))
|
(js/console.log (.-stack cause))
|
||||||
(not-valid plugin-id :error message))))
|
(not-valid plugin-id :error message))))
|
||||||
|
|||||||
@ -10,84 +10,10 @@
|
|||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.schema :as sm]
|
[app.common.schema :as sm]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.common.schema.messages :as csm]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[malli.core :as m]
|
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
;; --- Handlers Helpers
|
|
||||||
|
|
||||||
(defn- translate-code
|
|
||||||
[code]
|
|
||||||
(if (vector? code)
|
|
||||||
(tr (nth code 0) (i18n/c (nth code 1)))
|
|
||||||
(tr code)))
|
|
||||||
|
|
||||||
(defn- handle-error-fn
|
|
||||||
[props problem]
|
|
||||||
(let [v-fn (:error/fn props)
|
|
||||||
result (v-fn problem)]
|
|
||||||
(if (string? result)
|
|
||||||
{:message result}
|
|
||||||
{:message (or (some-> (get result :code)
|
|
||||||
(translate-code))
|
|
||||||
(get result :message)
|
|
||||||
(tr "errors.invalid-data"))})))
|
|
||||||
|
|
||||||
(defn- handle-error-message
|
|
||||||
[props]
|
|
||||||
{:message (get props :error/message)})
|
|
||||||
|
|
||||||
(defn- handle-error-code
|
|
||||||
[props]
|
|
||||||
(let [code (get props :error/code)]
|
|
||||||
{:message (translate-code code)}))
|
|
||||||
|
|
||||||
(defn- interpret-schema-problem
|
|
||||||
[acc {:keys [schema in value type] :as problem}]
|
|
||||||
(let [props (m/properties schema)
|
|
||||||
tprops (m/type-properties schema)
|
|
||||||
field (or (:error/field props)
|
|
||||||
in)
|
|
||||||
field (if (vector? field)
|
|
||||||
field
|
|
||||||
[field])]
|
|
||||||
|
|
||||||
(if (and (= 1 (count field))
|
|
||||||
(contains? acc (first field)))
|
|
||||||
acc
|
|
||||||
(cond
|
|
||||||
(or (nil? field)
|
|
||||||
(empty? field))
|
|
||||||
acc
|
|
||||||
|
|
||||||
(or (= type :malli.core/missing-key)
|
|
||||||
(nil? value))
|
|
||||||
(assoc-in acc field {:message (tr "errors.field-missing")})
|
|
||||||
|
|
||||||
;; --- CHECK on schema props
|
|
||||||
(contains? props :error/fn)
|
|
||||||
(assoc-in acc field (handle-error-fn props problem))
|
|
||||||
|
|
||||||
(contains? props :error/message)
|
|
||||||
(assoc-in acc field (handle-error-message props))
|
|
||||||
|
|
||||||
(contains? props :error/code)
|
|
||||||
(assoc-in acc field (handle-error-code props))
|
|
||||||
|
|
||||||
;; --- CHECK on type props
|
|
||||||
(contains? tprops :error/fn)
|
|
||||||
(assoc-in acc field (handle-error-fn tprops problem))
|
|
||||||
|
|
||||||
(contains? tprops :error/message)
|
|
||||||
(assoc-in acc field (handle-error-message tprops))
|
|
||||||
|
|
||||||
(contains? tprops :error/code)
|
|
||||||
(assoc-in acc field (handle-error-code tprops))
|
|
||||||
|
|
||||||
:else
|
|
||||||
(assoc-in acc field {:message (tr "errors.invalid-data")})))))
|
|
||||||
|
|
||||||
(defn- use-rerender-fn
|
(defn- use-rerender-fn
|
||||||
[]
|
[]
|
||||||
(let [state (mf/useState 0)
|
(let [state (mf/useState 0)
|
||||||
@ -97,24 +23,6 @@
|
|||||||
(fn []
|
(fn []
|
||||||
(render-fn inc)))))
|
(render-fn inc)))))
|
||||||
|
|
||||||
(defn- apply-validators
|
|
||||||
[validators state errors]
|
|
||||||
(reduce (fn [errors validator-fn]
|
|
||||||
(merge errors (validator-fn errors (:data state))))
|
|
||||||
errors
|
|
||||||
validators))
|
|
||||||
|
|
||||||
(defn- collect-schema-errors
|
|
||||||
[schema validators state]
|
|
||||||
(let [explain (sm/explain schema (:data state))
|
|
||||||
errors (->> (reduce interpret-schema-problem {} (:errors explain))
|
|
||||||
(apply-validators validators state))]
|
|
||||||
|
|
||||||
(-> (:errors state)
|
|
||||||
(merge errors)
|
|
||||||
(d/without-nils)
|
|
||||||
(not-empty))))
|
|
||||||
|
|
||||||
(defn- wrap-update-schema-fn
|
(defn- wrap-update-schema-fn
|
||||||
[f {:keys [schema validators]}]
|
[f {:keys [schema validators]}]
|
||||||
(fn [& args]
|
(fn [& args]
|
||||||
@ -124,7 +32,7 @@
|
|||||||
|
|
||||||
errors
|
errors
|
||||||
(when-not valid?
|
(when-not valid?
|
||||||
(collect-schema-errors schema validators state))
|
(csm/collect-schema-errors schema validators state))
|
||||||
|
|
||||||
extra-errors
|
extra-errors
|
||||||
(not-empty (:extra-errors state))]
|
(not-empty (:extra-errors state))]
|
||||||
@ -136,7 +44,6 @@
|
|||||||
(not extra-errors)
|
(not extra-errors)
|
||||||
valid?)))))
|
valid?)))))
|
||||||
|
|
||||||
|
|
||||||
(defn- make-initial-state
|
(defn- make-initial-state
|
||||||
[initial-data]
|
[initial-data]
|
||||||
(let [initial (if (fn? initial-data) (initial-data) initial-data)
|
(let [initial (if (fn? initial-data) (initial-data) initial-data)
|
||||||
|
|||||||
@ -216,3 +216,4 @@
|
|||||||
;; We set the real translation function in the common i18n namespace,
|
;; We set the real translation function in the common i18n namespace,
|
||||||
;; so that when common code calls (tr ...) it uses this function.
|
;; so that when common code calls (tr ...) it uses this function.
|
||||||
(set! app.common.i18n/tr tr)
|
(set! app.common.i18n/tr tr)
|
||||||
|
(set! app.common.i18n/c c)
|
||||||
|
|||||||
@ -8931,3 +8931,6 @@ msgstr "Troubleshooting guide"
|
|||||||
#, unused
|
#, unused
|
||||||
msgid "workspace.viewport.click-to-close-path"
|
msgid "workspace.viewport.click-to-close-path"
|
||||||
msgstr "Click to close the path"
|
msgstr "Click to close the path"
|
||||||
|
|
||||||
|
msgid "plugins.validation.message"
|
||||||
|
msgstr "Field %s is invalid: %s"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user