From 47ec5072f9fcebbdf8a5eec7b7cbefa0284bd40e Mon Sep 17 00:00:00 2001 From: Karina Moreira Date: Thu, 4 Apr 2024 22:11:37 -0300 Subject: [PATCH 01/11] Handle comma-separated strings in config --- src/codes/clj/docs/backend/config.clj | 26 +++++++++ .../codes/clj/docs/backend/config_test.clj | 57 +++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 src/codes/clj/docs/backend/config.clj create mode 100644 test/unit/codes/clj/docs/backend/config_test.clj diff --git a/src/codes/clj/docs/backend/config.clj b/src/codes/clj/docs/backend/config.clj new file mode 100644 index 0000000..96c6c92 --- /dev/null +++ b/src/codes/clj/docs/backend/config.clj @@ -0,0 +1,26 @@ +(ns codes.clj.docs.backend.config + (:require [clojure.string :as string])) + +(def vector-env-vars + "Nested keys for config variables with corresponding environment + variables that can hold multiple values separated by commas, e.g.: + ALLOWED_ORIGINS=\"https://domain.a.com, https://domain.b.com\"" + [[:config :webserver/allowed-origins]]) + +(defn- str-var->vector-var + "Converts a string config variable to a vector of strings, when applicable. + Environment variables are expected to be set as comma-separated values." + [config nested-keys] + (let [target-config (get-in config nested-keys)] + (if (string? target-config) + (let [env-config (clojure.string/split target-config #", ")] + (assoc-in config nested-keys env-config)) + config))) + +(defn- resolved-envs-config + [config str-envs] + (reduce str-var->vector-var config str-envs)) + +(defn config + [config] + (resolved-envs-config config vector-env-vars)) diff --git a/test/unit/codes/clj/docs/backend/config_test.clj b/test/unit/codes/clj/docs/backend/config_test.clj new file mode 100644 index 0000000..bd91c13 --- /dev/null +++ b/test/unit/codes/clj/docs/backend/config_test.clj @@ -0,0 +1,57 @@ +(ns unit.codes.clj.docs.backend.config-test + (:require [clojure.test :refer [deftest is testing use-fixtures]] + [codes.clj.docs.backend.config :as backend.config] + [com.stuartsierra.component :as component] + [matcher-combinators.matchers :refer [embeds]] + [matcher-combinators.test :refer [match?]] + [parenthesin.components.config.aero :as config.aero] + [parenthesin.helpers.malli :as helpers.malli])) + +(use-fixtures :once helpers.malli/with-intrumentation) + +(defn- create-and-start-system! + [{:keys [config]}] + (component/start-system + (component/system-map :config config))) + +(deftest str-var->vector-var-test + (let [system (create-and-start-system! + {:config (config.aero/new-config {:some-config "value1, value2" + :another-config {:nested-config + "value3, value4"}})}) + config-component (:config system)] + + (testing "root-level configs should be converted to vectors" + (is (match? {:config {:some-config ["value1" "value2"]}} + (#'backend.config/str-var->vector-var + config-component [:config :some-config])))) + + (testing "nested configs should be converted to vectors" + (is (match? {:config {:another-config {:nested-config ["value3" "value4"]}}} + (#'backend.config/str-var->vector-var + config-component [:config :another-config :nested-config])))) + + (let [vector-env-vars [[:config :some-config] + [:config :another-config :nested-config]] + no-matching-env-vars [[:config :non-existent-config]]] + + (testing "all defined vector-env-vars should be processed" + (is (match? {:config (embeds {:some-config ["value1" "value2"], + :another-config {:nested-config + ["value3" "value4"]}})} + (#'backend.config/resolved-envs-config config-component + vector-env-vars)))) + + (testing "when vector-env-vars is empty, the config should be left unaltered" + (is (match? {:config (embeds {:some-config "value1, value2", + :another-config {:nested-config + "value3, value4"}})} + (#'backend.config/resolved-envs-config config-component + [])))) + + (testing "when vector-env-vars has no matches, the config should be left unaltered" + (is (match? {:config (embeds {:some-config "value1, value2", + :another-config {:nested-config + "value3, value4"}})} + (#'backend.config/resolved-envs-config config-component + no-matching-env-vars))))))) From d6caad6405f7ae33666d0df6a70607d0cd10d1c4 Mon Sep 17 00:00:00 2001 From: Karina Moreira Date: Thu, 4 Apr 2024 22:12:08 -0300 Subject: [PATCH 02/11] Apply to base-system-map --- src/codes/clj/docs/backend/server.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/codes/clj/docs/backend/server.clj b/src/codes/clj/docs/backend/server.clj index ca9ef04..25ca6e5 100644 --- a/src/codes/clj/docs/backend/server.clj +++ b/src/codes/clj/docs/backend/server.clj @@ -1,5 +1,6 @@ (ns codes.clj.docs.backend.server (:require [codes.clj.docs.backend.components.db-docs :as components.db-docs] + [codes.clj.docs.backend.config :as backend.config] [codes.clj.docs.backend.db.datalevin :refer [read-conn-opts]] [codes.clj.docs.backend.routes :as routes] [com.stuartsierra.component :as component] @@ -16,7 +17,7 @@ (defn base-system-map [] (component/system-map - :config (config/new-config) + :config (backend.config/config (config/new-config)) :http (http/new-http) :router (router/new-router routes/routes) :database (component/using (database/new-database) [:config]) From e196ba087d6ec40de05a260c69fd6953d1f85938 Mon Sep 17 00:00:00 2001 From: Karina Moreira Date: Thu, 4 Apr 2024 22:12:52 -0300 Subject: [PATCH 03/11] Allowed-origins config accepts env var --- resources/config.edn | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/resources/config.edn b/resources/config.edn index 903c8d1..e213a73 100644 --- a/resources/config.edn +++ b/resources/config.edn @@ -1,6 +1,7 @@ {:webserver/port #long #or [#env PORT 3001] - :webserver/allowed-origins ["http://docs.clj.codes" "https://docs.clj.codes" - "http://docs-frontend.fly.dev" "https://docs-frontend.fly.dev"] + :webserver/allowed-origins #or [#env ALLOWED_ORIGINS + ["http://docs.clj.codes" "https://docs.clj.codes" + "http://docs-frontend.fly.dev" "https://docs-frontend.fly.dev"]] :database {:dbtype "postgres" :dbname #or [#env DB_NAME "postgres"] From abddf005ec5942d3f41201bda6bc24761e1385c0 Mon Sep 17 00:00:00 2001 From: Karina Moreira Date: Fri, 5 Apr 2024 15:10:13 -0300 Subject: [PATCH 04/11] Handle trailing commas and extra whitespaces --- src/codes/clj/docs/backend/config.clj | 14 ++-- .../codes/clj/docs/backend/config_test.clj | 81 ++++++++++--------- 2 files changed, 52 insertions(+), 43 deletions(-) diff --git a/src/codes/clj/docs/backend/config.clj b/src/codes/clj/docs/backend/config.clj index 96c6c92..b38f79b 100644 --- a/src/codes/clj/docs/backend/config.clj +++ b/src/codes/clj/docs/backend/config.clj @@ -10,12 +10,14 @@ (defn- str-var->vector-var "Converts a string config variable to a vector of strings, when applicable. Environment variables are expected to be set as comma-separated values." - [config nested-keys] - (let [target-config (get-in config nested-keys)] - (if (string? target-config) - (let [env-config (clojure.string/split target-config #", ")] - (assoc-in config nested-keys env-config)) - config))) + [config nested-keys] + (let [target-config (get-in config nested-keys)] + (if (string? target-config) + (let [split-configs (-> target-config + (string/split #", ")) + env-config (map string/trim split-configs)] + (assoc-in config nested-keys env-config)) + config))) (defn- resolved-envs-config [config str-envs] diff --git a/test/unit/codes/clj/docs/backend/config_test.clj b/test/unit/codes/clj/docs/backend/config_test.clj index bd91c13..579c72e 100644 --- a/test/unit/codes/clj/docs/backend/config_test.clj +++ b/test/unit/codes/clj/docs/backend/config_test.clj @@ -16,42 +16,49 @@ (deftest str-var->vector-var-test (let [system (create-and-start-system! - {:config (config.aero/new-config {:some-config "value1, value2" - :another-config {:nested-config - "value3, value4"}})}) + {:config (config.aero/new-config {:some-config "value1, value2" + :another-config + {:nested-config "value3, value4"} + :malformed-config "value5, value6, "})}) config-component (:config system)] - (testing "root-level configs should be converted to vectors" - (is (match? {:config {:some-config ["value1" "value2"]}} - (#'backend.config/str-var->vector-var - config-component [:config :some-config])))) - - (testing "nested configs should be converted to vectors" - (is (match? {:config {:another-config {:nested-config ["value3" "value4"]}}} - (#'backend.config/str-var->vector-var - config-component [:config :another-config :nested-config])))) - - (let [vector-env-vars [[:config :some-config] - [:config :another-config :nested-config]] - no-matching-env-vars [[:config :non-existent-config]]] - - (testing "all defined vector-env-vars should be processed" - (is (match? {:config (embeds {:some-config ["value1" "value2"], - :another-config {:nested-config - ["value3" "value4"]}})} - (#'backend.config/resolved-envs-config config-component - vector-env-vars)))) - - (testing "when vector-env-vars is empty, the config should be left unaltered" - (is (match? {:config (embeds {:some-config "value1, value2", - :another-config {:nested-config - "value3, value4"}})} - (#'backend.config/resolved-envs-config config-component - [])))) - - (testing "when vector-env-vars has no matches, the config should be left unaltered" - (is (match? {:config (embeds {:some-config "value1, value2", - :another-config {:nested-config - "value3, value4"}})} - (#'backend.config/resolved-envs-config config-component - no-matching-env-vars))))))) + (testing "root-level configs should be converted to vectors" + (is (match? {:config {:some-config ["value1" "value2"]}} + (#'backend.config/str-var->vector-var + config-component [:config :some-config])))) + + (testing "nested configs should be converted to vectors" + (is (match? {:config {:another-config {:nested-config ["value3" "value4"]}}} + (#'backend.config/str-var->vector-var + config-component [:config :another-config :nested-config])))) + + (testing "trailing commas and extra whitespaces are ignored" + (is (match? {:config {:malformed-config ["value5" "value6"]}} + (#'backend.config/str-var->vector-var + config-component [:config :malformed-config])))) + + (let [vector-env-vars [[:config :some-config] + [:config :another-config :nested-config] + [:config :malformed-config]] + no-matching-env-vars [[:config :non-existent-config]] + converted-configs {:some-config ["value1" "value2"], + :another-config {:nested-config ["value3" "value4"]} + :malformed-config ["value5" "value6"]} + unaltered-configs {:some-config "value1, value2", + :another-config {:nested-config "value3, value4"} + :malformed-config "value5, value6, "}] + + (testing "all defined vector-env-vars should be processed" + (is (match? {:config (embeds converted-configs)} + (#'backend.config/resolved-envs-config config-component + vector-env-vars)))) + + (testing "when vector-env-vars is empty, the config should be left unaltered" + (is (match? {:config (embeds unaltered-configs)} + (#'backend.config/resolved-envs-config config-component + [])))) + + (testing "when vector-env-vars has no matches, the config should be left unaltered" + (is (match? {:config (embeds unaltered-configs)} + (#'backend.config/resolved-envs-config config-component + no-matching-env-vars))))))) From 9e31c12cac7ca8b6da6bc5714c4d6a8451a73f5b Mon Sep 17 00:00:00 2001 From: Karina Moreira Date: Fri, 5 Apr 2024 15:12:02 -0300 Subject: [PATCH 05/11] Lint-fix --- src/codes/clj/docs/backend/config.clj | 22 +++--- .../codes/clj/docs/backend/config_test.clj | 72 +++++++++---------- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/src/codes/clj/docs/backend/config.clj b/src/codes/clj/docs/backend/config.clj index b38f79b..714b4fe 100644 --- a/src/codes/clj/docs/backend/config.clj +++ b/src/codes/clj/docs/backend/config.clj @@ -3,21 +3,21 @@ (def vector-env-vars "Nested keys for config variables with corresponding environment - variables that can hold multiple values separated by commas, e.g.: - ALLOWED_ORIGINS=\"https://domain.a.com, https://domain.b.com\"" + variables that can hold multiple values separated by commas, e.g.: + ALLOWED_ORIGINS=\"https://domain.a.com, https://domain.b.com\"" [[:config :webserver/allowed-origins]]) (defn- str-var->vector-var "Converts a string config variable to a vector of strings, when applicable. - Environment variables are expected to be set as comma-separated values." - [config nested-keys] - (let [target-config (get-in config nested-keys)] - (if (string? target-config) - (let [split-configs (-> target-config - (string/split #", ")) - env-config (map string/trim split-configs)] - (assoc-in config nested-keys env-config)) - config))) + Environment variables are expected to be set as comma-separated values." + [config nested-keys] + (let [target-config (get-in config nested-keys)] + (if (string? target-config) + (let [split-configs (-> target-config + (string/split #", ")) + env-config (map string/trim split-configs)] + (assoc-in config nested-keys env-config)) + config))) (defn- resolved-envs-config [config str-envs] diff --git a/test/unit/codes/clj/docs/backend/config_test.clj b/test/unit/codes/clj/docs/backend/config_test.clj index 579c72e..2382356 100644 --- a/test/unit/codes/clj/docs/backend/config_test.clj +++ b/test/unit/codes/clj/docs/backend/config_test.clj @@ -16,49 +16,49 @@ (deftest str-var->vector-var-test (let [system (create-and-start-system! - {:config (config.aero/new-config {:some-config "value1, value2" - :another-config + {:config (config.aero/new-config {:some-config "value1, value2" + :another-config {:nested-config "value3, value4"} :malformed-config "value5, value6, "})}) config-component (:config system)] - (testing "root-level configs should be converted to vectors" - (is (match? {:config {:some-config ["value1" "value2"]}} - (#'backend.config/str-var->vector-var - config-component [:config :some-config])))) + (testing "root-level configs should be converted to vectors" + (is (match? {:config {:some-config ["value1" "value2"]}} + (#'backend.config/str-var->vector-var + config-component [:config :some-config])))) - (testing "nested configs should be converted to vectors" - (is (match? {:config {:another-config {:nested-config ["value3" "value4"]}}} - (#'backend.config/str-var->vector-var - config-component [:config :another-config :nested-config])))) + (testing "nested configs should be converted to vectors" + (is (match? {:config {:another-config {:nested-config ["value3" "value4"]}}} + (#'backend.config/str-var->vector-var + config-component [:config :another-config :nested-config])))) - (testing "trailing commas and extra whitespaces are ignored" - (is (match? {:config {:malformed-config ["value5" "value6"]}} - (#'backend.config/str-var->vector-var - config-component [:config :malformed-config])))) + (testing "trailing commas and extra whitespaces are ignored" + (is (match? {:config {:malformed-config ["value5" "value6"]}} + (#'backend.config/str-var->vector-var + config-component [:config :malformed-config])))) - (let [vector-env-vars [[:config :some-config] - [:config :another-config :nested-config] - [:config :malformed-config]] - no-matching-env-vars [[:config :non-existent-config]] - converted-configs {:some-config ["value1" "value2"], - :another-config {:nested-config ["value3" "value4"]} - :malformed-config ["value5" "value6"]} - unaltered-configs {:some-config "value1, value2", - :another-config {:nested-config "value3, value4"} - :malformed-config "value5, value6, "}] + (let [vector-env-vars [[:config :some-config] + [:config :another-config :nested-config] + [:config :malformed-config]] + no-matching-env-vars [[:config :non-existent-config]] + converted-configs {:some-config ["value1" "value2"], + :another-config {:nested-config ["value3" "value4"]} + :malformed-config ["value5" "value6"]} + unaltered-configs {:some-config "value1, value2", + :another-config {:nested-config "value3, value4"} + :malformed-config "value5, value6, "}] - (testing "all defined vector-env-vars should be processed" - (is (match? {:config (embeds converted-configs)} - (#'backend.config/resolved-envs-config config-component - vector-env-vars)))) + (testing "all defined vector-env-vars should be processed" + (is (match? {:config (embeds converted-configs)} + (#'backend.config/resolved-envs-config config-component + vector-env-vars)))) - (testing "when vector-env-vars is empty, the config should be left unaltered" - (is (match? {:config (embeds unaltered-configs)} - (#'backend.config/resolved-envs-config config-component - [])))) + (testing "when vector-env-vars is empty, the config should be left unaltered" + (is (match? {:config (embeds unaltered-configs)} + (#'backend.config/resolved-envs-config config-component + [])))) - (testing "when vector-env-vars has no matches, the config should be left unaltered" - (is (match? {:config (embeds unaltered-configs)} - (#'backend.config/resolved-envs-config config-component - no-matching-env-vars))))))) + (testing "when vector-env-vars has no matches, the config should be left unaltered" + (is (match? {:config (embeds unaltered-configs)} + (#'backend.config/resolved-envs-config config-component + no-matching-env-vars))))))) From 62824e6e0b846f9d3b99039fb01bc355bf846c3b Mon Sep 17 00:00:00 2001 From: Karina Moreira Date: Fri, 5 Apr 2024 15:26:36 -0300 Subject: [PATCH 06/11] Extra test scenarios --- src/codes/clj/docs/backend/config.clj | 6 +- .../codes/clj/docs/backend/config_test.clj | 79 +++++++++++-------- 2 files changed, 48 insertions(+), 37 deletions(-) diff --git a/src/codes/clj/docs/backend/config.clj b/src/codes/clj/docs/backend/config.clj index 714b4fe..253d0c4 100644 --- a/src/codes/clj/docs/backend/config.clj +++ b/src/codes/clj/docs/backend/config.clj @@ -14,8 +14,10 @@ (let [target-config (get-in config nested-keys)] (if (string? target-config) (let [split-configs (-> target-config - (string/split #", ")) - env-config (map string/trim split-configs)] + (string/split #",")) + env-config (->> split-configs + (map string/trim) + (remove empty?))] (assoc-in config nested-keys env-config)) config))) diff --git a/test/unit/codes/clj/docs/backend/config_test.clj b/test/unit/codes/clj/docs/backend/config_test.clj index 2382356..d2e9dc3 100644 --- a/test/unit/codes/clj/docs/backend/config_test.clj +++ b/test/unit/codes/clj/docs/backend/config_test.clj @@ -19,46 +19,55 @@ {:config (config.aero/new-config {:some-config "value1, value2" :another-config {:nested-config "value3, value4"} - :malformed-config "value5, value6, "})}) + :malformed-config "value5, value6,value7, " + :trailing-comma "value8, value9,"})}) config-component (:config system)] - (testing "root-level configs should be converted to vectors" - (is (match? {:config {:some-config ["value1" "value2"]}} - (#'backend.config/str-var->vector-var - config-component [:config :some-config])))) + (testing "root-level configs should be converted to vectors" + (is (match? {:config {:some-config ["value1" "value2"]}} + (#'backend.config/str-var->vector-var + config-component [:config :some-config])))) - (testing "nested configs should be converted to vectors" - (is (match? {:config {:another-config {:nested-config ["value3" "value4"]}}} - (#'backend.config/str-var->vector-var - config-component [:config :another-config :nested-config])))) + (testing "nested configs should be converted to vectors" + (is (match? {:config {:another-config {:nested-config ["value3" "value4"]}}} + (#'backend.config/str-var->vector-var + config-component [:config :another-config :nested-config])))) - (testing "trailing commas and extra whitespaces are ignored" - (is (match? {:config {:malformed-config ["value5" "value6"]}} - (#'backend.config/str-var->vector-var - config-component [:config :malformed-config])))) + (testing "trailing and extra whitespaces should be ignored" + (is (match? {:config {:malformed-config ["value5" "value6" "value7"]}} + (#'backend.config/str-var->vector-var + config-component [:config :malformed-config])))) - (let [vector-env-vars [[:config :some-config] - [:config :another-config :nested-config] - [:config :malformed-config]] - no-matching-env-vars [[:config :non-existent-config]] - converted-configs {:some-config ["value1" "value2"], - :another-config {:nested-config ["value3" "value4"]} - :malformed-config ["value5" "value6"]} - unaltered-configs {:some-config "value1, value2", - :another-config {:nested-config "value3, value4"} - :malformed-config "value5, value6, "}] + (testing "trailing commas should be ignored" + (is (match? {:config {:trailing-comma ["value8" "value9"]}} + (#'backend.config/str-var->vector-var + config-component [:config :trailing-comma])))) - (testing "all defined vector-env-vars should be processed" - (is (match? {:config (embeds converted-configs)} - (#'backend.config/resolved-envs-config config-component - vector-env-vars)))) + (let [vector-env-vars [[:config :some-config] + [:config :another-config :nested-config] + [:config :malformed-config] + [:config :trailing-comma]] + no-matching-env-vars [[:config :non-existent-config]] + converted-configs {:some-config ["value1" "value2"], + :another-config {:nested-config ["value3" "value4"]} + :malformed-config ["value5" "value6" "value7"] + :trailing-comma ["value8" "value9"]} + unaltered-configs {:some-config "value1, value2", + :another-config {:nested-config "value3, value4"} + :malformed-config "value5, value6,value7, " + :trailing-comma "value8, value9,"}] - (testing "when vector-env-vars is empty, the config should be left unaltered" - (is (match? {:config (embeds unaltered-configs)} - (#'backend.config/resolved-envs-config config-component - [])))) + (testing "all defined vector-env-vars should be processed" + (is (match? {:config (embeds converted-configs)} + (#'backend.config/resolved-envs-config config-component + vector-env-vars)))) - (testing "when vector-env-vars has no matches, the config should be left unaltered" - (is (match? {:config (embeds unaltered-configs)} - (#'backend.config/resolved-envs-config config-component - no-matching-env-vars))))))) + (testing "when vector-env-vars is empty, the config should be left unaltered" + (is (match? {:config (embeds unaltered-configs)} + (#'backend.config/resolved-envs-config config-component + [])))) + + (testing "when vector-env-vars has no matches, the config should be left unaltered" + (is (match? {:config (embeds unaltered-configs)} + (#'backend.config/resolved-envs-config config-component + no-matching-env-vars))))))) From cdc68bc4e8ebcdb2463ab0d3e91bd21e29f7c711 Mon Sep 17 00:00:00 2001 From: Karina Moreira Date: Fri, 5 Apr 2024 15:27:08 -0300 Subject: [PATCH 07/11] Lint-fix --- .../codes/clj/docs/backend/config_test.clj | 82 +++++++++---------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/test/unit/codes/clj/docs/backend/config_test.clj b/test/unit/codes/clj/docs/backend/config_test.clj index d2e9dc3..e4a328d 100644 --- a/test/unit/codes/clj/docs/backend/config_test.clj +++ b/test/unit/codes/clj/docs/backend/config_test.clj @@ -23,51 +23,51 @@ :trailing-comma "value8, value9,"})}) config-component (:config system)] - (testing "root-level configs should be converted to vectors" - (is (match? {:config {:some-config ["value1" "value2"]}} - (#'backend.config/str-var->vector-var - config-component [:config :some-config])))) + (testing "root-level configs should be converted to vectors" + (is (match? {:config {:some-config ["value1" "value2"]}} + (#'backend.config/str-var->vector-var + config-component [:config :some-config])))) - (testing "nested configs should be converted to vectors" - (is (match? {:config {:another-config {:nested-config ["value3" "value4"]}}} - (#'backend.config/str-var->vector-var - config-component [:config :another-config :nested-config])))) + (testing "nested configs should be converted to vectors" + (is (match? {:config {:another-config {:nested-config ["value3" "value4"]}}} + (#'backend.config/str-var->vector-var + config-component [:config :another-config :nested-config])))) - (testing "trailing and extra whitespaces should be ignored" - (is (match? {:config {:malformed-config ["value5" "value6" "value7"]}} - (#'backend.config/str-var->vector-var - config-component [:config :malformed-config])))) + (testing "trailing and extra whitespaces should be ignored" + (is (match? {:config {:malformed-config ["value5" "value6" "value7"]}} + (#'backend.config/str-var->vector-var + config-component [:config :malformed-config])))) - (testing "trailing commas should be ignored" - (is (match? {:config {:trailing-comma ["value8" "value9"]}} - (#'backend.config/str-var->vector-var - config-component [:config :trailing-comma])))) + (testing "trailing commas should be ignored" + (is (match? {:config {:trailing-comma ["value8" "value9"]}} + (#'backend.config/str-var->vector-var + config-component [:config :trailing-comma])))) - (let [vector-env-vars [[:config :some-config] - [:config :another-config :nested-config] - [:config :malformed-config] - [:config :trailing-comma]] - no-matching-env-vars [[:config :non-existent-config]] - converted-configs {:some-config ["value1" "value2"], - :another-config {:nested-config ["value3" "value4"]} - :malformed-config ["value5" "value6" "value7"] - :trailing-comma ["value8" "value9"]} - unaltered-configs {:some-config "value1, value2", - :another-config {:nested-config "value3, value4"} - :malformed-config "value5, value6,value7, " - :trailing-comma "value8, value9,"}] + (let [vector-env-vars [[:config :some-config] + [:config :another-config :nested-config] + [:config :malformed-config] + [:config :trailing-comma]] + no-matching-env-vars [[:config :non-existent-config]] + converted-configs {:some-config ["value1" "value2"], + :another-config {:nested-config ["value3" "value4"]} + :malformed-config ["value5" "value6" "value7"] + :trailing-comma ["value8" "value9"]} + unaltered-configs {:some-config "value1, value2", + :another-config {:nested-config "value3, value4"} + :malformed-config "value5, value6,value7, " + :trailing-comma "value8, value9,"}] - (testing "all defined vector-env-vars should be processed" - (is (match? {:config (embeds converted-configs)} - (#'backend.config/resolved-envs-config config-component - vector-env-vars)))) + (testing "all defined vector-env-vars should be processed" + (is (match? {:config (embeds converted-configs)} + (#'backend.config/resolved-envs-config config-component + vector-env-vars)))) - (testing "when vector-env-vars is empty, the config should be left unaltered" - (is (match? {:config (embeds unaltered-configs)} - (#'backend.config/resolved-envs-config config-component - [])))) + (testing "when vector-env-vars is empty, the config should be left unaltered" + (is (match? {:config (embeds unaltered-configs)} + (#'backend.config/resolved-envs-config config-component + [])))) - (testing "when vector-env-vars has no matches, the config should be left unaltered" - (is (match? {:config (embeds unaltered-configs)} - (#'backend.config/resolved-envs-config config-component - no-matching-env-vars))))))) + (testing "when vector-env-vars has no matches, the config should be left unaltered" + (is (match? {:config (embeds unaltered-configs)} + (#'backend.config/resolved-envs-config config-component + no-matching-env-vars))))))) From c7509de3428303580848b44655f8d01c7c2cd2eb Mon Sep 17 00:00:00 2001 From: Karina Moreira Date: Fri, 5 Apr 2024 15:55:07 -0300 Subject: [PATCH 08/11] Refactor function --- src/codes/clj/docs/backend/config.clj | 10 ++++------ test/unit/codes/clj/docs/backend/config_test.clj | 10 ++++------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/codes/clj/docs/backend/config.clj b/src/codes/clj/docs/backend/config.clj index 253d0c4..d3b4693 100644 --- a/src/codes/clj/docs/backend/config.clj +++ b/src/codes/clj/docs/backend/config.clj @@ -21,10 +21,8 @@ (assoc-in config nested-keys env-config)) config))) -(defn- resolved-envs-config - [config str-envs] - (reduce str-var->vector-var config str-envs)) - (defn config - [config] - (resolved-envs-config config vector-env-vars)) + ([config] + (reduce str-var->vector-var config vector-env-vars)) + ([config vector-envs] + (reduce str-var->vector-var config vector-envs))) diff --git a/test/unit/codes/clj/docs/backend/config_test.clj b/test/unit/codes/clj/docs/backend/config_test.clj index e4a328d..5564171 100644 --- a/test/unit/codes/clj/docs/backend/config_test.clj +++ b/test/unit/codes/clj/docs/backend/config_test.clj @@ -59,15 +59,13 @@ (testing "all defined vector-env-vars should be processed" (is (match? {:config (embeds converted-configs)} - (#'backend.config/resolved-envs-config config-component - vector-env-vars)))) + (backend.config/config config-component vector-env-vars)))) (testing "when vector-env-vars is empty, the config should be left unaltered" (is (match? {:config (embeds unaltered-configs)} - (#'backend.config/resolved-envs-config config-component - [])))) + (backend.config/config config-component [])))) (testing "when vector-env-vars has no matches, the config should be left unaltered" (is (match? {:config (embeds unaltered-configs)} - (#'backend.config/resolved-envs-config config-component - no-matching-env-vars))))))) + (backend.config/config config-component + no-matching-env-vars))))))) From 6596c50bc0127f3f8fb84cc4bf6c9a32d8edfc7d Mon Sep 17 00:00:00 2001 From: Karina Moreira Date: Mon, 8 Apr 2024 15:45:43 -0300 Subject: [PATCH 09/11] Aero reader for csv --- resources/config.edn | 6 +++--- src/codes/clj/docs/backend/config.clj | 28 ++++++++++----------------- src/codes/clj/docs/backend/server.clj | 4 ++-- 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/resources/config.edn b/resources/config.edn index e213a73..a3a6943 100644 --- a/resources/config.edn +++ b/resources/config.edn @@ -1,7 +1,7 @@ {:webserver/port #long #or [#env PORT 3001] - :webserver/allowed-origins #or [#env ALLOWED_ORIGINS - ["http://docs.clj.codes" "https://docs.clj.codes" - "http://docs-frontend.fly.dev" "https://docs-frontend.fly.dev"]] + :webserver/allowed-origins #csv #or [#env ALLOWED_ORIGINS + ["http://docs.clj.codes" "https://docs.clj.codes" + "http://docs-frontend.fly.dev" "https://docs-frontend.fly.dev"]] :database {:dbtype "postgres" :dbname #or [#env DB_NAME "postgres"] diff --git a/src/codes/clj/docs/backend/config.clj b/src/codes/clj/docs/backend/config.clj index d3b4693..1b16a2c 100644 --- a/src/codes/clj/docs/backend/config.clj +++ b/src/codes/clj/docs/backend/config.clj @@ -1,28 +1,20 @@ (ns codes.clj.docs.backend.config - (:require [clojure.string :as string])) - -(def vector-env-vars - "Nested keys for config variables with corresponding environment - variables that can hold multiple values separated by commas, e.g.: - ALLOWED_ORIGINS=\"https://domain.a.com, https://domain.b.com\"" - [[:config :webserver/allowed-origins]]) + (:require [aero.core :as aero] + [clojure.string :as string])) (defn- str-var->vector-var "Converts a string config variable to a vector of strings, when applicable. Environment variables are expected to be set as comma-separated values." - [config nested-keys] - (let [target-config (get-in config nested-keys)] - (if (string? target-config) - (let [split-configs (-> target-config + [value] + (if (string? value) + (let [split-configs (-> value (string/split #",")) env-config (->> split-configs (map string/trim) (remove empty?))] - (assoc-in config nested-keys env-config)) - config))) + env-config) + value)) -(defn config - ([config] - (reduce str-var->vector-var config vector-env-vars)) - ([config vector-envs] - (reduce str-var->vector-var config vector-envs))) +(defmethod aero/reader 'csv + [_ _ value] + (str-var->vector-var value)) diff --git a/src/codes/clj/docs/backend/server.clj b/src/codes/clj/docs/backend/server.clj index 25ca6e5..e051e11 100644 --- a/src/codes/clj/docs/backend/server.clj +++ b/src/codes/clj/docs/backend/server.clj @@ -1,6 +1,6 @@ (ns codes.clj.docs.backend.server (:require [codes.clj.docs.backend.components.db-docs :as components.db-docs] - [codes.clj.docs.backend.config :as backend.config] + [codes.clj.docs.backend.config] [codes.clj.docs.backend.db.datalevin :refer [read-conn-opts]] [codes.clj.docs.backend.routes :as routes] [com.stuartsierra.component :as component] @@ -17,7 +17,7 @@ (defn base-system-map [] (component/system-map - :config (backend.config/config (config/new-config)) + :config (config/new-config) :http (http/new-http) :router (router/new-router routes/routes) :database (component/using (database/new-database) [:config]) From 70b67f625abc2867b5083f7a88924325954a7eda Mon Sep 17 00:00:00 2001 From: Karina Moreira Date: Mon, 8 Apr 2024 15:46:31 -0300 Subject: [PATCH 10/11] Update tests --- test/resources/csv-config.edn | 3 + .../codes/clj/docs/backend/config_test.clj | 83 +++++-------------- 2 files changed, 23 insertions(+), 63 deletions(-) create mode 100644 test/resources/csv-config.edn diff --git a/test/resources/csv-config.edn b/test/resources/csv-config.edn new file mode 100644 index 0000000..7c1b796 --- /dev/null +++ b/test/resources/csv-config.edn @@ -0,0 +1,3 @@ +{:some-config #csv #or [#env SOME "value1, value2"] + :malformed-config #csv #or [#env MALFORMED "value3, value4,value5, "] + :trailing-comma-config #csv #or [#env TRAILING_COMMA "value6, value7,"]} \ No newline at end of file diff --git a/test/unit/codes/clj/docs/backend/config_test.clj b/test/unit/codes/clj/docs/backend/config_test.clj index 5564171..77cadcd 100644 --- a/test/unit/codes/clj/docs/backend/config_test.clj +++ b/test/unit/codes/clj/docs/backend/config_test.clj @@ -1,71 +1,28 @@ (ns unit.codes.clj.docs.backend.config-test - (:require [clojure.test :refer [deftest is testing use-fixtures]] + (:require [aero.core :as aero] + [clojure.test :refer [deftest is testing use-fixtures]] [codes.clj.docs.backend.config :as backend.config] - [com.stuartsierra.component :as component] - [matcher-combinators.matchers :refer [embeds]] [matcher-combinators.test :refer [match?]] - [parenthesin.components.config.aero :as config.aero] [parenthesin.helpers.malli :as helpers.malli])) (use-fixtures :once helpers.malli/with-intrumentation) -(defn- create-and-start-system! - [{:keys [config]}] - (component/start-system - (component/system-map :config config))) - (deftest str-var->vector-var-test - (let [system (create-and-start-system! - {:config (config.aero/new-config {:some-config "value1, value2" - :another-config - {:nested-config "value3, value4"} - :malformed-config "value5, value6,value7, " - :trailing-comma "value8, value9,"})}) - config-component (:config system)] - - (testing "root-level configs should be converted to vectors" - (is (match? {:config {:some-config ["value1" "value2"]}} - (#'backend.config/str-var->vector-var - config-component [:config :some-config])))) - - (testing "nested configs should be converted to vectors" - (is (match? {:config {:another-config {:nested-config ["value3" "value4"]}}} - (#'backend.config/str-var->vector-var - config-component [:config :another-config :nested-config])))) - - (testing "trailing and extra whitespaces should be ignored" - (is (match? {:config {:malformed-config ["value5" "value6" "value7"]}} - (#'backend.config/str-var->vector-var - config-component [:config :malformed-config])))) - - (testing "trailing commas should be ignored" - (is (match? {:config {:trailing-comma ["value8" "value9"]}} - (#'backend.config/str-var->vector-var - config-component [:config :trailing-comma])))) - - (let [vector-env-vars [[:config :some-config] - [:config :another-config :nested-config] - [:config :malformed-config] - [:config :trailing-comma]] - no-matching-env-vars [[:config :non-existent-config]] - converted-configs {:some-config ["value1" "value2"], - :another-config {:nested-config ["value3" "value4"]} - :malformed-config ["value5" "value6" "value7"] - :trailing-comma ["value8" "value9"]} - unaltered-configs {:some-config "value1, value2", - :another-config {:nested-config "value3, value4"} - :malformed-config "value5, value6,value7, " - :trailing-comma "value8, value9,"}] - - (testing "all defined vector-env-vars should be processed" - (is (match? {:config (embeds converted-configs)} - (backend.config/config config-component vector-env-vars)))) - - (testing "when vector-env-vars is empty, the config should be left unaltered" - (is (match? {:config (embeds unaltered-configs)} - (backend.config/config config-component [])))) - - (testing "when vector-env-vars has no matches, the config should be left unaltered" - (is (match? {:config (embeds unaltered-configs)} - (backend.config/config config-component - no-matching-env-vars))))))) + (testing "root-level configs should be converted to vectors" + (is (match? ["value1" "value2"] + (#'backend.config/str-var->vector-var "value1, value2")))) + + (testing "trailing and extra whitespaces should be ignored" + (is (match? ["value3" "value4" "value5"] + (#'backend.config/str-var->vector-var "value3, value4,value5, ")))) + + (testing "trailing commas should be ignored" + (is (match? ["value6" "value7"] + (#'backend.config/str-var->vector-var "value6, value7,"))))) + +(deftest csv-reader-test + (testing "tag literal #csv should turn comma-separated strings into vectors" + (is (match? {:some-config ["value1" "value2"] + :malformed-config ["value3" "value4" "value5"] + :trailing-comma-config ["value6" "value7"]} + (aero/read-config "test/resources/csv-config.edn"))))) From 51464cea8581c4183e01d20d2ae2bb5bed86a579 Mon Sep 17 00:00:00 2001 From: Karina Moreira Date: Mon, 8 Apr 2024 15:49:20 -0300 Subject: [PATCH 11/11] Lint-fix, testing doc --- src/codes/clj/docs/backend/config.clj | 20 ++++++------- .../codes/clj/docs/backend/config_test.clj | 28 +++++++++---------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/codes/clj/docs/backend/config.clj b/src/codes/clj/docs/backend/config.clj index 1b16a2c..ca0a965 100644 --- a/src/codes/clj/docs/backend/config.clj +++ b/src/codes/clj/docs/backend/config.clj @@ -1,20 +1,20 @@ (ns codes.clj.docs.backend.config - (:require [aero.core :as aero] - [clojure.string :as string])) + (:require [aero.core :as aero] + [clojure.string :as string])) (defn- str-var->vector-var "Converts a string config variable to a vector of strings, when applicable. Environment variables are expected to be set as comma-separated values." [value] (if (string? value) - (let [split-configs (-> value - (string/split #",")) - env-config (->> split-configs - (map string/trim) - (remove empty?))] - env-config) - value)) + (let [split-configs (-> value + (string/split #",")) + env-config (->> split-configs + (map string/trim) + (remove empty?))] + env-config) + value)) -(defmethod aero/reader 'csv +(defmethod aero/reader 'csv [_ _ value] (str-var->vector-var value)) diff --git a/test/unit/codes/clj/docs/backend/config_test.clj b/test/unit/codes/clj/docs/backend/config_test.clj index 77cadcd..9cf4395 100644 --- a/test/unit/codes/clj/docs/backend/config_test.clj +++ b/test/unit/codes/clj/docs/backend/config_test.clj @@ -8,21 +8,21 @@ (use-fixtures :once helpers.malli/with-intrumentation) (deftest str-var->vector-var-test - (testing "root-level configs should be converted to vectors" - (is (match? ["value1" "value2"] - (#'backend.config/str-var->vector-var "value1, value2")))) + (testing "csv configs should be converted to vectors" + (is (match? ["value1" "value2"] + (#'backend.config/str-var->vector-var "value1, value2")))) - (testing "trailing and extra whitespaces should be ignored" - (is (match? ["value3" "value4" "value5"] - (#'backend.config/str-var->vector-var "value3, value4,value5, ")))) + (testing "trailing and extra whitespaces should be ignored" + (is (match? ["value3" "value4" "value5"] + (#'backend.config/str-var->vector-var "value3, value4,value5, ")))) - (testing "trailing commas should be ignored" - (is (match? ["value6" "value7"] - (#'backend.config/str-var->vector-var "value6, value7,"))))) + (testing "trailing commas should be ignored" + (is (match? ["value6" "value7"] + (#'backend.config/str-var->vector-var "value6, value7,"))))) (deftest csv-reader-test - (testing "tag literal #csv should turn comma-separated strings into vectors" - (is (match? {:some-config ["value1" "value2"] - :malformed-config ["value3" "value4" "value5"] - :trailing-comma-config ["value6" "value7"]} - (aero/read-config "test/resources/csv-config.edn"))))) + (testing "tag literal #csv should turn comma-separated strings into vectors" + (is (match? {:some-config ["value1" "value2"] + :malformed-config ["value3" "value4" "value5"] + :trailing-comma-config ["value6" "value7"]} + (aero/read-config "test/resources/csv-config.edn")))))