Skip to content

Commit c0c33c0

Browse files
authored
🐛Fixes batch-get operations on trash (#7194)
1 parent 7457535 commit c0c33c0

File tree

4 files changed

+137
-4
lines changed

4 files changed

+137
-4
lines changed

services/web/server/src/simcore_service_webserver/folders/_folders_repository.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,10 @@ async def get_folders_recursively(
566566

567567

568568
def _select_trashed_by_primary_gid_query():
569-
return sa.select(users.c.primary_gid.label("trashed_by_primary_gid")).select_from(
569+
return sa.select(
570+
folders_v2.c.folder_id,
571+
users.c.primary_gid.label("trashed_by_primary_gid"),
572+
).select_from(
570573
folders_v2.outerjoin(users, folders_v2.c.trashed_by == users.c.id),
571574
)
572575

@@ -583,7 +586,7 @@ async def get_trashed_by_primary_gid(
583586

584587
async with pass_or_acquire_connection(get_asyncpg_engine(app), connection) as conn:
585588
result = await conn.execute(query)
586-
row = result.first()
589+
row = result.one_or_none()
587590
return row.trashed_by_primary_gid if row else None
588591

589592

@@ -611,4 +614,6 @@ async def batch_get_trashed_by_primary_gid(
611614

612615
async with pass_or_acquire_connection(get_asyncpg_engine(app), connection) as conn:
613616
result = await conn.stream(query)
614-
return [row.trashed_by_primary_gid async for row in result]
617+
rows = {row.folder_id: row.trashed_by_primary_gid async for row in result}
618+
619+
return [rows.get(folder_id) for folder_id in folders_ids]

services/web/server/src/simcore_service_webserver/projects/_projects_db.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ async def patch_project(
5353

5454
def _select_trashed_by_primary_gid_query() -> sql.Select:
5555
return sa.select(
56+
projects.c.uuid,
5657
users.c.primary_gid.label("trashed_by_primary_gid"),
5758
).select_from(projects.outerjoin(users, projects.c.trashed_by == users.c.id))
5859

@@ -106,4 +107,6 @@ async def batch_get_trashed_by_primary_gid(
106107
)
107108
async with pass_or_acquire_connection(get_asyncpg_engine(app), connection) as conn:
108109
result = await conn.stream(query)
109-
return [row.trashed_by_primary_gid async for row in result]
110+
rows = {row.uuid: row.trashed_by_primary_gid async for row in result}
111+
112+
return [rows.get(uuid) for uuid in projects_uuids_str]

services/web/server/tests/unit/with_dbs/03/test_project_db.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from uuid import UUID, uuid5
1414

1515
import aiopg.sa
16+
import arrow
1617
import pytest
1718
import sqlalchemy as sa
1819
from aiohttp.test_utils import TestClient
@@ -31,6 +32,9 @@
3132
from simcore_postgres_database.utils_projects_nodes import ProjectNodesRepo
3233
from simcore_service_webserver.projects._db_utils import PermissionStr
3334
from simcore_service_webserver.projects._groups_db import update_or_insert_project_group
35+
from simcore_service_webserver.projects._projects_db import (
36+
batch_get_trashed_by_primary_gid,
37+
)
3438
from simcore_service_webserver.projects.api import has_user_project_access_rights
3539
from simcore_service_webserver.projects.db import ProjectAccessRights, ProjectDBAPI
3640
from simcore_service_webserver.projects.exceptions import (
@@ -962,3 +966,42 @@ async def test_check_project_node_has_all_required_inputs_ok(
962966
project_uuid=UUID(inserted_project["uuid"]),
963967
node_id=UUID("324d6ef2-a82c-414d-9001-dc84da1cbea3"),
964968
)
969+
970+
971+
@pytest.mark.parametrize(
972+
"user_role",
973+
[UserRole.USER],
974+
)
975+
async def test_batch_get_trashed_by_primary_gid(
976+
client: TestClient,
977+
logged_user: UserInfoDict,
978+
insert_project_in_db: Callable[..., Awaitable[dict[str, Any]]],
979+
fake_project: ProjectDict,
980+
):
981+
assert client.app
982+
983+
# Insert two different projects
984+
fake_project_1 = deepcopy(fake_project)
985+
fake_project_1["trashed_by"] = logged_user["id"]
986+
fake_project_1["trashed"] = arrow.now().datetime
987+
fake_project_1["trashed_explicitly"] = True
988+
989+
project_1 = await insert_project_in_db(fake_project_1, user_id=logged_user["id"])
990+
project_2 = await insert_project_in_db(fake_project, user_id=logged_user["id"])
991+
992+
# Test with two different project UUIDs
993+
trashed_by_primary_gid = await batch_get_trashed_by_primary_gid(
994+
client.app,
995+
projects_uuids=[project_1["uuid"], project_2["uuid"]],
996+
)
997+
assert trashed_by_primary_gid == [logged_user["primary_gid"], None]
998+
999+
# Test with two identical project UUIDs
1000+
trashed_by_primary_gid = await batch_get_trashed_by_primary_gid(
1001+
client.app,
1002+
projects_uuids=[project_1["uuid"], project_1["uuid"]],
1003+
)
1004+
assert trashed_by_primary_gid == [
1005+
logged_user["primary_gid"],
1006+
logged_user["primary_gid"],
1007+
]
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# pylint: disable=redefined-outer-name
2+
# pylint: disable=unused-argument
3+
# pylint: disable=unused-variable
4+
# pylint: disable=too-many-arguments
5+
6+
from typing import Any
7+
8+
import arrow
9+
import pytest
10+
from aiohttp.test_utils import TestClient
11+
from common_library.users_enums import UserRole
12+
from models_library.products import ProductName
13+
from simcore_service_webserver.folders import _folders_repository
14+
15+
16+
@pytest.fixture
17+
def user_role():
18+
return UserRole.USER
19+
20+
21+
@pytest.fixture
22+
def product_name():
23+
return "osparc"
24+
25+
26+
async def test_batch_get_trashed_by_primary_gid(
27+
client: TestClient,
28+
logged_user: dict[str, Any],
29+
product_name: ProductName,
30+
):
31+
assert client.app
32+
33+
# Create two folders
34+
folder_1 = await _folders_repository.create(
35+
client.app,
36+
created_by_gid=logged_user["primary_gid"],
37+
folder_name="Folder 1",
38+
product_name=product_name,
39+
parent_folder_id=None,
40+
user_id=logged_user["id"],
41+
workspace_id=None,
42+
)
43+
folder_2 = await _folders_repository.create(
44+
client.app,
45+
created_by_gid=logged_user["primary_gid"],
46+
folder_name="Folder 2",
47+
product_name=product_name,
48+
parent_folder_id=None,
49+
user_id=logged_user["id"],
50+
workspace_id=None,
51+
)
52+
53+
# Update the trashed flag for folder_1
54+
await _folders_repository.update(
55+
client.app,
56+
folders_id_or_ids=folder_1.folder_id,
57+
product_name=product_name,
58+
trashed=arrow.now().datetime,
59+
trashed_explicitly=True,
60+
trashed_by=logged_user["id"],
61+
)
62+
63+
# Test batch_get_trashed_by_primary_gid
64+
trashed_by_primary_gid = await _folders_repository.batch_get_trashed_by_primary_gid(
65+
client.app,
66+
folders_ids=[folder_1.folder_id, folder_2.folder_id],
67+
)
68+
assert trashed_by_primary_gid == [logged_user["primary_gid"], None]
69+
70+
# flipped
71+
trashed_by_primary_gid = await _folders_repository.batch_get_trashed_by_primary_gid(
72+
client.app,
73+
folders_ids=[folder_2.folder_id, folder_1.folder_id],
74+
)
75+
assert trashed_by_primary_gid == [None, logged_user["primary_gid"]]
76+
77+
# repeated
78+
trashed_by_primary_gid = await _folders_repository.batch_get_trashed_by_primary_gid(
79+
client.app,
80+
folders_ids=[folder_1.folder_id] * 3,
81+
)
82+
assert trashed_by_primary_gid == [logged_user["primary_gid"]] * 3

0 commit comments

Comments
 (0)