Skip to content

Commit

Permalink
feat(cli): implement list --shared argument (#692)
Browse files Browse the repository at this point in the history
Adds new `--shared`, `--shared-by`, and `--shared-with` arguments to
the `list` CLI command to display workflows along with their sharing
information.

Closes #687
  • Loading branch information
DaanRosendal committed Mar 18, 2024
1 parent d0d935d commit a90b056
Show file tree
Hide file tree
Showing 3 changed files with 224 additions and 4 deletions.
9 changes: 9 additions & 0 deletions reana_client/api/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ def get_workflows(
include_progress=None,
include_workspace_size=None,
workflow=None,
shared=None,
shared_by=None,
shared_with=None,
):
"""List all existing workflows.
Expand All @@ -130,6 +133,9 @@ def get_workflows(
:param include_progress: include progress information in the response.
:param include_workspace_size: include workspace size information in the response.
:param workflow: name or id of the workflow.
:param shared: list all shared (owned and unowned) workflows.
:param shared_by: list workflows shared by the specified user(s).
:param shared_with: list workflows shared with the specified user(s).
:return: a list of dictionaries with the information about the workflows.
The information includes the workflow ``name``, ``id``, ``status``, ``size``,
Expand All @@ -148,6 +154,9 @@ def get_workflows(
include_progress=include_progress,
include_workspace_size=include_workspace_size,
workflow_id_or_name=workflow,
shared=shared,
shared_by=shared_by,
shared_with=shared_with,
).result()
if http_response.status_code == 200:
return response.get("items")
Expand Down
70 changes: 66 additions & 4 deletions reana_client/cli/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,25 @@ def workflow_sharing_group(ctx):
default=False,
help="Include deleted workflows in the output.",
)
@click.option(
"--shared",
"shared",
is_flag=True,
default=False,
help="List all shared (owned and unowned) workflows.",
)
@click.option(
"--shared-by",
"shared_by",
default=None,
help="List workflows shared by the specified user(s).",
)
@click.option(
"--shared-with",
"shared_with",
default=None,
help="List workflows shared with the specified user(s).",
)
@add_access_token_options
@add_pagination_options
@check_connection
Expand All @@ -179,21 +198,42 @@ def workflows_list( # noqa: C901
include_progress,
include_workspace_size,
show_deleted_runs: bool,
shared,
shared_by,
shared_with,
): # noqa: D301
"""List all workflows and sessions.
The ``list`` command lists workflows and sessions. By default, the list of
workflows is returned. If you would like to see the list of your open
interactive sessions, you need to pass the ``--sessions`` command-line
option.
option. If you would like to see the list of all workflows, including those
shared with you, you need to pass the ``--shared`` command-line option.
Example:\n
Along with specific user emails, you can pass the following special values
to the ``--shared-by`` and ``--shared-with`` command-line options:\n
\t - ``--shared-by anybody``: list workflows shared with you by anybody.\n
\t - ``--shared-with anybody``: list your shared workflows exclusively.\n
\t - ``--shared-with nobody``: list your unshared workflows exclusively.\n
\t - ``--shared-with [email protected],[email protected]``: list workflows shared with either [email protected] or [email protected]
Examples:\n
\t $ reana-client list --all\n
\t $ reana-client list --sessions\n
\t $ reana-client list --verbose --bytes
\t $ reana-client list --verbose --bytes\n
\t $ reana-client list --shared\n
\t $ reana-client list --shared-by [email protected]\n
\t $ reana-client list --shared-with anybody
"""
from reana_client.api.client import get_workflows

if shared_by and shared_with:
display_message(
"Please provide either --shared-by or --shared-with, not both.",
msg_type="error",
)
sys.exit(1)

logging.debug("command: {}".format(ctx.command_path.replace(" ", ".")))
for p in ctx.params:
logging.debug("{param}: {value}".format(param=p, value=ctx.params[p]))
Expand Down Expand Up @@ -224,13 +264,23 @@ def workflows_list( # noqa: C901
include_progress=include_progress,
include_workspace_size=include_workspace_size,
workflow=workflow,
shared=shared,
shared_by=shared_by,
shared_with=shared_with,
)
verbose_headers = ["id", "user"]
workspace_size_header = ["size"]
progress_header = ["progress"]
duration_header = ["duration"]
headers = {
"batch": ["name", "run_number", "created", "started", "ended", "status"],
"batch": [
"name",
"run_number",
"created",
"started",
"ended",
"status",
],
"interactive": [
"name",
"run_number",
Expand All @@ -249,6 +299,14 @@ def workflows_list( # noqa: C901
if verbose or include_duration:
headers[type] += duration_header

if shared:
headers[type] += ["shared_with", "shared_by"]
else:
if shared_with:
headers[type] += ["shared_with"]
if shared_by:
headers[type] += ["shared_by"]

data = []
for workflow in response:
name, run_number = get_workflow_name_and_run_number(workflow["name"])
Expand All @@ -271,6 +329,10 @@ def workflows_list( # noqa: C901
"run_started_at" if header == "started" else "run_finished_at"
)
value = workflow.get("progress", {}).get(_key) or "-"
if header == "shared_by":
value = workflow.get("owner_email")
if header == "shared_with":
value = workflow.get("shared_with")
if not value:
value = workflow.get(header)
row.append(value)
Expand Down
149 changes: 149 additions & 0 deletions tests/test_cli_workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,155 @@ def test_workflows_filter():
assert "running" in json_response[0]["status"]


def test_workflows_shared():
"""Test workflow list command with --shared flag."""
response = {
"items": [
{
"status": "running",
"created": "2018-06-13T09:47:35.66097",
"user": "00000000-0000-0000-0000-000000000000",
"name": "mytest.1",
"id": "256b25f4-4cfb-4684-b7a8-73872ef455a1",
"size": {"raw": 0, "human_readable": "0 Bytes"},
"progress": {},
}
]
}
status_code = 200
mock_http_response, mock_response = Mock(), Mock()
mock_http_response.status_code = status_code
mock_response = response
env = {"REANA_SERVER_URL": "localhost"}
reana_token = "000000"
runner = CliRunner(env=env)
with runner.isolation():
with patch(
"reana_client.api.client.current_rs_api_client",
make_mock_api_client("reana-server")(mock_response, mock_http_response),
):
result = runner.invoke(cli, ["list", "--shared", "-t", reana_token])
assert result.exit_code == 0
assert "SHARED_WITH" in result.output
assert "SHARED_BY" in result.output


def test_workflows_shared_with():
"""Test workflow list command with --shared-with flag."""
response = {
"items": [
{
"status": "running",
"created": "2018-06-13T09:47:35.66097",
"user": "00000000-0000-0000-0000-000000000000",
"name": "mytest.1",
"id": "256b25f4-4cfb-4684-b7a8-73872ef455a1",
"size": {"raw": 0, "human_readable": "0 Bytes"},
"progress": {},
}
]
}
status_code = 200
mock_http_response, mock_response = Mock(), Mock()
mock_http_response.status_code = status_code
mock_response = response
env = {"REANA_SERVER_URL": "localhost"}
reana_token = "000000"
runner = CliRunner(env=env)
with runner.isolation():
with patch(
"reana_client.api.client.current_rs_api_client",
make_mock_api_client("reana-server")(mock_response, mock_http_response),
):
result = runner.invoke(
cli, ["list", "--shared-with", "anybody", "-t", reana_token]
)
assert result.exit_code == 0
assert "SHARED_WITH" in result.output
assert "SHARED_BY" not in result.output


def test_workflows_shared_by():
"""Test workflow list command with --shared-by flag."""
response = {
"items": [
{
"status": "running",
"created": "2018-06-13T09:47:35.66097",
"user": "00000000-0000-0000-0000-000000000000",
"name": "mytest.1",
"id": "256b25f4-4cfb-4684-b7a8-73872ef455a1",
"size": {"raw": 0, "human_readable": "0 Bytes"},
"progress": {},
}
]
}
status_code = 200
mock_http_response, mock_response = Mock(), Mock()
mock_http_response.status_code = status_code
mock_response = response
env = {"REANA_SERVER_URL": "localhost"}
reana_token = "000000"
runner = CliRunner(env=env)
with runner.isolation():
with patch(
"reana_client.api.client.current_rs_api_client",
make_mock_api_client("reana-server")(mock_response, mock_http_response),
):
result = runner.invoke(
cli, ["list", "--shared-by", "anybody", "-t", reana_token]
)
assert result.exit_code == 0
assert "SHARED_WITH" not in result.output
assert "SHARED_BY" in result.output


def test_workflows_shared_with_and_shared_by():
"""Test workflow list command with --shared-with and --shared-by flags."""
response = {
"items": [
{
"status": "running",
"created": "2018-06-13T09:47:35.66097",
"user": "00000000-0000-0000-0000-000000000000",
"name": "mytest.1",
"id": "256b25f4-4cfb-4684-b7a8-73872ef455a1",
"size": {"raw": 0, "human_readable": "0 Bytes"},
"progress": {},
}
]
}
status_code = 200
mock_http_response, mock_response = Mock(), Mock()
mock_http_response.status_code = status_code
mock_response = response
env = {"REANA_SERVER_URL": "localhost"}
reana_token = "000000"
runner = CliRunner(env=env)
with runner.isolation():
with patch(
"reana_client.api.client.current_rs_api_client",
make_mock_api_client("reana-server")(mock_response, mock_http_response),
):
result = runner.invoke(
cli,
[
"list",
"--shared-with",
"anybody",
"--shared-by",
"anybody",
"-t",
reana_token,
],
)
assert result.exit_code == 1
assert (
"Please provide either --shared-by or --shared-with, not both"
in result.output
)


def test_workflow_create_failed():
"""Test workflow create when creation fails."""
env = {"REANA_SERVER_URL": "localhost"}
Expand Down

0 comments on commit a90b056

Please sign in to comment.