diff --git a/bb.edn b/bb.edn index b5a647b9..3bad1ec2 100644 --- a/bb.edn +++ b/bb.edn @@ -1,4 +1,5 @@ -{:deps {org.clojars.hephaistox/automaton-build-app {:mvn/version "0.0.70"}} +;;The file is updated automatically +{:deps {org.clojars.hephaistox/automaton-build-app {:mvn/version "0.0.74"}} :paths [] :tasks {:requires [[automaton-build-app.tasks.launcher.bb-entrypoint :as build-task-bb-entrypoint] [babashka.process :as babahska-process]] heph-task {:doc "Launch an Hephaistox task" @@ -6,4 +7,4 @@ lconnect {:doc "Repl in case automaton-core is failing. Using -f or --force cli arguments to force start if some part are failing" :task (try (-> (babahska-process/shell "clojure" "-M:common-test:env-development-repl:build" *command-line-args*) System/exit) - (catch Exception e (println "Repl failed also - error during repl startup" (ex-message e))))}}} + (catch Exception e (println "Repl failed also - error during repl startup" (ex-message e))))}}} \ No newline at end of file diff --git a/build_config.edn b/build_config.edn index aee17a92..350caed1 100644 --- a/build_config.edn +++ b/build_config.edn @@ -1,13 +1,9 @@ {:app-name "automaton-core" :task-shared {:gha {} - :mermaid-dir "docs/code/" :publication {:as-lib org.clojars.hephaistox/automaton-core - :branch "main" - :jar-path "target/prod/automaton-core.jar" + :deploy-to :clojars :major-version "0.0.%d" - :pom-path "target/prod/class/META-INF/maven/org.clojars.hephaistox/automaton-core/pom.xml" :repo "git@github.com:hephaistox/automaton-core.git"} - :repl-aliases [:common-test :env-development-repl :build] - :storage-datomic {:datomic-ver "1.0.7021"}} + :repl-aliases [:common-test :env-development-repl :build]} :tasks {:clean {:dirs [".cpcache/" ".clj-kondo/.cache/" "tmp/" "target/" "node_modules/" ".shadow-cljs/builds/"]} :reports {:forbiddenwords-words #{"automaton-web" "landing" "tap>"}}}} diff --git a/deps.edn b/deps.edn index 1fce99b6..d00d8beb 100644 --- a/deps.edn +++ b/deps.edn @@ -1,5 +1,5 @@ -{:aliases {:bb-deps {:extra-deps {org.clojars.hephaistox/automaton-build-app {:mvn/version "0.0.70"}}} - :build {:extra-deps {org.clojars.hephaistox/automaton-build-app {:mvn/version "0.0.70"}}} +{:aliases {:bb-deps {:extra-deps {org.clojars.hephaistox/automaton-build-app {:mvn/version "0.0.74"}}} + :build {:extra-deps {org.clojars.hephaistox/automaton-build-app {:mvn/version "0.0.74"}}} :cljs-deps {:extra-deps {binaryage/devtools {:mvn/version "1.0.7"} cider/cider-nrepl {:mvn/version "0.30.0"} thheller/shadow-cljs {:mvn/version "2.26.2"}} @@ -28,7 +28,7 @@ org.clojure/data.json {:mvn/version "2.5.0"} com.taoensso/tempura {:mvn/version "1.5.3"} danlentz/clj-uuid {:mvn/version "0.1.9"} - djblue/portal {:mvn/version "0.51.0"} ;; developer tooling + djblue/portal {:mvn/version "0.51.1"} ;; developer tooling http-kit/http-kit {:mvn/version "2.7.0"} io.sentry/sentry {:mvn/version "7.1.0"} io.sentry/sentry-clj {:mvn/version "6.33.209"} diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..039d8b56 --- /dev/null +++ b/pom.xml @@ -0,0 +1,151 @@ + + + 4.0.0 + jar + org.clojars.hephaistox + automaton-core + 0.0.55 + automaton-core + + + org.clojure + data.json + 2.5.0 + + + org.clojure + clojure + 1.11.1 + + + org.apache.logging.log4j + log4j-slf4j2-impl + 2.22.1 + + + org.clojure + tools.logging + 1.2.4 + + + org.clojure + tools.cli + 1.0.219 + + + danlentz + clj-uuid + 0.1.9 + + + com.clojure-goes-fast + clj-memory-meter + 0.3.0 + + + com.taoensso + encore + 3.62.1 + + + io.sentry + sentry + 7.1.0 + + + com.datomic + peer + 1.0.7075 + + + io.sentry + sentry-clj + 6.33.209 + + + org.postgresql + postgresql + 42.7.1 + + + com.taoensso + tempura + 1.5.3 + + + refactor-nrepl + refactor-nrepl + 3.6.0 + + + djblue + portal + 0.51.1 + + + org.apache.logging.log4j + log4j-core + 2.22.1 + + + zprint + zprint + 1.2.8 + + + metosin + malli + 0.13.0 + + + babashka + process + 0.5.21 + + + nrepl + nrepl + 1.1.0 + + + mount + mount + 0.1.17 + + + org.apache.logging.log4j + log4j-api + 2.22.1 + + + lambdaisland + uri + 1.16.134 + + + babashka + fs + 0.5.20 + + + http-kit + http-kit + 2.7.0 + + + + env/development/src/clj/ + + + + clojars + https://repo.clojars.org/ + + + + + Eclipse Public License 1.0 + https://opensource.org/license/epl-1-0/ + + + diff --git a/src/clj/automaton_core/log/be_log.clj b/src/clj/automaton_core/log/be_log.clj index 9155c46b..2c26e860 100644 --- a/src/clj/automaton_core/log/be_log.clj +++ b/src/clj/automaton_core/log/be_log.clj @@ -4,7 +4,8 @@ Current structure is generic for logging level, as they are the same right now in sense of this proxy. In future it may develop if needed to e.g. have the same number of macros as in `automaton-core.log`." (:require [automaton-core.log.be-registry :as log-be-registry] - [automaton-core.log.tracking.be-error-tracking :as exs])) + [automaton-core.log.tracking.be-error-tracking :as exs] + [automaton-core.log.terminal :as core-terminal])) (defn log-init! [{:keys [dsn env]}] @@ -17,7 +18,7 @@ (reduce (fn [acc logger-id] (if-let [logger-strategy (get-in log-be-registry/strategies-registry [logger-id :impl])] (conj acc logger-strategy) - (do (print "WARN: Logging strategy is nil for id: " logger-id) acc))) + (do (core-terminal/log "WARN: Logging strategy is nil for id: " logger-id) acc))) [] logger-ids)) diff --git a/src/clj/automaton_core/storage/component.clj b/src/clj/automaton_core/storage/component.clj index 4cba45e6..a29725a4 100644 --- a/src/clj/automaton_core/storage/component.clj +++ b/src/clj/automaton_core/storage/component.clj @@ -9,10 +9,11 @@ (defn start-storage [] - (try (core-log/info "Starting storage component") + (try (core-log/info "Start storage component") (let [dc (datomic/make-datomic-client datomic-schema/all-schema) - db-uri (or (conf/read-param [:storage :datomic :url]) (conf/read-param [:storage-datomic-url])) - _db-uri-valid? (when-not db-uri (throw (ex-info "Database uri was not found." {}))) + db-uri (conf/read-param [:storage :datomic :url]) + _db-uri-valid? (when-not db-uri + (throw (ex-info "Database uri was not found. Are you sure that env variable STORAGE_DATOMIC_URL is set?" {}))) conn (storage/connection dc db-uri) access (datomic/make-datomic-access)] (core-log/trace "Storage component is started") @@ -20,7 +21,7 @@ :access access}) (catch Throwable e (core-log/fatal (ex-info "Storage component failed." {:error e}))))) -(defstate storage-state :start (start-storage) :stop (.release storage-state)) +(defstate storage-state :start (start-storage) :stop (.release (:connection @storage-state))) (defn upsert [storage update-fn] (core-log/trace "Executed: " update-fn) (storage/upsert (:access storage) (:connection storage) update-fn)) diff --git a/src/cljc/automaton_core/configuration.cljc b/src/cljc/automaton_core/configuration.cljc index b88bcebe..e25ea70c 100644 --- a/src/cljc/automaton_core/configuration.cljc +++ b/src/cljc/automaton_core/configuration.cljc @@ -11,7 +11,8 @@ gathering all classpath, so all `config.edn` versions. The solution was to be based on environment parameter. So each alias can tell which version it uses, especially monorepo could be different." (:require [automaton-core.configuration.protocol :as core-conf-prot] - [automaton-core.configuration.simple-files :as simple-files] + [automaton-core.configuration.files :as core-conf-files] + [automaton-core.configuration.environment :as core-conf-env] [mount.core :refer [defstate in-cljc-mode]])) ;; Force the use of `cljc mode` in mount library, so call to `@` will work @@ -20,9 +21,10 @@ (defn start-conf [] (try (println "Starting configuration component") - (let [conf (simple-files/->SimpleConf)] + (let [conf (core-conf-files/->FilesConf) + env-conf (core-conf-env/->EnvConf)] (println "Configuration component is started") - conf) + [conf env-conf]) (catch #?(:clj Throwable :cljs :default) e @@ -35,11 +37,9 @@ (defn read-param "Returns value under `key-path` vector." ([key-path default-value] - (let [value (core-conf-prot/read-conf-param @conf-state key-path)] - (if (nil? value) - (do (println "Value for " key-path " is not set, use default value" default-value) default-value) - (do (println "Read key-path " key-path " = " value) value)))) + (let [value (or (core-conf-prot/read-conf-param (first @conf-state) key-path) + (core-conf-prot/read-conf-param (second @conf-state) key-path))] + (when (nil? value) (println "Value for " key-path " is not set, use default value") default-value))) ([key-path] (read-param key-path nil))) -#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]} -(defn all-config "Returns whole configuration map, with all the keys and values." [] (core-conf-prot/config @conf-state)) + diff --git a/src/cljc/automaton_core/configuration/environment.cljc b/src/cljc/automaton_core/configuration/environment.cljc new file mode 100644 index 00000000..ed21c505 --- /dev/null +++ b/src/cljc/automaton_core/configuration/environment.cljc @@ -0,0 +1,54 @@ +(ns automaton-core.configuration.environment + (:require #?@(:clj [[clojure.edn :as edn]] + :cljs [[cljs.reader :as edn] [goog.object :as obj]]) + [automaton-core.configuration.protocol :as core-conf-prot] + [automaton-core.utils.keyword :as utils-keyword] + [clojure.string :as str])) + +#?(:cljs (def ^:private nodejs? (exists? js/require))) + +#?(:cljs (def ^:private process (when nodejs? (js/require "process")))) + +(defn env-key-path + "Turns key-path ([:a :b :c] -> 'a-b-c') into environment type key." + [key-path] + (let [path-str (str/join "-" (map name key-path))] (when-not (str/blank? path-str) (utils-keyword/keywordize path-str)))) + +(defn parse-number + [^String v] + (try #?(:clj (Long/parseLong v) + :cljs (parse-long v)) + #?(:clj (catch NumberFormatException _ (BigInteger. v))) + (catch #?(:clj Exception + :cljs js/Error) + _ + v))) + +#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]} +(defn parse-system-env + "Turns string type into number. In case of failure in parsing it's returned in a format as it was (a string)." + [v] + (cond (re-matches #"[0-9]+" v) (parse-number v) + (re-matches #"^(true|false)$" v) #?(:clj (Boolean/parseBoolean v) + :cljs (parse-boolean v)) + (re-matches #"\w+" v) v + :else (try (let [parsed (edn/read-string v)] (if (symbol? parsed) v parsed)) + (catch #?(:clj Exception + :cljs js/Error) + _ + v)))) + +(defn read-all + "Reads all system env properties and converts to appropriate type." + [] + (->> #?(:clj (System/getenv) + :cljs (if process (let [env (.-env process)] (zipmap (obj/getKeys env) (obj/getValues env))) {})) + (map (fn [[k v]] [(utils-keyword/keywordize k) v])) + (into {}))) + +(def ^{:doc "A map of configuration variables."} conf (memoize read-all)) + +(defrecord EnvConf [] + core-conf-prot/Conf + (read-conf-param [_this key-path] (get (conf) (env-key-path key-path))) + (config [_this] (conf))) diff --git a/src/cljc/automaton_core/configuration/simple_files.cljc b/src/cljc/automaton_core/configuration/files.cljc similarity index 50% rename from src/cljc/automaton_core/configuration/simple_files.cljc rename to src/cljc/automaton_core/configuration/files.cljc index 120dab74..bcf5f919 100644 --- a/src/cljc/automaton_core/configuration/simple_files.cljc +++ b/src/cljc/automaton_core/configuration/files.cljc @@ -1,50 +1,16 @@ -(ns automaton-core.configuration.simple-files +(ns automaton-core.configuration.files "Namespace for simple configuration based on local file. Just like in core configuration, we are not using log nor outside dependencies to comply with the configuration requirements." (:require #?@(:clj [[clojure.edn :as edn] [clojure.java.io :as io] [automaton-core.adapters.java-properties :as java-properties]] - :cljs [[cljs.reader :as edn] [goog.object :as obj]]) - [automaton-core.utils.map :as utils-map] + :cljs [[cljs.reader :as edn]]) [automaton-core.configuration.protocol :as core-conf-prot] - [automaton-core.utils.keyword :as utils-keyword])) + [automaton-core.utils.keyword :as utils-keyword] + [automaton-core.utils.map :as utils-map])) #?(:cljs (def ^:private nodejs? (exists? js/require))) #?(:cljs (def ^:private fs (when nodejs? (js/require "fs")))) -#?(:cljs (def ^:private process (when nodejs? (js/require "process")))) - -(defn parse-number - [^String v] - (try #?(:clj (Long/parseLong v) - :cljs (parse-long v)) - #?(:clj (catch NumberFormatException _ (BigInteger. v))) - (catch #?(:clj Exception - :cljs js/Error) - _ - v))) - -(defn str->value - "ENV vars and system properties are strings. str->value will convert: - the numbers to longs, the alphanumeric values to strings, and will use Clojure reader for the rest - in case reader can't read OR it reads a symbol, the value will be returned as is (a string)" - [v] - (cond (re-matches #"[0-9]+" v) (parse-number v) - (re-matches #"^(true|false)$" v) #?(:clj (Boolean/parseBoolean v) - :cljs (parse-boolean v)) - (re-matches #"\w+" v) v - :else (try (let [parsed (edn/read-string v)] (if (symbol? parsed) v parsed)) - (catch #?(:clj Exception - :cljs js/Error) - _ - v)))) - -(defn read-system-env - [] - (->> #?(:clj (System/getenv) - :cljs (zipmap (obj/getKeys (.-env process)) (obj/getValues (.-env process)))) - (map (fn [[k v]] [(utils-keyword/keywordize k) (str->value v)])) - (into {}))) - (defn slurp-file [f] (try #?(:clj (when-let [file (or (io/resource f) (io/file f))] (slurp file)) @@ -71,12 +37,8 @@ (defn- warn-on-overwrite [ms] - (doseq [[k kvs] (group-by key (apply concat ms)) - :let [vs (map val kvs)] - :when (and (next kvs) (not= (first vs) (last vs)))] - (println "WARNING: configuration keys are duplicated" - {:k k - :vs vs}))) + (let [kseq (reduce (fn [acc m] (concat acc (keys m))) [] ms)] + (for [[id freq] (frequencies kseq) :when (> freq 1)] (println "WARNING: configuration keys are duplicated for:" id)))) (defn merge-configs [& m] (warn-on-overwrite m) (apply utils-map/deep-merge m)) @@ -87,15 +49,12 @@ property->config-files (mapv read-config-file) (filterv some?) - (apply merge-configs (read-system-env))) - :cljs (if nodejs? - (->> (read-config-file config-file) - (merge-configs (read-system-env))) - {}))) + (apply merge-configs)) + :cljs (if nodejs? (read-config-file config-file) {}))) (def ^{:doc "A map of configuration variables."} conf (memoize read-config)) -(defrecord SimpleConf [] +(defrecord FilesConf [] core-conf-prot/Conf (read-conf-param [_this key-path] (get-in (conf) key-path)) (config [_this] (conf))) diff --git a/src/cljc/automaton_core/log/terminal.cljc b/src/cljc/automaton_core/log/terminal.cljc new file mode 100644 index 00000000..838f652b --- /dev/null +++ b/src/cljc/automaton_core/log/terminal.cljc @@ -0,0 +1,3 @@ +(ns automaton-core.log.terminal) + +(defn log [& msg] (apply println msg)) diff --git a/src/cljc/automaton_core/utils/keyword.cljc b/src/cljc/automaton_core/utils/keyword.cljc index b3527236..b157702a 100644 --- a/src/cljc/automaton_core/utils/keyword.cljc +++ b/src/cljc/automaton_core/utils/keyword.cljc @@ -5,18 +5,17 @@ (defn keywordize "Change string to appropriate clojure keyword" [s] - (-> (str/lower-case s) + (-> (name s) + str/lower-case (str/replace "_" "-") (str/replace "." "-") (keyword))) -(defn sanitize-key "Changes keyword to appropriate clojure keyword." [k] (let [s (keywordize (name k))] s)) - (defn sanitize-map-keys "Changes all keywords in a map to appropriate clojure keys." [map] (reduce-kv (fn [acc key value] - (let [new-key (if (keyword? key) (sanitize-key key) key) + (let [new-key (if (keyword? key) (keywordize key) key) new-val (if (map? value) (sanitize-map-keys value) value)] (assoc acc new-key new-val))) {} diff --git a/src/cljc/automaton_core/utils/map.cljc b/src/cljc/automaton_core/utils/map.cljc index 50da3bea..914ac4c9 100644 --- a/src/cljc/automaton_core/utils/map.cljc +++ b/src/cljc/automaton_core/utils/map.cljc @@ -107,3 +107,9 @@ (cond (map? v) [k (modify-type-fn v)] (and (some? v) (some? k)) [k v])))] (walk/postwalk (fn [x] (if (map? x) (into {} (map f x)) x)) m))) + +(defn replace-keys + "Replace keys in `m2` with keys from `m1`. Similiar to merge but non-existen keys in first map won't be added. e.g. (replace-keys {:a 3 :b 2} {:a 1}) -> {:a 3}" + [m1 m2] + (->> (select-keys m1 (keys m2)) + (merge m2))) diff --git a/test/cljc/automaton_core/utils/keyword_test.cljc b/test/cljc/automaton_core/utils/keyword_test.cljc index 1efc55db..1e5583fe 100644 --- a/test/cljc/automaton_core/utils/keyword_test.cljc +++ b/test/cljc/automaton_core/utils/keyword_test.cljc @@ -3,12 +3,12 @@ #?(:clj [clojure.test :refer [deftest is testing]] :cljs [cljs.test :refer [deftest is testing] :include-macros true]))) -(deftest sanitize-key-test - (testing "Basic case" (is (= :hello (sut/sanitize-key :hello)))) - (testing "capital letters turned into lower case" (is (= :hello (sut/sanitize-key :HeLLo)))) - (testing "_ turned into -" (is (= :hello-world (sut/sanitize-key :hello_world)))) - (testing ". turned into -" (is (= :hello-world (sut/sanitize-key :hello-world)))) - (testing "Works also on strings" (is (= :hello-my-world (sut/sanitize-key "hello.MY_wOrLd"))))) +(deftest keywordize-test + (testing "Basic case" (is (= :hello (sut/keywordize :hello)))) + (testing "capital letters turned into lower case" (is (= :hello (sut/keywordize :HeLLo)))) + (testing "_ turned into -" (is (= :hello-world (sut/keywordize :hello_world)))) + (testing ". turned into -" (is (= :hello-world (sut/keywordize :hello-world)))) + (testing "Works also on strings" (is (= :hello-my-world (sut/keywordize "hello.MY_wOrLd"))))) (deftest sanitize-map-keys-test (testing "Basic case" diff --git a/version.edn b/version.edn index 17048c31..f959289c 100644 --- a/version.edn +++ b/version.edn @@ -1,4 +1,4 @@ ;;Last generated version, note a failed push consume a number {:major-version "0.0.-1" - :minor-version 26 - :version "0.0.26"} \ No newline at end of file + :minor-version 55 + :version "0.0.55"} \ No newline at end of file