Skip to content

Commit

Permalink
fixes folders
Browse files Browse the repository at this point in the history
  • Loading branch information
pcrespov committed Jan 17, 2025
1 parent a0c8619 commit d6856bf
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 30 deletions.
37 changes: 11 additions & 26 deletions packages/models-library/src/models_library/folders.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,7 @@
from enum import auto
from typing import TypeAlias

from pydantic import (
BaseModel,
ConfigDict,
Field,
PositiveInt,
ValidationInfo,
field_validator,
)
from pydantic import BaseModel, ConfigDict, PositiveInt, ValidationInfo, field_validator

from .access_rights import AccessRights
from .groups import GroupID
Expand Down Expand Up @@ -52,25 +45,17 @@ class FolderDB(BaseModel):
folder_id: FolderID
name: str
parent_folder_id: FolderID | None
created_by_gid: GroupID = Field(
...,
description="GID of the group that owns this wallet",
)
created: datetime = Field(
...,
description="Timestamp on creation",
)
modified: datetime = Field(
...,
description="Timestamp of last modification",
)
trashed: datetime | None = Field(
...,
)

user_id: UserID | None
workspace_id: WorkspaceID | None

created_by_gid: GroupID
created: datetime
modified: datetime

trashed: datetime | None
trashed_by: UserID | None
trashed_explicitly: bool

user_id: UserID | None # owner?
workspace_id: WorkspaceID | None
model_config = ConfigDict(from_attributes=True)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
folders_v2.c.created,
folders_v2.c.modified,
folders_v2.c.trashed,
folders_v2.c.trashed_by,
folders_v2.c.trashed_explicitly,
folders_v2.c.user_id,
folders_v2.c.workspace_id,
)
Expand Down Expand Up @@ -285,8 +287,8 @@ async def get(
)

async with pass_or_acquire_connection(get_asyncpg_engine(app), connection) as conn:
result = await conn.stream(query)
row = await result.first()
result = await conn.execute(query)
row = result.first()
if row is None:
raise FolderAccessForbiddenError(
reason=f"Folder {folder_id} does not exist.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ async def create_folder(
created_at=folder_db.created,
modified_at=folder_db.modified,
trashed_at=folder_db.trashed,
trashed_by=folder_db.trashed_by,
owner=folder_db.created_by_gid,
workspace_id=workspace_id,
my_access_rights=user_folder_access_rights,
Expand Down Expand Up @@ -136,6 +137,7 @@ async def get_folder(
created_at=folder_db.created,
modified_at=folder_db.modified,
trashed_at=folder_db.trashed,
trashed_by=folder_db.trashed_by,
owner=folder_db.created_by_gid,
workspace_id=folder_db.workspace_id,
my_access_rights=user_folder_access_rights,
Expand Down Expand Up @@ -186,6 +188,7 @@ async def list_folders(
created_at=folder.created,
modified_at=folder.modified,
trashed_at=folder.trashed,
trashed_by=folder.trashed_by,
owner=folder.created_by_gid,
workspace_id=folder.workspace_id,
my_access_rights=folder.my_access_rights,
Expand Down Expand Up @@ -230,6 +233,7 @@ async def list_folders_full_depth(
created_at=folder.created,
modified_at=folder.modified,
trashed_at=folder.trashed,
trashed_by=folder.trashed_by,
owner=folder.created_by_gid,
workspace_id=folder.workspace_id,
my_access_rights=folder.my_access_rights,
Expand Down Expand Up @@ -307,6 +311,7 @@ async def update_folder(
created_at=folder_db.created,
modified_at=folder_db.modified,
trashed_at=folder_db.trashed,
trashed_by=folder_db.trashed_by,
owner=folder_db.created_by_gid,
workspace_id=folder_db.workspace_id,
my_access_rights=user_folder_access_rights,
Expand Down
102 changes: 100 additions & 2 deletions services/web/server/tests/unit/with_dbs/03/test_trash.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@
from pytest_simcore.helpers.assert_checks import assert_status
from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict
from pytest_simcore.helpers.typing_env import EnvVarsDict
from pytest_simcore.helpers.webserver_login import UserInfoDict
from pytest_simcore.helpers.webserver_login import NewUser, UserInfoDict
from servicelib.aiohttp import status
from simcore_service_webserver.db.models import UserRole
from simcore_service_webserver.projects._groups_api import ProjectGroupGet
from simcore_service_webserver.projects.models import ProjectDict
from yarl import URL

Expand Down Expand Up @@ -74,7 +75,7 @@ async def test_trash_projects( # noqa: PLR0915
):
assert client.app

# this test should have no errors stopping services
# this test should emulate NO errors stopping services
mock_remove_dynamic_services = mocker.patch(
"simcore_service_webserver.projects._trash_service.projects_api.remove_project_dynamic_services",
autospec=True,
Expand Down Expand Up @@ -182,6 +183,103 @@ async def test_trash_projects( # noqa: PLR0915
mock_remove_dynamic_services.assert_awaited()


@pytest.fixture
async def other_user(
client: TestClient, logged_user: UserInfoDict
) -> AsyncIterable[UserInfoDict]:
# new user different from logged_user
async with NewUser(
{
"name": f"other_user_than_{logged_user['name']}",
"role": "USER",
},
client.app,
) as user:
yield user


async def test_trash_projects_shared_among_users(
client: TestClient,
logged_user: UserInfoDict,
user_project: ProjectDict,
other_user: UserInfoDict,
mocked_catalog: None,
mocked_dynamic_services_interface: dict[str, MagicMock],
):
assert client.app

project_uuid = UUID(user_project["uuid"])

# GET project
url = client.app.router["get_project"].url_for(project_id=f"{project_uuid}")
resp = await client.get(f"{url}")
data, _ = await assert_status(resp, status.HTTP_200_OK)

project = ProjectGet.model_validate(data)
assert project.uuid == project_uuid
assert project.prj_owner == logged_user["email"]

# SHARE PROJECT with other-user
url = client.app.router["create_project_group"].url_for(
project_id=f"{project_uuid}", group_id=f"{other_user['primary_gid']}"
)
resp = await client.post(
f"{url}",
json={"read": True, "write": True, "delete": False},
)
data, _ = await assert_status(resp, status.HTTP_201_CREATED)

project_group = ProjectGroupGet.model_validate(data)
assert project_group.gid == other_user["primary_gid"]
assert project_group.read is True
assert project_group.write is True
assert project_group.delete is False

# TRASH project
trashing_at = arrow.utcnow().datetime
resp = await client.post(
f"/v0/projects/{project_uuid}:trash", params={"force": "true"}
)
await assert_status(resp, status.HTTP_204_NO_CONTENT)

# LIST trashed of logged_user
resp = await client.get("/v0/projects", params={"filters": '{"trashed": true}'})
await assert_status(resp, status.HTTP_200_OK)

page = Page[ProjectListItem].model_validate(await resp.json())
assert page.meta.total == 1
assert page.data[0].uuid == project_uuid
assert page.data[0].trashed_at
assert trashing_at < page.data[0].trashed_at
assert page.data[0].trashed_by == logged_user["id"]

# Swith USER: LOGOUT
url = client.app.router["auth_logout"].url_for()
resp = await client.post(f"{url}")
await assert_status(resp, status.HTTP_200_OK)

url = client.app.router["auth_login"].url_for()
resp = await client.post(
f"{url}",
json={
"email": other_user["email"],
"password": other_user["raw_password"],
},
)
data, _ = await assert_status(resp, status.HTTP_200_OK)

# LIST trashed of another_user
resp = await client.get("/v0/projects", params={"filters": '{"trashed": true}'})
await assert_status(resp, status.HTTP_200_OK)

page = Page[ProjectListItem].model_validate(await resp.json())
assert page.meta.total == 1
assert page.data[0].uuid == project_uuid
assert page.data[0].trashed_at
assert trashing_at < page.data[0].trashed_at
assert page.data[0].trashed_by == logged_user["id"]


@pytest.mark.acceptance_test(
"For https://github.com/ITISFoundation/osparc-simcore/pull/6642"
)
Expand Down

0 comments on commit d6856bf

Please sign in to comment.