forked from reanahub/reana-client
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(workflow): add share-add command (reanahub#692)
Adds a new command to the CLI to share a workflow with a user. Closes reanahub#680
- Loading branch information
1 parent
cd45ad5
commit f7b53e7
Showing
7 changed files
with
193 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,12 +15,8 @@ | |
import traceback | ||
|
||
import click | ||
from jsonschema.exceptions import ValidationError | ||
from reana_commons.config import INTERACTIVE_SESSION_TYPES, REANA_COMPUTE_BACKENDS | ||
from reana_commons.errors import REANAValidationError | ||
from reana_commons.validation.operational_options import validate_operational_options | ||
import yaml | ||
|
||
from jsonschema.exceptions import ValidationError | ||
from reana_client.cli.files import get_files, upload_files | ||
from reana_client.cli.utils import ( | ||
add_access_token_options, | ||
|
@@ -51,6 +47,9 @@ | |
validate_input_parameters, | ||
validate_workflow_name_parameter, | ||
) | ||
from reana_commons.config import INTERACTIVE_SESSION_TYPES, REANA_COMPUTE_BACKENDS | ||
from reana_commons.errors import REANAValidationError | ||
from reana_commons.validation.operational_options import validate_operational_options | ||
|
||
|
||
@click.group(help="Workflow management commands") | ||
|
@@ -67,6 +66,13 @@ def workflow_execution_group(ctx): | |
logging.debug(ctx.info_name) | ||
|
||
|
||
@click.group(help="Workflow sharing commands") | ||
@click.pass_context | ||
def workflow_sharing_group(ctx): | ||
"""Top level wrapper for workflow sharing.""" | ||
logging.debug(ctx.info_name) | ||
|
||
|
||
@workflow_management_group.command("list") | ||
@click.option( | ||
"-w", | ||
|
@@ -451,12 +457,12 @@ def workflow_start( | |
\t $ reana-client start -w myanalysis.42 -p sleeptime=10 -p myparam=4\n | ||
\t $ reana-client start -w myanalysis.42 -p myparam1=myvalue1 -o CACHE=off | ||
""" | ||
from reana_client.utils import get_api_url | ||
from reana_client.api.client import ( | ||
get_workflow_parameters, | ||
get_workflow_status, | ||
start_workflow, | ||
) | ||
from reana_client.utils import get_api_url | ||
|
||
def display_status(workflow: str, current_status: str): | ||
"""Display the current status of the workflow.""" | ||
|
@@ -586,12 +592,12 @@ def workflow_restart( | |
\t $ reana-client restart -w myanalysis.42 -o TARGET=gendata\n | ||
\t $ reana-client restart -w myanalysis.42 -o FROM=fitdata | ||
""" | ||
from reana_client.utils import get_api_url | ||
from reana_client.api.client import ( | ||
get_workflow_parameters, | ||
get_workflow_status, | ||
start_workflow, | ||
) | ||
from reana_client.utils import get_api_url | ||
|
||
logging.debug("command: {}".format(ctx.command_path.replace(" ", "."))) | ||
for p in ctx.params: | ||
|
@@ -1372,7 +1378,7 @@ def workflow_open_interactive_session( | |
Examples:\n | ||
\t $ reana-client open -w myanalysis.42 jupyter | ||
""" | ||
from reana_client.api.client import open_interactive_session, info | ||
from reana_client.api.client import info, open_interactive_session | ||
|
||
if workflow: | ||
try: | ||
|
@@ -1457,3 +1463,76 @@ def workflow_close_interactive_session(workflow, access_token): # noqa: D301 | |
sys.exit(1) | ||
else: | ||
display_message("Cannot find workflow {} ".format(workflow), msg_type="error") | ||
|
||
|
||
@workflow_sharing_group.command("share-add") | ||
@check_connection | ||
@add_workflow_option | ||
@add_access_token_options | ||
@click.option( | ||
"-u", | ||
"--user", | ||
"users", | ||
multiple=True, | ||
help="Users to share the workflow with.", | ||
required=True, | ||
) | ||
@click.option( | ||
"-m", | ||
"--message", | ||
help="Optional message that is sent to the user(s) with the sharing invitation.", | ||
) | ||
@click.option( | ||
"--valid-until", | ||
type=click.DateTime(formats=["%Y-%m-%d"]), | ||
help="Optional date when access to the workflow will expire for the given user(s) (format: YYYY-MM-DD).", | ||
) | ||
@click.pass_context | ||
def workflow_share_add( | ||
ctx, workflow, access_token, users, message, valid_until | ||
): # noqa D412 | ||
"""Share a workflow with other users (read-only). | ||
The `share-add` command allows sharing a workflow with other users. The | ||
users will be able to view the workflow but not modify it. | ||
Examples: | ||
\t $ reana-client share-add -w myanalysis.42 --user [email protected] | ||
\t $ reana-client share-add -w myanalysis.42 --user [email protected] --user [email protected] --message "Please review my analysis" --valid-until 2025-12-31 | ||
""" | ||
from reana_client.api.client import share_workflow | ||
|
||
share_errors = [] | ||
shared_users = [] | ||
|
||
if valid_until: | ||
valid_until = valid_until.strftime("%Y-%m-%d") | ||
|
||
for user in users: | ||
try: | ||
logging.info(f"Sharing workflow {workflow} with user {user}") | ||
share_workflow( | ||
workflow, | ||
user, | ||
access_token, | ||
message=message, | ||
valid_until=valid_until, | ||
) | ||
shared_users.append(user) | ||
except Exception as e: | ||
share_errors.append(f"Failed to share {workflow} with {user}: {str(e)}") | ||
logging.debug(traceback.format_exc()) | ||
|
||
if shared_users: | ||
display_message( | ||
f"{workflow} is now read-only shared with {', '.join(shared_users)}", | ||
msg_type="success", | ||
) | ||
|
||
for error in share_errors: | ||
display_message(error, msg_type="error") | ||
|
||
if share_errors: | ||
sys.exit(1) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,12 +17,11 @@ | |
from click.testing import CliRunner | ||
from mock import Mock, patch | ||
from pytest_reana.test_utils import make_mock_api_client | ||
from reana_commons.config import INTERACTIVE_SESSION_TYPES | ||
|
||
from reana_client.api.client import create_workflow_from_json | ||
from reana_client.config import RUN_STATUSES | ||
from reana_client.cli import cli | ||
from reana_client.config import RUN_STATUSES | ||
from reana_client.utils import get_workflow_status_change_msg | ||
from reana_commons.config import INTERACTIVE_SESSION_TYPES | ||
|
||
|
||
def test_workflows_server_not_connected(): | ||
|
@@ -1011,3 +1010,42 @@ def test_run_with_no_inputs(spec_without_inputs): | |
cli_workflow_start.assert_called() | ||
get_workflow_specification.assert_called() | ||
upload_to_server.assert_not_called() | ||
|
||
|
||
def test_share_add_workflow(): | ||
"""Test share-add workflows.""" | ||
status_code = 200 | ||
response = { | ||
"message": "is now read-only shared with", | ||
"workflow_id": "string", | ||
"workflow_name": "string", | ||
} | ||
env = {"REANA_SERVER_URL": "localhost"} | ||
mock_http_response, mock_response = Mock(), Mock() | ||
mock_http_response.status_code = status_code | ||
mock_response = response | ||
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, | ||
[ | ||
"share-add", | ||
"-t", | ||
reana_token, | ||
"--workflow", | ||
"test-workflow.1", | ||
"--user", | ||
"[email protected]", | ||
"--message", | ||
"Test message", | ||
"--valid-until", | ||
"2024-01-01", | ||
], | ||
) | ||
assert result.exit_code == 0 | ||
assert response["message"] in result.output |