diff --git a/services/web/server/src/simcore_service_webserver/projects/projects_service.py b/services/web/server/src/simcore_service_webserver/projects/projects_service.py index c29ffb627856..572034033752 100644 --- a/services/web/server/src/simcore_service_webserver/projects/projects_service.py +++ b/services/web/server/src/simcore_service_webserver/projects/projects_service.py @@ -1036,6 +1036,26 @@ async def is_project_hidden(app: web.Application, project_id: ProjectID) -> bool return await db.is_hidden(project_id) +async def set_project_hidden_flag( + app: web.Application, + *, + product_name: ProductName, + user_id: UserID, + project_id: ProjectID, + hidden: bool, +) -> None: + await check_user_project_permission( + app, + project_id=project_id, + user_id=user_id, + product_name=product_name, + permission="write", + ) + + projects_repo: ProjectDBAPI = app[APP_PROJECT_DBAPI] + await projects_repo.set_hidden_flag(project_id, hidden=hidden) + + async def patch_project_node( app: web.Application, *, diff --git a/services/web/server/src/simcore_service_webserver/trash/_rest.py b/services/web/server/src/simcore_service_webserver/trash/_rest.py index 045593f62185..e3d0503ac3a1 100644 --- a/services/web/server/src/simcore_service_webserver/trash/_rest.py +++ b/services/web/server/src/simcore_service_webserver/trash/_rest.py @@ -1,4 +1,3 @@ -import asyncio import logging from aiohttp import web @@ -50,10 +49,7 @@ async def empty_trash(request: web.Request): user_id = get_user_id(request) product_name = products_web.get_product_name(request) - is_fired = asyncio.Event() - async def _empty_trash(): - is_fired.set() await _service.safe_empty_trash( request.app, product_name=product_name, user_id=user_id ) @@ -64,10 +60,7 @@ async def _empty_trash(): fire_and_forget_tasks_collection=request.app[APP_FIRE_AND_FORGET_TASKS_KEY], ) - # NOTE: Ensures `fire_and_forget_task` is triggered; otherwise, - # when the front-end requests the trash item list, - # it may still display items, misleading the user into - # thinking the `empty trash` operation failed. - await is_fired.wait() + # ensure trashed entities are hidden before returning response + await _service.hide_trashed(request.app, product_name=product_name, user_id=user_id) return web.json_response(status=status.HTTP_204_NO_CONTENT) diff --git a/services/web/server/src/simcore_service_webserver/trash/_service.py b/services/web/server/src/simcore_service_webserver/trash/_service.py index 8f7dd70d0beb..bac33f23ad0b 100644 --- a/services/web/server/src/simcore_service_webserver/trash/_service.py +++ b/services/web/server/src/simcore_service_webserver/trash/_service.py @@ -11,7 +11,7 @@ from ..folders import folders_trash_service from ..products import products_service -from ..projects import projects_trash_service +from ..projects import projects_service, projects_trash_service from .settings import get_plugin_settings _logger = logging.getLogger(__name__) @@ -35,8 +35,9 @@ async def _empty_explicitly_trashed_projects( with log_context( _logger, logging.DEBUG, - "Deleting %s explicitly trashed projects", + "Deleting %s explicitly trashed projects: %s", len(trashed_projects_ids), + trashed_projects_ids, ): for project_id in trashed_projects_ids: try: @@ -57,7 +58,6 @@ async def _empty_explicitly_trashed_projects( "product_name": product_name, "user_id": user_id, }, - tip=_TIP, ) ) @@ -72,8 +72,9 @@ async def _empty_explicitly_trashed_folders_and_content( with log_context( _logger, logging.DEBUG, - "Deleting %s trashed folders (and all its content)", + "Deleting %s trashed folders (and all its content): %s", len(trashed_folders_ids), + trashed_folders_ids, ): for folder_id in trashed_folders_ids: try: @@ -99,6 +100,50 @@ async def _empty_explicitly_trashed_folders_and_content( ) +async def _hide_trashed_projects( + app: web.Application, product_name: ProductName, user_id: UserID +): + trashed_projects_ids = ( + await projects_trash_service.list_explicitly_trashed_projects( + app=app, product_name=product_name, user_id=user_id + ) + ) + with log_context( + _logger, + logging.DEBUG, + "Hiding %s explicitly trashed projects", + len(trashed_projects_ids), + ): + for project_id in trashed_projects_ids: + try: + await projects_service.set_project_hidden_flag( + app, + product_name=product_name, + user_id=user_id, + project_id=project_id, + hidden=True, + ) + except Exception as exc: # pylint: disable=broad-exception-caught + _logger.warning( + **create_troubleshotting_log_kwargs( + "Error hiding a trashed project while emptying trash.", + error=exc, + error_context={ + "project_id": project_id, + "product_name": product_name, + "user_id": user_id, + }, + tip=_TIP, + ) + ) + + +async def hide_trashed( + app: web.Application, *, product_name: ProductName, user_id: UserID +): + await _hide_trashed_projects(app, product_name, user_id) + + async def safe_empty_trash( app: web.Application, *, product_name: ProductName, user_id: UserID ):