diff options
| -rw-r--r-- | firefly_cli/commands/__init__.py | 3 | ||||
| -rw-r--r-- | firefly_cli/commands/account.py | 28 | ||||
| -rw-r--r-- | firefly_cli/commands/auth.py | 21 | ||||
| -rw-r--r-- | firefly_cli/commands/category.py | 8 | ||||
| -rw-r--r-- | firefly_cli/commands/tag.py | 8 | ||||
| -rw-r--r-- | tests/unit/test_commands_account.py | 27 |
6 files changed, 95 insertions, 0 deletions
diff --git a/firefly_cli/commands/__init__.py b/firefly_cli/commands/__init__.py new file mode 100644 index 0000000..67cab2e --- /dev/null +++ b/firefly_cli/commands/__init__.py @@ -0,0 +1,3 @@ +# Copyright (C) 2026 Danilo M. <danix@danix.xyz> GPL-2.0-only +# Importing each module runs its @registry.command decorators. +from firefly_cli.commands import auth, account, category, tag # noqa: F401 diff --git a/firefly_cli/commands/account.py b/firefly_cli/commands/account.py new file mode 100644 index 0000000..fa2dc65 --- /dev/null +++ b/firefly_cli/commands/account.py @@ -0,0 +1,28 @@ +# Copyright (C) 2026 Danilo M. <danix@danix.xyz> GPL-2.0-only +from firefly_cli import registry, output + +def _list_args(p): + p.add_argument("--type", help="filter: asset, expense, revenue, liability, ...") + +@registry.command("account list", help="list accounts", args=_list_args) +def cmd_list(args, ctx): + params = {"type": args.type} if getattr(args, "type", None) else None + resp = ctx.client.request("GET", "/api/v1/accounts", params=params) + output.emit(output.unwrap(resp), human=ctx.human) + return 0 + +def _name_arg(p): + p.add_argument("account", help="account name or id") + +@registry.command("account get", help="show one account", args=_name_arg) +def cmd_get(args, ctx): + acc = ctx.resolver.account(args.account) + output.emit(acc, human=ctx.human) + return 0 + +@registry.command("account balance", help="show account balance", args=_name_arg) +def cmd_balance(args, ctx): + acc = ctx.resolver.account(args.account) + output.emit({"id": acc["id"], "name": acc.get("name"), + "current_balance": acc.get("current_balance")}, human=ctx.human) + return 0 diff --git a/firefly_cli/commands/auth.py b/firefly_cli/commands/auth.py new file mode 100644 index 0000000..d2319e9 --- /dev/null +++ b/firefly_cli/commands/auth.py @@ -0,0 +1,21 @@ +# Copyright (C) 2026 Danilo M. <danix@danix.xyz> GPL-2.0-only +import getpass +from firefly_cli import registry, output, config + +def _set_args(p): + p.add_argument("--url", help="Firefly III base URL") + p.add_argument("--token", help="Personal Access Token") + +@registry.command("auth set", help="write url+token to config", args=_set_args) +def cmd_set(args, ctx): + url = args.url or input("Firefly III URL: ").strip() + token = args.token or getpass.getpass("Personal Access Token: ").strip() + path = config.write(url, token) + output.emit({"written": str(path)}, human=ctx.human) + return 0 + +@registry.command("auth test", help="verify connectivity and token") +def cmd_test(args, ctx): + resp = ctx.client.request("GET", "/api/v1/about") + output.emit(resp.get("data", resp), human=ctx.human) + return 0 diff --git a/firefly_cli/commands/category.py b/firefly_cli/commands/category.py new file mode 100644 index 0000000..a7ddbe8 --- /dev/null +++ b/firefly_cli/commands/category.py @@ -0,0 +1,8 @@ +# Copyright (C) 2026 Danilo M. <danix@danix.xyz> GPL-2.0-only +from firefly_cli import registry, output + +@registry.command("category list", help="list categories") +def cmd_list(args, ctx): + resp = ctx.client.request("GET", "/api/v1/categories") + output.emit(output.unwrap(resp), human=ctx.human) + return 0 diff --git a/firefly_cli/commands/tag.py b/firefly_cli/commands/tag.py new file mode 100644 index 0000000..668c4ae --- /dev/null +++ b/firefly_cli/commands/tag.py @@ -0,0 +1,8 @@ +# Copyright (C) 2026 Danilo M. <danix@danix.xyz> GPL-2.0-only +from firefly_cli import registry, output + +@registry.command("tag list", help="list tags") +def cmd_list(args, ctx): + resp = ctx.client.request("GET", "/api/v1/tags") + output.emit(output.unwrap(resp), human=ctx.human) + return 0 diff --git a/tests/unit/test_commands_account.py b/tests/unit/test_commands_account.py new file mode 100644 index 0000000..4d28802 --- /dev/null +++ b/tests/unit/test_commands_account.py @@ -0,0 +1,27 @@ +import unittest +from unittest.mock import MagicMock +from firefly_cli.commands import account as acct +from firefly_cli.context import Context + +def make_ctx(): + client = MagicMock() + resolver = MagicMock() + return Context(client=client, resolver=resolver, human=False), client, resolver + +class TestAccountCmd(unittest.TestCase): + def test_list_passes_type_filter(self): + ctx, client, _ = make_ctx() + client.request.return_value = {"data": []} + args = MagicMock(type="asset") + acct.cmd_list(args, ctx) + client.request.assert_called_once_with( + "GET", "/api/v1/accounts", params={"type": "asset"}) + + def test_balance_resolves_name_and_returns_balance(self): + ctx, client, resolver = make_ctx() + resolver.account.return_value = {"id": "3", "name": "Checking", + "current_balance": "100.00"} + args = MagicMock(account="Checking") + rc = acct.cmd_balance(args, ctx) + resolver.account.assert_called_once_with("Checking") + self.assertEqual(rc, 0) |
