refactor config module

This commit is contained in:
Travis Shears 2026-03-24 11:50:04 +01:00
parent 113384b6ce
commit 86e40f90a1
Signed by: travisshears
GPG key ID: CB9BF1910F3F7469
5 changed files with 60 additions and 76 deletions

View file

@ -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

View file

@ -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 }
}
}

View file

@ -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)

View file

@ -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")

View file

@ -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)