diff --git a/common/src/app/common/i18n.cljc b/common/src/app/common/i18n.cljc index bdd80b9741..f363329f2d 100644 --- a/common/src/app/common/i18n.cljc +++ b/common/src/app/common/i18n.cljc @@ -13,3 +13,10 @@ unit tests or backend code for logs or error messages." [key & _args] 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) diff --git a/common/src/app/common/schema/messages.cljc b/common/src/app/common/schema/messages.cljc new file mode 100644 index 0000000000..93903c1b9c --- /dev/null +++ b/common/src/app/common/schema/messages.cljc @@ -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)))) diff --git a/frontend/src/app/plugins/utils.cljs b/frontend/src/app/plugins/utils.cljs index cbfc512323..19de73fcde 100644 --- a/frontend/src/app/plugins/utils.cljs +++ b/frontend/src/app/plugins/utils.cljs @@ -9,14 +9,17 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] + [app.common.i18n :as i18n :refer [tr]] [app.common.schema :as sm] + [app.common.schema.messages :as csm] [app.common.types.component :as ctk] [app.common.types.container :as ctn] [app.common.types.file :as ctf] [app.common.types.tokens-lib :as ctob] [app.main.data.helpers :as dsh] [app.main.store :as st] - [app.util.object :as obj])) + [app.util.object :as obj] + [cuerdas.core :as str])) (defn locate-file [id] @@ -262,6 +265,15 @@ (let [s (set values)] (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 "Function to be used in plugin proxies methods to handle errors and print a readable message to the console." @@ -269,7 +281,9 @@ (fn [cause] (let [message (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))] (js/console.log (.-stack cause)) (not-valid plugin-id :error message)))) diff --git a/frontend/src/app/util/forms.cljs b/frontend/src/app/util/forms.cljs index 253d32470c..f0e7e5466c 100644 --- a/frontend/src/app/util/forms.cljs +++ b/frontend/src/app/util/forms.cljs @@ -10,84 +10,10 @@ [app.common.data :as d] [app.common.data.macros :as dm] [app.common.schema :as sm] - [app.util.i18n :as i18n :refer [tr]] + [app.common.schema.messages :as csm] [cuerdas.core :as str] - [malli.core :as m] [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 [] (let [state (mf/useState 0) @@ -97,24 +23,6 @@ (fn [] (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 [f {:keys [schema validators]}] (fn [& args] @@ -124,7 +32,7 @@ errors (when-not valid? - (collect-schema-errors schema validators state)) + (csm/collect-schema-errors schema validators state)) extra-errors (not-empty (:extra-errors state))] @@ -136,7 +44,6 @@ (not extra-errors) valid?))))) - (defn- make-initial-state [initial-data] (let [initial (if (fn? initial-data) (initial-data) initial-data) diff --git a/frontend/src/app/util/i18n.cljs b/frontend/src/app/util/i18n.cljs index 2f93c48129..93282d9c56 100644 --- a/frontend/src/app/util/i18n.cljs +++ b/frontend/src/app/util/i18n.cljs @@ -216,3 +216,4 @@ ;; We set the real translation function in the common i18n namespace, ;; so that when common code calls (tr ...) it uses this function. (set! app.common.i18n/tr tr) +(set! app.common.i18n/c c) diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 29efc62a11..6516514d57 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -8931,3 +8931,6 @@ msgstr "Troubleshooting guide" #, unused msgid "workspace.viewport.click-to-close-path" msgstr "Click to close the path" + +msgid "plugins.validation.message" +msgstr "Field %s is invalid: %s"