summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--firefly_cli/config.py35
-rw-r--r--tests/unit/test_config.py35
2 files changed, 70 insertions, 0 deletions
diff --git a/firefly_cli/config.py b/firefly_cli/config.py
new file mode 100644
index 0000000..eb1c460
--- /dev/null
+++ b/firefly_cli/config.py
@@ -0,0 +1,35 @@
+# Copyright (C) 2026 Danilo M. <danix@danix.xyz> GPL-2.0-only
+import os
+import tomllib
+from pathlib import Path
+from firefly_cli.errors import ConfigError
+
+DEFAULT_PATH = Path(os.path.expanduser("~/.config/firefly-cli/config.toml"))
+
+def load(path=DEFAULT_PATH, env=None):
+ env = os.environ if env is None else env
+ url = env.get("FIREFLY_URL")
+ token = env.get("FIREFLY_TOKEN")
+ if not (url and token) and Path(path).exists():
+ with open(path, "rb") as fh:
+ data = tomllib.load(fh)
+ url = url or data.get("url")
+ token = token or data.get("token")
+ if not url or not token:
+ raise ConfigError(
+ "No Firefly III config found. Run `firefly auth set` "
+ "or set FIREFLY_URL and FIREFLY_TOKEN."
+ )
+ return {"url": url.rstrip("/"), "token": token}
+
+def write(url, token, path=DEFAULT_PATH):
+ path = Path(path)
+ path.parent.mkdir(parents=True, exist_ok=True)
+ # tomllib cannot write; template the 2-key file (deps stay at zero).
+ content = (
+ f'url = "{url.rstrip("/")}"\n'
+ f'token = "{token}"\n'
+ )
+ path.write_text(content)
+ path.chmod(0o600)
+ return path
diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py
new file mode 100644
index 0000000..de90a3b
--- /dev/null
+++ b/tests/unit/test_config.py
@@ -0,0 +1,35 @@
+import os, tempfile, unittest
+from pathlib import Path
+from firefly_cli import config
+from firefly_cli.errors import ConfigError
+
+class TestConfig(unittest.TestCase):
+ def setUp(self):
+ self.dir = tempfile.TemporaryDirectory()
+ self.path = Path(self.dir.name) / "config.toml"
+ def tearDown(self):
+ self.dir.cleanup()
+ for k in ("FIREFLY_URL", "FIREFLY_TOKEN"):
+ os.environ.pop(k, None)
+
+ def test_write_then_read_roundtrip(self):
+ config.write("https://f.example/", "tok123", path=self.path)
+ cfg = config.load(path=self.path, env={})
+ self.assertEqual(cfg["url"], "https://f.example") # trailing slash trimmed
+ self.assertEqual(cfg["token"], "tok123")
+
+ def test_env_overrides_file(self):
+ config.write("https://file/", "filetok", path=self.path)
+ cfg = config.load(path=self.path,
+ env={"FIREFLY_URL": "https://env", "FIREFLY_TOKEN": "envtok"})
+ self.assertEqual(cfg["url"], "https://env")
+ self.assertEqual(cfg["token"], "envtok")
+
+ def test_missing_everything_raises_configerror(self):
+ with self.assertRaises(ConfigError):
+ config.load(path=self.path, env={})
+
+ def test_env_only_no_file(self):
+ cfg = config.load(path=self.path,
+ env={"FIREFLY_URL": "https://env/", "FIREFLY_TOKEN": "t"})
+ self.assertEqual(cfg["url"], "https://env")