Skip to content

Commit 696a19b

Browse files
update
1 parent 74ee277 commit 696a19b

File tree

10 files changed

+475
-125
lines changed

10 files changed

+475
-125
lines changed

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,9 @@ async def list_( # pylint: disable=too-many-arguments,too-many-branches
245245

246246
# Ordering and pagination
247247
list_query = (
248-
combined_query.order_by(_to_expression(order_by)).offset(offset).limit(limit)
248+
combined_query.order_by(_to_expression(order_by), folders_v2.c.folder_id)
249+
.offset(offset)
250+
.limit(limit)
249251
)
250252

251253
async with pass_or_acquire_connection(get_asyncpg_engine(app), connection) as conn:
@@ -294,7 +296,9 @@ async def list_trashed_folders(
294296

295297
# Ordering and pagination
296298
list_query = (
297-
base_query.order_by(_to_expression(order_by)).offset(offset).limit(limit)
299+
base_query.order_by(_to_expression(order_by), folders_v2.c.folder_id)
300+
.offset(offset)
301+
.limit(limit)
298302
)
299303

300304
async with pass_or_acquire_connection(get_asyncpg_engine(app), connection) as conn:
@@ -534,7 +538,7 @@ async def get_all_folders_and_projects_ids_recursively(
534538
product_name: ProductName,
535539
) -> tuple[list[FolderID], list[ProjectID]]:
536540
"""
537-
The purpose of this function is to retrieve all projects within the provided folder ID.
541+
The purpose of this function is to retrieve all subfolders and projects within the provided folder ID.
538542
"""
539543

540544
async with pass_or_acquire_connection(get_asyncpg_engine(app), connection) as conn:

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

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -132,15 +132,17 @@ async def trash_folder(
132132
)
133133

134134
# 2. Trash all child projects that I am an owner
135-
child_projects: list[
136-
ProjectID
137-
] = await _folders_repository.get_projects_recursively_only_if_user_is_owner(
138-
app,
139-
connection,
140-
folder_id=folder_id,
141-
private_workspace_user_id_or_none=user_id if workspace_is_private else None,
142-
user_id=user_id,
143-
product_name=product_name,
135+
child_projects: list[ProjectID] = (
136+
await _folders_repository.get_projects_recursively_only_if_user_is_owner(
137+
app,
138+
connection,
139+
folder_id=folder_id,
140+
private_workspace_user_id_or_none=(
141+
user_id if workspace_is_private else None
142+
),
143+
user_id=user_id,
144+
product_name=product_name,
145+
)
144146
)
145147

146148
for project_id in child_projects:
@@ -178,14 +180,14 @@ async def untrash_folder(
178180
)
179181

180182
# 3.2 UNtrash all child projects that I am an owner
181-
child_projects: list[
182-
ProjectID
183-
] = await _folders_repository.get_projects_recursively_only_if_user_is_owner(
184-
app,
185-
folder_id=folder_id,
186-
private_workspace_user_id_or_none=user_id if workspace_is_private else None,
187-
user_id=user_id,
188-
product_name=product_name,
183+
child_projects: list[ProjectID] = (
184+
await _folders_repository.get_projects_recursively_only_if_user_is_owner(
185+
app,
186+
folder_id=folder_id,
187+
private_workspace_user_id_or_none=user_id if workspace_is_private else None,
188+
user_id=user_id,
189+
product_name=product_name,
190+
)
189191
)
190192

191193
for project_id in child_projects:
@@ -204,7 +206,7 @@ def _can_delete(
204206
folder_db.trashed
205207
and (until_equal_datetime is None or folder_db.trashed < until_equal_datetime)
206208
and my_access_rights.delete
207-
and folder_db.trashed_by == user_id
209+
and folder_db.trashed_by == user_id # <-- Ask PC - is this correct?
208210
and folder_db.trashed_explicitly
209211
)
210212

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from ._folders_service import delete_folder, list_folders
2+
3+
__all__: tuple[str, ...] = (
4+
"list_folders",
5+
"delete_folder",
6+
)
7+
8+
# nopycln: file

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
from ..catalog import catalog_service
2323
from ..folders import _folders_repository
24-
from ..workspaces._workspaces_service import check_user_workspace_access
24+
from ..workspaces.api import check_user_workspace_access
2525
from . import _projects_service
2626
from ._projects_repository import batch_get_trashed_by_primary_gid
2727
from ._projects_repository_legacy import ProjectDBAPI

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
check_user_project_permission,
66
has_user_project_access_rights,
77
)
8+
from ._crud_api_read import list_projects
89
from ._groups_service import (
910
create_project_group_without_checking_permissions,
1011
delete_project_group_without_checking_permissions,
1112
)
13+
from ._projects_service import delete_project_by_user
1214
from ._wallets_service import (
1315
check_project_financial_status,
1416
connect_wallet_to_project,
@@ -23,6 +25,8 @@
2325
"delete_project_group_without_checking_permissions",
2426
"get_project_wallet",
2527
"has_user_project_access_rights",
28+
"list_projects",
29+
"delete_project_by_user",
2630
)
2731

2832

services/web/server/src/simcore_service_webserver/workspaces/_workspaces_rest.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from ..login.decorators import login_required
2424
from ..security.decorators import permission_required
2525
from ..utils_aiohttp import envelope_json_response
26-
from . import _workspaces_service
26+
from . import _workspaces_service, _workspaces_service_crud_read
2727
from ._common.exceptions_handlers import handle_plugin_requests_exceptions
2828
from ._common.models import (
2929
WorkspacesFilters,
@@ -76,7 +76,7 @@ async def list_workspaces(request: web.Request):
7676
query_params.filters = WorkspacesFilters()
7777

7878
assert query_params.filters
79-
total_count, workspaces = await _workspaces_service.list_workspaces(
79+
total_count, workspaces = await _workspaces_service_crud_read.list_workspaces(
8080
app=request.app,
8181
user_id=req_ctx.user_id,
8282
product_name=req_ctx.product_name,
@@ -110,7 +110,7 @@ async def get_workspace(request: web.Request):
110110
req_ctx = WorkspacesRequestContext.model_validate(request)
111111
path_params = parse_request_path_parameters_as(WorkspacesPathParams, request)
112112

113-
workspace = await _workspaces_service.get_workspace(
113+
workspace = await _workspaces_service_crud_read.get_workspace(
114114
app=request.app,
115115
workspace_id=path_params.workspace_id,
116116
user_id=req_ctx.user_id,
Lines changed: 73 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,29 @@
11
# pylint: disable=unused-argument
22

3+
import asyncio
34
import logging
45

56
from aiohttp import web
7+
from common_library.pagination_tools import iter_pagination_params
8+
from models_library.basic_types import IDStr
9+
from models_library.folders import FolderID
610
from models_library.products import ProductName
7-
from models_library.rest_ordering import OrderBy
11+
from models_library.projects import Project, ProjectID
12+
from models_library.rest_ordering import OrderBy, OrderDirection
13+
from models_library.rest_pagination import MAXIMUM_NUMBER_OF_ITEMS_PER_PAGE
814
from models_library.users import UserID
915
from models_library.workspaces import (
1016
UserWorkspaceWithAccessRights,
1117
WorkspaceID,
1218
WorkspaceUpdates,
1319
)
14-
from pydantic import NonNegativeInt
1520

16-
from ..projects._projects_repository_legacy_utils import PermissionStr
21+
from ..folders.service import delete_folder, list_folders # <-- MD: wrong
22+
from ..projects.api import delete_project_by_user, list_projects # <-- MD: wrong
23+
from ..projects.models import ProjectTypeAPI
1724
from ..users.api import get_user
1825
from . import _workspaces_repository as db
19-
from .errors import WorkspaceAccessForbiddenError
26+
from ._workspaces_service_crud_read import check_user_workspace_access
2027

2128
_logger = logging.getLogger(__name__)
2229

@@ -47,47 +54,6 @@ async def create_workspace(
4754
)
4855

4956

50-
async def get_workspace(
51-
app: web.Application,
52-
*,
53-
user_id: UserID,
54-
workspace_id: WorkspaceID,
55-
product_name: ProductName,
56-
) -> UserWorkspaceWithAccessRights:
57-
return await get_user_workspace(
58-
app=app,
59-
user_id=user_id,
60-
workspace_id=workspace_id,
61-
product_name=product_name,
62-
permission="read",
63-
)
64-
65-
66-
async def list_workspaces(
67-
app: web.Application,
68-
*,
69-
user_id: UserID,
70-
product_name: ProductName,
71-
filter_trashed: bool | None,
72-
filter_by_text: str | None,
73-
offset: NonNegativeInt,
74-
limit: int,
75-
order_by: OrderBy,
76-
) -> tuple[int, list[UserWorkspaceWithAccessRights]]:
77-
total_count, workspaces = await db.list_workspaces_for_user(
78-
app,
79-
user_id=user_id,
80-
product_name=product_name,
81-
filter_trashed=filter_trashed,
82-
filter_by_text=filter_by_text,
83-
offset=offset,
84-
limit=limit,
85-
order_by=order_by,
86-
)
87-
88-
return total_count, workspaces
89-
90-
9157
async def update_workspace(
9258
app: web.Application,
9359
*,
@@ -133,67 +99,75 @@ async def delete_workspace(
13399
permission="delete",
134100
)
135101

136-
await db.delete_workspace(
137-
app,
138-
workspace_id=workspace_id,
139-
product_name=product_name,
140-
)
141-
142-
143-
async def get_user_workspace(
144-
app: web.Application,
145-
*,
146-
user_id: UserID,
147-
workspace_id: WorkspaceID,
148-
product_name: ProductName,
149-
permission: PermissionStr | None,
150-
) -> UserWorkspaceWithAccessRights:
151-
"""
152-
153-
Here checking access is optional. A use case is when the caller has guarantees that
154-
`user_id` has granted access and we do not want to re-check
102+
# Get all root projects
103+
for page_params in iter_pagination_params(limit=MAXIMUM_NUMBER_OF_ITEMS_PER_PAGE):
104+
(
105+
projects,
106+
page_params.total_number_of_items,
107+
) = await list_projects(
108+
app,
109+
user_id=user_id,
110+
product_name=product_name,
111+
show_hidden=True,
112+
workspace_id=workspace_id,
113+
project_type=ProjectTypeAPI.all,
114+
folder_id=None,
115+
trashed=None,
116+
offset=page_params.offset,
117+
limit=page_params.limit,
118+
order_by=OrderBy(
119+
field=IDStr("last_change_date"), direction=OrderDirection.DESC
120+
),
121+
)
155122

156-
Raises:
157-
WorkspaceAccessForbiddenError: if permission not None and user_id does not have access
158-
"""
159-
workspace: UserWorkspaceWithAccessRights = await db.get_workspace_for_user(
160-
app=app, user_id=user_id, workspace_id=workspace_id, product_name=product_name
161-
)
123+
workspace_root_projects: list[ProjectID] = [
124+
Project(**project).uuid for project in projects
125+
]
162126

163-
# NOTE: check here is optional
164-
if permission is not None:
165-
has_user_granted_permission = getattr(
166-
workspace.my_access_rights, permission, False
167-
)
168-
if not has_user_granted_permission:
169-
raise WorkspaceAccessForbiddenError(
170-
user_id=user_id,
171-
workspace_id=workspace_id,
172-
product_name=product_name,
173-
permission_checked=permission,
127+
# Delete projects properly
128+
await asyncio.gather(
129+
*(
130+
delete_project_by_user(app, project_uuid=project_uuid, user_id=user_id)
131+
for project_uuid in workspace_root_projects
174132
)
175-
return workspace
133+
)
176134

135+
# Get all root folders
136+
for page_params in iter_pagination_params(limit=MAXIMUM_NUMBER_OF_ITEMS_PER_PAGE):
137+
(
138+
folders,
139+
page_params.total_number_of_items,
140+
) = await list_folders(
141+
app,
142+
user_id=user_id,
143+
product_name=product_name,
144+
workspace_id=workspace_id,
145+
folder_id=None,
146+
trashed=None,
147+
offset=page_params.offset,
148+
limit=page_params.limit,
149+
order_by=OrderBy(field=IDStr("folder_id"), direction=OrderDirection.ASC),
150+
)
177151

178-
async def check_user_workspace_access(
179-
app: web.Application,
180-
*,
181-
user_id: UserID,
182-
workspace_id: WorkspaceID,
183-
product_name: ProductName,
184-
permission: PermissionStr,
185-
) -> UserWorkspaceWithAccessRights:
186-
"""
187-
As `get_user_workspace` but here access check is required
152+
workspace_root_folders: list[FolderID] = [
153+
folder.folder_db.folder_id for folder in folders
154+
]
155+
156+
# Delete folders properly
157+
await asyncio.gather(
158+
*(
159+
delete_folder(
160+
app,
161+
user_id=user_id,
162+
product_name=product_name,
163+
folder_id=folder_id,
164+
)
165+
for folder_id in workspace_root_folders
166+
)
167+
)
188168

189-
Raises:
190-
WorkspaceAccessForbiddenError
191-
"""
192-
return await get_user_workspace(
169+
await db.delete_workspace(
193170
app,
194-
user_id=user_id,
195171
workspace_id=workspace_id,
196172
product_name=product_name,
197-
# NOTE: check here is required
198-
permission=permission,
199173
)

0 commit comments

Comments
 (0)