diff --git a/config.yml b/config.yml index 0e4a279..17d157d 100644 --- a/config.yml +++ b/config.yml @@ -3,4 +3,4 @@ mqtt: client_id: "weather_portal_dev" user: "homeassistant" # password comes from env -jwt_key: apuOQZyML+8PixmVDP4nXU4M # loaded from env on prod +jwt_secret: apuOQZyML+8PixmVDP4nXU4M # loaded from env on prod diff --git a/src/config.gleam b/src/config.gleam index 2fecd45..9891a17 100644 --- a/src/config.gleam +++ b/src/config.gleam @@ -1,5 +1,10 @@ import envoy import glaml +import gleam/dict +import gleam/erlang/node +import gleam/io +import gleam/list +import gleam/option.{type Option, None, Some} import gleam/result import simplifile @@ -9,7 +14,7 @@ pub type Config { mqtt_user: String, mqtt_pw: String, mqtt_client_id: String, - jwt_key: String, + jwt_secret: String, ) } @@ -30,71 +35,59 @@ fn parse_yaml(contents: String) -> Result(glaml.Document, String) { } } -fn compile_config(doc: glaml.Document) -> Result(Config, String) { - let root = glaml.document_root(doc) +fn get_config_value( + root: glaml.Node, + yaml_key: Option(String), + env_var: Option(String), +) -> String { + let from_env = case env_var { + Some(var) -> envoy.get(var) + None -> Error(Nil) + } - // Extract values from YAML - use mqtt_host_result <- result.try( - glaml.select_sugar(root, "mqtt.host") - |> result.map_error(fn(_) { "mqtt.host not found in config.yml" }), - ) - use mqtt_user_result <- result.try( - glaml.select_sugar(root, "mqtt.user") - |> result.map_error(fn(_) { "mqtt.user not found in config.yml" }), - ) - use mqtt_client_id_result <- result.try( - glaml.select_sugar(root, "mqtt.client_id") - |> result.map_error(fn(_) { "mqtt.client_id not found in config.yml" }), - ) - use jwt_key_result <- result.try( - glaml.select_sugar(root, "jwt_key") - |> result.map_error(fn(_) { "jwt_key not found in config.yml" }), - ) - - // Extract strings from nodes - case - mqtt_host_result, - mqtt_user_result, - jwt_key_result, - mqtt_client_id_result - { - glaml.NodeStr(host), - glaml.NodeStr(user), - glaml.NodeStr(key), - glaml.NodeStr(client_id) - -> { - let yaml_config = - Config( - mqtt_host: host, - mqtt_user: user, - mqtt_pw: "placeholder", - mqtt_client_id: client_id, - jwt_key: key, - ) - - // Override with env vars - let final_config = - Config( - mqtt_host: envoy.get("MQTT_HOST") - |> result.unwrap(yaml_config.mqtt_host), - mqtt_user: envoy.get("MQTT_USER") - |> result.unwrap(yaml_config.mqtt_user), - mqtt_pw: envoy.get("MQTT_PW") - |> result.unwrap(yaml_config.mqtt_pw), - mqtt_client_id: envoy.get("MQTT_CLIENT_ID") - |> result.unwrap(yaml_config.mqtt_client_id), - jwt_key: envoy.get("JWT_KEY") - |> result.unwrap(yaml_config.jwt_key), - ) - - Ok(final_config) + case from_env { + Ok(val) -> val + Error(_) -> { + // Fall back to YAML if env var not found + case yaml_key { + Some(key) -> { + let assert Ok(node) = glaml.select_sugar(root, key) + let assert glaml.NodeStr(str) = node + str + } + None -> { + panic as { "Config value not found in environment or YAML" } + } + } } - _, _, _, _ -> Error("Config values must be strings in config.yml") } } -pub fn load_config() -> Result(Config, String) { +fn compile_config(doc: glaml.Document) -> Config { + let root = glaml.document_root(doc) + let get = fn(yaml_key: option.Option(String), env_var: option.Option(String)) -> String { + get_config_value(root, yaml_key, env_var) + } + + Config( + mqtt_host: get(Some("mqtt.host"), None), + mqtt_pw: get(None, Some("MQTT_PW")), + mqtt_user: get(Some("mqtt.user"), None), + jwt_secret: get(Some("jwt_secret"), Some("JWT_SECRET")), + mqtt_client_id: get(Some("mqtt.client_id"), Some("MQTT_CLIENT_ID")), + ) +} + +fn get_doc() -> Result(glaml.Document, String) { use file_content <- result.try(load_file()) use doc <- result.try(parse_yaml(file_content)) - compile_config(doc) + Ok(doc) +} + +pub fn load_config() -> Config { + let doc = get_doc() + case doc { + Ok(doc) -> compile_config(doc) + Error(err) -> panic as { "fail to load config: " <> err } + } } diff --git a/src/weather_portal.gleam b/src/weather_portal.gleam index 2888f43..629dc7c 100644 --- a/src/weather_portal.gleam +++ b/src/weather_portal.gleam @@ -5,18 +5,8 @@ import mqtt_dummy import sensors pub fn main() -> Nil { - case config.load_config() { - Ok(cfg) -> { - io.println("Config loaded successfully!") - io.println("MQTT Host: " <> cfg.mqtt_host) - io.println("MQTT User: " <> cfg.mqtt_user) - io.println("MQTT PW: " <> cfg.mqtt_pw) - io.println("JWT Key: " <> cfg.jwt_key) - } - Error(err) -> { - io.println("Failed to load config: " <> err) - } - } + let cfg = config.load_config() + io.println("Config loaded successfully!") let assert Ok(subject) = mqtt_dummy.start() let mailbox = mqtt_dummy.subscribe(subject) diff --git a/test/config_test.gleam b/test/config_test.gleam index 1ed224d..ea18727 100644 --- a/test/config_test.gleam +++ b/test/config_test.gleam @@ -1,4 +1,5 @@ import config +import envoy import gleeunit import gleeunit/should @@ -8,7 +9,8 @@ pub fn main() -> Nil { // gleeunit test functions end in `_test` pub fn file_read_test() { - let assert Ok(cfg) = config.load_config() + envoy.set("MQTT_PW", "TEST") + let cfg = config.load_config() cfg.mqtt_host |> should.equal("192.168.1.11") diff --git a/test/sensors_test.gleam b/test/sensors_test.gleam index fd4ce0e..f9f9d97 100644 --- a/test/sensors_test.gleam +++ b/test/sensors_test.gleam @@ -6,7 +6,6 @@ pub fn main() -> Nil { gleeunit.main() } -// gleeunit test functions end in `_test` pub fn file_read_test() { let reading = sensors.sensor_name(sensors.Temperature)