Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add workspace add-user command #3712

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ These are the section headers that we use:
- Added `info` command to get info about the used Argilla client and server ([#3707](https://github.com/argilla-io/argilla/pull/3707)).
- Added `datasets delete` command to delete a `FeedbackDataset` from Argilla ([#3703](https://github.com/argilla-io/argilla/pull/3703)).
- Added `created_at` and `updated_at` properties to `RemoteFeedbackDataset` and `FilteredRemoteFeedbackDataset` ([#3709](https://github.com/argilla-io/argilla/pull/3709)).
- Added `workspaces add-user` command to add a user to workspace ([#3712](https://github.com/argilla-io/argilla/pull/3712)).

### Fixed

Expand Down
2 changes: 2 additions & 0 deletions src/argilla/tasks/workspaces/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

from argilla.tasks.callback import init_callback

from .add_user import add_user
from .create import create_workspace
from .delete_user import delete_user
from .list import list_workspaces
Expand Down Expand Up @@ -57,6 +58,7 @@ def callback(
app.command(name="create", help="Create a workspace")(create_workspace)
app.command(name="list", help="Lists workspaces of the logged user.")(list_workspaces)
app.command(name="delete-user", help="Deletes a user from a workspace.")(delete_user)
app.command(name="add-user", help="Adds a user to a workspace.")(add_user)


if __name__ == "__main__":
Expand Down
64 changes: 64 additions & 0 deletions src/argilla/tasks/workspaces/add_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Copyright 2021-present, the Recognai S.L. team.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import TYPE_CHECKING

import typer

if TYPE_CHECKING:
from argilla.client.workspaces import Workspace


def add_user(
ctx: typer.Context,
username: str = typer.Argument(..., help="The username of the user to be added to the workspace"),
) -> None:
from rich.console import Console

from argilla.client.users import User
from argilla.tasks.rich import get_argilla_themed_panel

workspace: "Workspace" = ctx.obj

try:
user = User.from_name(username)
except ValueError as e:
typer.echo(f"User with username '{username}' does not exist")
raise typer.Exit(code=1) from e
except RuntimeError as e:
typer.echo("An unexpected error occurred when trying to retrieve the user from the Argilla server")
raise typer.Exit(code=1) from e

gabrielmbmb marked this conversation as resolved.
Show resolved Hide resolved
if user.is_owner:
typer.echo(
f"User with name={username} is an owner. Users with owner role don't need specific permissions per"
" workspace, as those are super-users with privileges over everything under Argilla."
)
raise typer.Exit(code=1)

try:
workspace.add_user(user.id)
except ValueError as e:
typer.echo(f"User with username '{username}' already exists in workspace '{workspace.name}'")
raise typer.Exit(code=1) from e
except RuntimeError as e:
typer.echo("An unexpected error occurred when trying to add user to the workspace")
raise typer.Exit(code=1) from e

panel = get_argilla_themed_panel(
f"User with username '{username}' has been added to '{workspace.name}' workspace",
title="User Added",
title_align="left",
)
Console().print(panel)
122 changes: 122 additions & 0 deletions tests/unit/tasks/workspaces/test_add_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Copyright 2021-present, the Recognai S.L. team.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import TYPE_CHECKING

import pytest

if TYPE_CHECKING:
from click.testing import CliRunner
from pytest_mock import MockerFixture
from typer import Typer


@pytest.mark.usefixtures("login_mock")
class TestSuiteWorkspaceAddUser:
def test_workspace_add_user(
self, cli_runner: "CliRunner", cli: "Typer", mocker: "MockerFixture", workspace, user
) -> None:
mocker.patch("argilla.client.workspaces.Workspace.from_name", return_value=workspace)
mocker.patch("argilla.client.users.User.from_name", return_value=user)
mocker.patch("argilla.client.workspaces.Workspace.add_user")

result = cli_runner.invoke(cli, "workspaces --name unit-test add-user unit-test")

assert result.exit_code == 0
assert "User with username 'unit-test' has been added to 'unit-test' workspace" in result.stdout

def test_workspace_add_user_with_non_existing_workspace(
self, cli_runner: "CliRunner", cli: "Typer", mocker: "MockerFixture"
) -> None:
mocker.patch("argilla.client.workspaces.Workspace.from_name", side_effect=ValueError)

result = cli_runner.invoke(cli, "workspaces --name unit-test add-user unit-test")

assert result.exit_code == 1
assert "Workspace 'unit-test' does not exist" in result.stdout

def test_workspace_add_user_with_non_existing_user(
self, cli_runner: "CliRunner", cli: "Typer", mocker: "MockerFixture", workspace, user
) -> None:
mocker.patch("argilla.client.workspaces.Workspace.from_name", return_value=workspace)
mocker.patch("argilla.client.users.User.from_name", side_effect=ValueError)

result = cli_runner.invoke(cli, "workspaces --name unit-test add-user unit-test")

assert result.exit_code == 1
assert "User with username 'unit-test' does not exist" in result.stdout

def test_workspace_add_user_with_owner_user(
self, cli_runner: "CliRunner", cli: "Typer", mocker: "MockerFixture", workspace, user
) -> None:
user.role = "owner"
mocker.patch("argilla.client.workspaces.Workspace.from_name", return_value=workspace)
mocker.patch("argilla.client.users.User.from_name", return_value=user)

result = cli_runner.invoke(cli, "workspaces --name unit-test add-user unit-test")

assert result.exit_code == 1
assert (
"User with name=unit-test is an owner. Users with owner role don't need specific permissions per"
" workspace, as those are super-users with privileges over everything under Argilla." in result.stdout
)

def test_workspace_add_user_with_user_belonging_to_workspace(
self, cli_runner: "CliRunner", cli: "Typer", mocker: "MockerFixture", workspace, user
) -> None:
mocker.patch("argilla.client.workspaces.Workspace.from_name", return_value=workspace)
mocker.patch("argilla.client.users.User.from_name", return_value=user)
mocker.patch("argilla.client.workspaces.Workspace.add_user", side_effect=ValueError)

result = cli_runner.invoke(cli, "workspaces --name unit-test add-user unit-test")

assert result.exit_code == 1
assert "User with username 'unit-test' already exists in workspace 'unit-test'" in result.stdout

def test_workspace_add_user_with_unexpected_error(
self, cli_runner: "CliRunner", cli: "Typer", mocker: "MockerFixture", workspace, user
) -> None:
mocker.patch("argilla.client.workspaces.Workspace.from_name", return_value=workspace)
mocker.patch("argilla.client.users.User.from_name", return_value=user)
mocker.patch("argilla.client.workspaces.Workspace.add_user", side_effect=RuntimeError)

result = cli_runner.invoke(cli, "workspaces --name unit-test add-user unit-test")

assert result.exit_code == 1
assert "An unexpected error occurred when trying to add user to the workspace" in result.stdout

def test_workspace_add_user_with_unexpected_error_retieve_user(
self, cli_runner: "CliRunner", cli: "Typer", mocker: "MockerFixture", workspace, user
) -> None:
mocker.patch("argilla.client.workspaces.Workspace.from_name", return_value=workspace)
mocker.patch("argilla.client.users.User.from_name", side_effect=RuntimeError)
mocker.patch("argilla.client.workspaces.Workspace.add_user")

result = cli_runner.invoke(cli, "workspaces --name unit-test add-user unit-test")

assert result.exit_code == 1
assert "An unexpected error occurred when trying to retrieve the user from the Argilla server" in result.stdout

def test_workspace_add_user_without_workspace_name(self, cli_runner: "CliRunner", cli: "Typer") -> None:
result = cli_runner.invoke(cli, "workspaces add-user unit-test")

assert result.exit_code == 2


@pytest.mark.usefixtures("not_logged_mock")
def test_list_users_needs_login(cli_runner: "CliRunner", cli: "Typer") -> None:
result = cli_runner.invoke(cli, "workspaces --name unit-test add-user unit-test")

assert result.exit_code == 1
assert "You are not logged in. Please run `argilla login` to login to an Argilla server." in result.stdout
Loading