diff --git a/docs/cmd_list.txt b/docs/cmd_list.txt index 94bd326b..0ae11fc0 100644 --- a/docs/cmd_list.txt +++ b/docs/cmd_list.txt @@ -31,7 +31,8 @@ Workflow execution commands: validate Validate workflow specification file. Workflow sharing commands: - share-add Share a workflow with other users (read-only). + share-add Share a workflow with other users (read-only). + share-remove Unshare a workflow. Workspace interactive commands: close Close an interactive session. diff --git a/reana_client/api/client.py b/reana_client/api/client.py index d06aba2a..11e4aa9d 100644 --- a/reana_client/api/client.py +++ b/reana_client/api/client.py @@ -1327,3 +1327,41 @@ def share_workflow( f"Message: {e.response.json()['message']}" ) raise Exception(e.response.json()["message"]) + + +def unshare_workflow(workflow, user_email_to_unshare_with, access_token): + """Unshare a workflow with a user. + + :param workflow: name or id of the workflow. + :param user_email_to_unshare_with: user to unshare the workflow with. + :param access_token: access token of the current user. + + :return: a dictionary containing the ``workflow_id``, ``workflow_name``, and + a ``message`` key with the result of the operation. + """ + try: + unshare_params = { + "workflow_id_or_name": workflow, + "user_email_to_unshare_with": user_email_to_unshare_with, + "access_token": access_token, + } + + (response, http_response) = current_rs_api_client.api.unshare_workflow( + **unshare_params + ).result() + + if http_response.status_code == 200: + return response + else: + raise Exception( + "Expected status code 200 but replied with " + f"{http_response.status_code}" + ) + + except HTTPError as e: + logging.debug( + "Workflow could not be unshared: " + f"\nStatus: {e.response.status_code}\nReason: {e.response.reason}\n" + f"Message: {e.response.json()['message']}" + ) + raise Exception(e.response.json()["message"]) diff --git a/reana_client/cli/workflow.py b/reana_client/cli/workflow.py index cee22892..a1dfb941 100644 --- a/reana_client/cli/workflow.py +++ b/reana_client/cli/workflow.py @@ -1545,3 +1545,64 @@ def share_workflow_add( else: display_message(f"Cannot find workflow {workflow}", msg_type="error") + + +@workflow_sharing_group.command("share-remove") +@check_connection +@add_workflow_option +@add_access_token_options +@click.option( + "-u", + "--user", + "users", + multiple=True, + help="Users to unshare the workflow with.", + required=True, +) +@click.pass_context +def share_workflow_remove(ctx, workflow, access_token, users): # noqa D412 + """Unshare a workflow. + + The `share-remove` command allows for unsharing a workflow. The workflow + will no longer be visible to the users with whom it was shared. + + Example: + + $ reana-client share-remove -w myanalysis.42 --user bob@example.org + """ + from reana_client.api.client import unshare_workflow + + unshare_errors = [] + unshared_users = [] + + if workflow: + try: + for user in users: + try: + logging.info(f"Unsharing workflow {workflow} with user {user}") + unshare_workflow(workflow, user, access_token) + unshared_users.append(user) + except Exception as e: + unshare_errors.append( + f"Failed to unshare {workflow} with {user}: {str(e)}" + ) + logging.debug(traceback.format_exc()) + except Exception as e: + logging.debug(traceback.format_exc()) + logging.debug(str(e)) + display_message( + "An error occurred while unsharing workflow:\n{}".format(str(e)), + msg_type="error", + ) + + if unshared_users: + display_message( + f"{workflow} is no longer shared with {', '.join(unshared_users)}", + msg_type="success", + ) + if unshare_errors: + for error in unshare_errors: + display_message(error, msg_type="error") + + else: + display_message(f"Cannot find workflow {workflow}", msg_type="error") diff --git a/tests/test_cli_workflows.py b/tests/test_cli_workflows.py index 4d3c3050..c4af475e 100644 --- a/tests/test_cli_workflows.py +++ b/tests/test_cli_workflows.py @@ -1026,3 +1026,38 @@ def test_share_add_workflow(): ) assert result.exit_code == 0 assert response["message"] in result.output + + +def test_share_remove_workflow(): + """Test share-remove workflows.""" + status_code = 200 + response = { + "message": "is no longer 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-remove", + "-t", + reana_token, + "--workflow", + "test-workflow.1", + "--user", + "bob@.cern.ch", + ], + ) + assert result.exit_code == 0 + assert response["message"] in result.output