From 1dac093be7b37b6922aca80fc581f6d7a779a5bb Mon Sep 17 00:00:00 2001 From: Ivan Habunek Date: Thu, 14 Dec 2023 14:10:53 +0100 Subject: [PATCH] Add --json option to lists commands --- CHANGELOG.md | 3 +- changelog.yaml | 3 +- docs/changelog.md | 3 +- tests/integration/test_lists.py | 94 ++++++++++++++++++++++++++--- tests/integration/test_timelines.py | 2 +- toot/api.py | 4 +- toot/cli/lists.py | 68 +++++++++++++++------ 7 files changed, 142 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6894551..032d844 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,6 @@ noted below please report any issues. * Enable passing params via environment variables, see: https://toot.bezdomni.net/environment_variables.html * Add shell completion, see: https://toot.bezdomni.net/shell_completion.html -* Add `--json` option to tag commands * Add `tags info`, `tags featured`, `tags feature`, and `tags unfeature` commands * Add `tags followed`, `tags follow`, and `tags unfollow` sub-commands, @@ -27,6 +26,8 @@ noted below please report any issues. * Add `lists accounts`, `lists add`, `lists create`, `lists delete`, `lists list`, `lists remove` subcommands, deprecate `lists`, `lists_accounts`, `lists_add`, `lists_create`, `lists_delete`, `lists_remove` commands. +* Add `--json` option to tags commands +* Add `--json` option to lists commands * Add `toot --width` option for setting your prefered terminal width * Add `--media-viewer` and `--colors` options to `toot tui`. These were previously accessible only via settings. diff --git a/changelog.yaml b/changelog.yaml index cf25955..2d48e53 100644 --- a/changelog.yaml +++ b/changelog.yaml @@ -11,10 +11,11 @@ - "BREAKING: Options `--debug`, `--color`, `--quiet` must be specified after `toot` but before the command" - "Enable passing params via environment variables, see: https://toot.bezdomni.net/environment_variables.html" - "Add shell completion, see: https://toot.bezdomni.net/shell_completion.html" - - "Add `--json` option to tag commands" - "Add `tags info`, `tags featured`, `tags feature`, and `tags unfeature` commands" - "Add `tags followed`, `tags follow`, and `tags unfollow` sub-commands, deprecate `tags_followed`, `tags_follow`, and `tags tags_unfollow`" - "Add `lists accounts`, `lists add`, `lists create`, `lists delete`, `lists list`, `lists remove` subcommands, deprecate `lists`, `lists_accounts`, `lists_add`, `lists_create`, `lists_delete`, `lists_remove` commands." + - "Add `--json` option to tags commands" + - "Add `--json` option to lists commands" - "Add `toot --width` option for setting your prefered terminal width" - "Add `--media-viewer` and `--colors` options to `toot tui`. These were previously accessible only via settings." diff --git a/docs/changelog.md b/docs/changelog.md index 6894551..032d844 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -19,7 +19,6 @@ noted below please report any issues. * Enable passing params via environment variables, see: https://toot.bezdomni.net/environment_variables.html * Add shell completion, see: https://toot.bezdomni.net/shell_completion.html -* Add `--json` option to tag commands * Add `tags info`, `tags featured`, `tags feature`, and `tags unfeature` commands * Add `tags followed`, `tags follow`, and `tags unfollow` sub-commands, @@ -27,6 +26,8 @@ noted below please report any issues. * Add `lists accounts`, `lists add`, `lists create`, `lists delete`, `lists list`, `lists remove` subcommands, deprecate `lists`, `lists_accounts`, `lists_add`, `lists_create`, `lists_delete`, `lists_remove` commands. +* Add `--json` option to tags commands +* Add `--json` option to lists commands * Add `toot --width` option for setting your prefered terminal width * Add `--media-viewer` and `--colors` options to `toot tui`. These were previously accessible only via settings. diff --git a/tests/integration/test_lists.py b/tests/integration/test_lists.py index 21b3f0b..979724f 100644 --- a/tests/integration/test_lists.py +++ b/tests/integration/test_lists.py @@ -1,3 +1,4 @@ +from uuid import uuid4 from toot import cli from tests.integration.conftest import register_account @@ -9,6 +10,11 @@ def test_lists_empty(run): assert result.stdout.strip() == "You have no lists defined." +def test_lists_empty_json(run_json): + lists = run_json(cli.lists.list, "--json") + assert lists == [] + + def test_list_create_delete(run): result = run(cli.lists.create, "banana") assert result.exit_code == 0 @@ -49,26 +55,62 @@ def test_list_create_delete(run): assert result.stderr.strip() == "Error: List not found" -def test_list_add_remove(run, app): - acc = register_account(app) - run(cli.lists.create, "foo") +def test_list_create_delete_json(run, run_json): + result = run_json(cli.lists.list, "--json") + assert result == [] - result = run(cli.lists.add, "foo", acc.username) + list = run_json(cli.lists.create, "banana", "--json") + assert list["title"] == "banana" + + [list] = run_json(cli.lists.list, "--json") + assert list["title"] == "banana" + + list = run_json(cli.lists.create, "mango", "--json") + assert list["title"] == "mango" + + lists = run_json(cli.lists.list, "--json") + [list1, list2] = sorted(lists, key=lambda l: l["title"]) + assert list1["title"] == "banana" + assert list2["title"] == "mango" + + result = run_json(cli.lists.delete, "banana", "--json") + assert result == {} + + [list] = run_json(cli.lists.list, "--json") + assert list["title"] == "mango" + + result = run_json(cli.lists.delete, "mango", "--json") + assert result == {} + + result = run_json(cli.lists.list, "--json") + assert result == [] + + result = run(cli.lists.delete, "mango", "--json") + assert result.exit_code == 1 + assert result.stderr.strip() == "Error: List not found" + + +def test_list_add_remove(run, app): + list_name = str(uuid4()) + acc = register_account(app) + run(cli.lists.create, list_name) + + result = run(cli.lists.add, list_name, acc.username) assert result.exit_code == 1 assert result.stderr.strip() == f"Error: You must follow @{acc.username} before adding this account to a list." run(cli.accounts.follow, acc.username) - result = run(cli.lists.add, "foo", acc.username) + result = run(cli.lists.add, list_name, acc.username) assert result.exit_code == 0 assert result.stdout.strip() == f'✓ Added account "{acc.username}"' - result = run(cli.lists.accounts, "foo") + result = run(cli.lists.accounts, list_name) assert result.exit_code == 0 assert acc.username in result.stdout # Account doesn't exist - result = run(cli.lists.add, "foo", "does_not_exist") + result = run(cli.lists.add, list_name, "does_not_exist") assert result.exit_code == 1 assert result.stderr.strip() == "Error: Account not found" @@ -77,10 +119,44 @@ def test_list_add_remove(run, app): assert result.exit_code == 1 assert result.stderr.strip() == "Error: List not found" - result = run(cli.lists.remove, "foo", acc.username) + result = run(cli.lists.remove, list_name, acc.username) assert result.exit_code == 0 assert result.stdout.strip() == f'✓ Removed account "{acc.username}"' - result = run(cli.lists.accounts, "foo") + result = run(cli.lists.accounts, list_name) assert result.exit_code == 0 assert result.stdout.strip() == "This list has no accounts." + + +def test_list_add_remove_json(run, run_json, app): + list_name = str(uuid4()) + acc = register_account(app) + run(cli.lists.create, list_name) + + result = run(cli.lists.add, list_name, acc.username, "--json") + assert result.exit_code == 1 + assert result.stderr.strip() == f"Error: You must follow @{acc.username} before adding this account to a list." + + run(cli.accounts.follow, acc.username) + + result = run_json(cli.lists.add, list_name, acc.username, "--json") + assert result == {} + + [account] = run_json(cli.lists.accounts, list_name, "--json") + assert account["username"] == acc.username + + # Account doesn't exist + result = run(cli.lists.add, list_name, "does_not_exist", "--json") + assert result.exit_code == 1 + assert result.stderr.strip() == "Error: Account not found" + + # List doesn't exist + result = run(cli.lists.add, "does_not_exist", acc.username, "--json") + assert result.exit_code == 1 + assert result.stderr.strip() == "Error: List not found" + + result = run_json(cli.lists.remove, list_name, acc.username, "--json") + assert result == {} + + result = run_json(cli.lists.accounts, list_name, "--json") + assert result == [] diff --git a/tests/integration/test_timelines.py b/tests/integration/test_timelines.py index 8823f69..d4cebbd 100644 --- a/tests/integration/test_timelines.py +++ b/tests/integration/test_timelines.py @@ -30,7 +30,7 @@ def friend_user(app, user): @pytest.fixture(scope="module") def friend_list(app, user, friend_user): friend_account = api.find_account(app, user, friend_user.username) - list = api.create_list(app, user, str(uuid4())) + list = api.create_list(app, user, str(uuid4())).json() api.add_accounts_to_list(app, user, list["id"], account_ids=[friend_account["id"]]) return list diff --git a/toot/api.py b/toot/api.py index c6aa91a..5a1e021 100644 --- a/toot/api.py +++ b/toot/api.py @@ -639,7 +639,7 @@ def create_list(app, user, title, replies_policy="none"): json = {'title': title} if replies_policy: json['replies_policy'] = replies_policy - return http.post(app, user, url, json=json).json() + return http.post(app, user, url, json=json) def delete_list(app, user, id): @@ -649,7 +649,7 @@ def delete_list(app, user, id): def add_accounts_to_list(app, user, list_id, account_ids): url = f"/api/v1/lists/{list_id}/accounts" json = {'account_ids': account_ids} - return http.post(app, user, url, json=json).json() + return http.post(app, user, url, json=json) def remove_accounts_from_list(app, user, list_id, account_ids): diff --git a/toot/cli/lists.py b/toot/cli/lists.py index a7a8fcc..8bf39fb 100644 --- a/toot/cli/lists.py +++ b/toot/cli/lists.py @@ -1,7 +1,8 @@ import click +import json as pyjson from toot import api, config -from toot.cli import Context, cli, pass_context +from toot.cli import Context, cli, pass_context, json_option from toot.output import print_list_accounts, print_lists, print_warning @@ -24,26 +25,35 @@ def lists(ctx: click.Context): @lists.command() +@json_option @pass_context -def list(ctx: Context): +def list(ctx: Context, json: bool): """List all your lists""" lists = api.get_lists(ctx.app, ctx.user) - if lists: - print_lists(lists) + if json: + click.echo(pyjson.dumps(lists)) else: - click.echo("You have no lists defined.") + if lists: + print_lists(lists) + else: + click.echo("You have no lists defined.") @lists.command() @click.argument("title", required=False) @click.option("--id", help="List ID if not title is given") +@json_option @pass_context -def accounts(ctx: Context, title: str, id: str): +def accounts(ctx: Context, title: str, id: str, json: bool): """List the accounts in a list""" list_id = _get_list_id(ctx, title, id) response = api.get_list_accounts(ctx.app, ctx.user, list_id) - print_list_accounts(response) + + if json: + click.echo(pyjson.dumps(response)) + else: + print_list_accounts(response) @lists.command() @@ -54,36 +64,49 @@ def accounts(ctx: Context, title: str, id: str): default="none", help="Replies policy" ) +@json_option @pass_context -def create(ctx: Context, title: str, replies_policy: str): +def create(ctx: Context, title: str, replies_policy: str, json: bool): """Create a list""" - api.create_list(ctx.app, ctx.user, title=title, replies_policy=replies_policy) - click.secho(f"✓ List \"{title}\" created.", fg="green") + response = api.create_list(ctx.app, ctx.user, title=title, replies_policy=replies_policy) + if json: + print(response.text) + else: + click.secho(f"✓ List \"{title}\" created.", fg="green") @lists.command() @click.argument("title", required=False) @click.option("--id", help="List ID if not title is given") +@json_option @pass_context -def delete(ctx: Context, title: str, id: str): +def delete(ctx: Context, title: str, id: str, json: bool): """Delete a list""" list_id = _get_list_id(ctx, title, id) - api.delete_list(ctx.app, ctx.user, list_id) - click.secho(f"✓ List \"{title if title else id}\" deleted.", fg="green") + response = api.delete_list(ctx.app, ctx.user, list_id) + if json: + click.echo(response.text) + else: + click.secho(f"✓ List \"{title if title else id}\" deleted.", fg="green") @lists.command() @click.argument("title", required=False) @click.argument("account") @click.option("--id", help="List ID if not title is given") +@json_option @pass_context -def add(ctx: Context, title: str, account: str, id: str): +def add(ctx: Context, title: str, account: str, id: str, json: bool): """Add an account to a list""" list_id = _get_list_id(ctx, title, id) found_account = api.find_account(ctx.app, ctx.user, account) try: - api.add_accounts_to_list(ctx.app, ctx.user, list_id, [found_account["id"]]) + response = api.add_accounts_to_list(ctx.app, ctx.user, list_id, [found_account["id"]]) + if json: + click.echo(response.text) + else: + click.secho(f"✓ Added account \"{account}\"", fg="green") except Exception: # TODO: this is slow, improve # if we failed to add the account, try to give a @@ -99,20 +122,25 @@ def add(ctx: Context, title: str, account: str, id: str): raise click.ClickException(f"You must follow @{account} before adding this account to a list.") raise - click.secho(f"✓ Added account \"{account}\"", fg="green") - @lists.command() @click.argument("title", required=False) @click.argument("account") @click.option("--id", help="List ID if not title is given") +@json_option @pass_context -def remove(ctx: Context, title: str, account: str, id: str): +def remove(ctx: Context, title: str, account: str, id: str, json: bool): """Remove an account from a list""" list_id = _get_list_id(ctx, title, id) found_account = api.find_account(ctx.app, ctx.user, account) - api.remove_accounts_from_list(ctx.app, ctx.user, list_id, [found_account["id"]]) - click.secho(f"✓ Removed account \"{account}\"", fg="green") + response = api.remove_accounts_from_list(ctx.app, ctx.user, list_id, [found_account["id"]]) + if json: + click.echo(response.text) + else: + click.secho(f"✓ Removed account \"{account}\"", fg="green") + + +# -- Deprecated commands ------------------------------------------------------- @cli.command(name="list_accounts", hidden=True)