diff --git a/.github/workflows/commit_validation.yml b/.github/workflows/commit_validation.yml index d07d3a82..3f4be6e0 100644 --- a/.github/workflows/commit_validation.yml +++ b/.github/workflows/commit_validation.yml @@ -12,4 +12,4 @@ jobs: # this is to fix GIT not liking owner of the checkout dir, See https://github.com/actions/runner/issues/2033 chown -R $(id -u):$(id -g) $PWD - name: Base image - uses: docker://hephaistox/gha-automaton-core:1.2.2 + uses: docker://hephaistox/gha-automaton-core:1.2.3 diff --git a/bb.edn b/bb.edn index 39b33b58..8c3d41d6 100644 --- a/bb.edn +++ b/bb.edn @@ -1,5 +1,5 @@ ;; The file is updated automatically -{:deps {org.clojars.hephaistox/automaton-build #:mvn{:version "1.4.3"}} +{:deps {org.clojars.hephaistox/automaton-build #:mvn{:version "1.4.4"}} :paths [] :tasks {:requires diff --git a/deps.edn b/deps.edn index dbfefa1e..fae8747e 100644 --- a/deps.edn +++ b/deps.edn @@ -1,8 +1,8 @@ {:aliases - {:bb-deps {:extra-deps {org.clojars.hephaistox/automaton-build #:mvn{:version - "1.4.3"}}} - :build {:extra-deps {org.clojars.hephaistox/automaton-build #:mvn{:version - "1.4.3"}}} + {:bb-deps {:extra-deps {org.clojars.hephaistox/automaton-build + #:mvn{:version "1.4.4"}}} + :build {:extra-deps {org.clojars.hephaistox/automaton-build + #:mvn{:version "1.4.4"}}} :cljs-deps {:extra-deps {binaryage/devtools #:mvn{:version "1.0.7"} com.yetanalytics/colossal-squuid {:mvn/version "0.1.5"} diff --git a/pom.xml b/pom.xml index 2d6784cd..8e23f8d2 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ jar org.clojars.hephaistox automaton-core - 1.2.2 + 1.2.3 automaton-core diff --git a/src/cljc/automaton_core/adapters/schema.cljc b/src/cljc/automaton_core/adapters/schema.cljc index fda7d65d..747f4e5a 100644 --- a/src/cljc/automaton_core/adapters/schema.cljc +++ b/src/cljc/automaton_core/adapters/schema.cljc @@ -3,8 +3,22 @@ Is a proxy for malli" (:require - [malli.core :as malli] - [malli.error :as malli-error])) + [malli.core :as malli] + [malli.error :as malli-error] + [malli.transform :as malli-transform] + [malli.util :as malli-util])) + +(def registry (merge (malli/default-schemas) (malli-util/schemas))) + +(defn close-map-schema + "Turn a map schema into a closed one." + [map-schema] + (update map-schema + 1 + (fn [schema-params] + (if (map? schema-params) + (assoc schema-params :closed true) + schema-params)))) (defn validate-data "Return true if the data is matching the schema @@ -12,7 +26,9 @@ * `schema` schema to match * `data` data to check appliance to schema" [schema data] - (malli/validate schema data)) + (-> schema + (malli/schema {:registry registry}) + (malli/validate data))) (defn validate-data-humanize "Returns nil if valid, the error message otherwise. @@ -21,10 +37,14 @@ * `schema` schema to match * `data` data to check appliance to schema" [schema data] - (when-not (validate-data schema data) - (-> (malli/explain schema data) - malli-error/with-spell-checking - malli-error/humanize))) + (when-not (-> schema + (malli/schema {:registry registry}) + (validate-data data)) + {:error (-> (malli/explain schema data) + malli-error/with-spell-checking + malli-error/humanize) + :schema schema + :data data})) (defn validate "Test the schema parameter is valid @@ -32,7 +52,7 @@ Params: * `schema` schema to test" [schema] - (try (malli/schema schema) + (try (malli/schema schema {:registry registry}) true (catch #?(:clj Exception :cljs :default) @@ -47,9 +67,17 @@ Params: * `schema` schema to test" [schema] - (try (malli/schema schema) + (try (malli/schema schema {:registry registry}) nil (catch #?(:clj Exception :cljs :default) _ - (str "Schema not valid" (str schema))))) + (str "Schema not valid: " (str schema))))) + +(defn add-default + "Adds to `data` default values defined in the `schema`." + [schema data] + (malli/decode schema + data + (malli-transform/default-value-transformer + {::malli-transform/add-optional-keys true}))) diff --git a/src/cljc/automaton_core/utils/map.cljc b/src/cljc/automaton_core/utils/map.cljc index 25a3c937..5c4b1f27 100644 --- a/src/cljc/automaton_core/utils/map.cljc +++ b/src/cljc/automaton_core/utils/map.cljc @@ -93,6 +93,13 @@ [lang-id language])) m))) +(defn maps-to-key + "`maps` is a list of map which value matching key `k` is used as a key to store the map as a value." + [maps k] + (->> (for [m maps] [(get m k) m]) + (filter (comp some? first)) + (into {}))) + (defn update-kw "Update the keywords `kws` in map `m` with function `f`" [m kws f] @@ -175,6 +182,13 @@ (map (fn [[k v]] [k (get translation v k)])) (into {}))) +(defn submap? + "Is `sub` existing in `m`?" + [sub m] + (if (and (map? sub) (map? m)) + (every? (fn [[k v]] (and (contains? m k) (submap? v (get m k)))) sub) + (= sub m))) + (defn get-key-or-before "Returns the key if it exists in the sorted-map diff --git a/src/cljc/automaton_core/utils/sequences.cljc b/src/cljc/automaton_core/utils/sequences.cljc index a71ebf1f..0354e980 100644 --- a/src/cljc/automaton_core/utils/sequences.cljc +++ b/src/cljc/automaton_core/utils/sequences.cljc @@ -24,6 +24,28 @@ [coll pred] (let [idx? (fn [i a] (when (pred a) i))] (first (keep-indexed idx? coll)))) +(defn indexed + "Returns a lazy sequence of [index, item] pairs, where items come + from 's' and indexes count up from zero. + + (indexed '(a b c d)) => ([0 a] [1 b] [2 c] [3 d])" + [s] + (map vector (iterate inc 0) s)) + +(defn positions + "Returns a lazy sequence containing the positions at which pred + is true for items in coll." + [pred coll] + (for [[idx elt] (indexed coll) :when (pred elt)] idx)) + +(defn concat-at + "Concatenate sequences `x` and `y`, `y` is inserted at position `kw`. + If `kw` not found, `y` is concatenated at the end of `x`." + [x kw y] + (if-let [i (first (positions #{kw} x))] + (concat (take i x) y (drop (inc i) x)) + (concat x y))) + (defn position-by-values "Returns a map which associates each value of the vector to the positions where it happens. diff --git a/test/clj/automaton_core/adapters/schema_test.clj b/test/clj/automaton_core/adapters/schema_test.clj index b103f2f8..01f71f83 100644 --- a/test/clj/automaton_core/adapters/schema_test.clj +++ b/test/clj/automaton_core/adapters/schema_test.clj @@ -15,3 +15,10 @@ (testing "Invalid schema returns false" (is (some? (sut/validate-humanize nil))) (is (some? (sut/validate-humanize 12))))) + +(deftest add-default-test + (testing "Adds default values." + (is (sut/add-default [:map + [:foo {:default "bar"} + :string]] + {})))) diff --git a/test/cljc/automaton_core/utils/map_test.cljc b/test/cljc/automaton_core/utils/map_test.cljc index f08269dd..0e41c282 100644 --- a/test/cljc/automaton_core/utils/map_test.cljc +++ b/test/cljc/automaton_core/utils/map_test.cljc @@ -125,6 +125,24 @@ :bar "foo"})))) (testing "Empty maps are ok" (is (= {} (sut/add-ids {}))))) +(deftest maps-to-k-test + (is (= {:b {:a :b} + :a {:c :d + :a :a}} + (sut/maps-to-key [{:a :b} + {:c :d + :a :a}] + :a))) + (testing "Maps with no value for the key are removed." + (is (= {:b {:a :b} + :a {:c :d + :a :a}} + (sut/maps-to-key [{:a :b} + {:c :d + :a :a} + {}] + :a))))) + (deftest update-kw-test (testing "Update is ok, non selected keys are excluded" (is (= {:foo "arg" @@ -234,6 +252,21 @@ (is (= {} (sut/translate-keys {} nil))) (is (= {} (sut/translate-keys nil nil))))) +(deftest submap? + (testing "submap is found correctly" + (is (sut/submap? {:a {:b :B1}} + {:a {:b :B1 + :c :C1}})) + (is (sut/submap? {:c 5} + {:a {:b :B1} + :c 5})) + (is (sut/submap? {:a 1} {:a 1})) + (is (sut/submap? {} {}))) + (testing "submap is not found cases" + (is (false? (sut/submap? {:a {:b :B1}} {:c 5}))) + (is (false? (sut/submap? nil {}))) + (is (false? (sut/submap? {:c 5} nil))))) + (deftest get-key-or-before-test (testing "Test if latest key is returned, even if doesn't exist" (is (nil? (sut/get-key-or-before (into (sorted-map) diff --git a/test/cljc/automaton_core/utils/sequences_test.cljc b/test/cljc/automaton_core/utils/sequences_test.cljc index cf8e51dc..f3ef9f3e 100644 --- a/test/cljc/automaton_core/utils/sequences_test.cljc +++ b/test/cljc/automaton_core/utils/sequences_test.cljc @@ -30,6 +30,12 @@ (testing "Element not found in the sequence" (is (nil? (sut/index-of [1 2 3] :foo))))) +(deftest concat-at-test + (is (= [:a :b :c :d :e :f] (sut/concat-at [:a :b :x :e :f] :x [:c :d]))) + (testing "If replacing `kw` is not found, do nothing." + (is (= [:a :b :x :e :f :c :d] + (sut/concat-at [:a :b :x :e :f] :y [:c :d]))))) + (deftest position-by-values-test (testing "Non vector are ok" (is (= {1 [0] diff --git a/version.edn b/version.edn index fb112c08..9f51c8f3 100644 --- a/version.edn +++ b/version.edn @@ -1,2 +1,2 @@ ;; Last generated version, note a failed push consume a number -{:version "1.2.2"} \ No newline at end of file +{:version "1.2.3"} \ No newline at end of file