77from models_library .products import ProductName
88from models_library .projects import ProjectID
99from models_library .users import UserID
10+ from servicelib .aiohttp .application_keys import APP_FIRE_AND_FORGET_TASKS_KEY
1011from servicelib .common_headers import UNDEFINED_DEFAULT_SIMCORE_USER_AGENT_VALUE
11- from simcore_service_webserver . director_v2 . exceptions import DirectorServiceError
12+ from servicelib . utils import fire_and_forget_task
1213
1314from ..director_v2 import api as director_v2_api
1415from . import projects_api
15- from .exceptions import ProjectLockError , ProjectRunningConflictError , ProjectStopError
16+ from ._access_rights_api import check_user_project_permission
17+ from .exceptions import ProjectRunningConflictError
1618from .models import ProjectPatchExtended
1719from .settings import get_plugin_settings
1820
@@ -43,54 +45,73 @@ async def prune_all_trashes(app: web.Application) -> list[str]:
4345 return []
4446
4547
48+ async def _is_project_running (
49+ app : web .Application ,
50+ * ,
51+ user_id : UserID ,
52+ project_id : ProjectID ,
53+ ) -> bool :
54+ return bool (
55+ # computational
56+ await director_v2_api .is_pipeline_running (
57+ app , user_id = user_id , project_id = project_id
58+ )
59+ ) or bool (
60+ # dynamic
61+ await director_v2_api .list_dynamic_services (
62+ app , user_id = user_id , project_id = f"{ project_id } "
63+ )
64+ )
65+
66+
4667async def trash_project (
4768 app : web .Application ,
4869 * ,
4970 product_name : ProductName ,
5071 user_id : UserID ,
5172 project_id : ProjectID ,
52- forced : bool ,
73+ force_stop_first : bool ,
5374):
5475 """
76+
5577 Raises:
5678 ProjectStopError:
5779 ProjectRunningConflictError:
5880 """
81+ await check_user_project_permission (
82+ app ,
83+ project_id = project_id ,
84+ user_id = user_id ,
85+ product_name = product_name ,
86+ permission = "write" ,
87+ )
88+
89+ if force_stop_first :
90+ # NOTE: schedules stop-project
5991
60- if forced :
61- # SCHEDULE stop!
62- try :
63- await projects_api .remove_project_dynamic_services (
92+ stop_project_task = asyncio .gather (
93+ director_v2_api .stop_pipeline (app , user_id = user_id , project_id = project_id ),
94+ projects_api .remove_project_dynamic_services (
6495 user_id = user_id ,
6596 project_uuid = f"{ project_id } " ,
6697 app = app ,
6798 simcore_user_agent = UNDEFINED_DEFAULT_SIMCORE_USER_AGENT_VALUE ,
6899 notify_users = False ,
69- )
100+ ),
101+ )
70102
71- await director_v2_api .delete_pipeline (
72- app , user_id = user_id , project_id = project_id , force = forced
73- )
103+ fire_and_forget_task (
104+ stop_project_task ,
105+ task_suffix_name = f"trash_project_stop_project_{ user_id = } _{ project_id = } " ,
106+ fire_and_forget_tasks_collection = app [APP_FIRE_AND_FORGET_TASKS_KEY ],
107+ )
74108
75- except (DirectorServiceError , ProjectLockError ) as exc :
76- raise ProjectStopError (
77- project_uuid = project_id ,
78- user_id = user_id ,
79- product_name = product_name ,
80- from_err = exc ,
81- ) from exc
82-
83- else :
84- # NOTE: must do here as well for dynamic services but needs refactoring!
85- running = await director_v2_api .is_pipeline_running (
86- app = app , user_id = user_id , project_id = project_id
109+ elif _is_project_running (app , user_id = user_id , project_id = project_id ):
110+ raise ProjectRunningConflictError (
111+ project_uuid = project_id ,
112+ user_id = user_id ,
113+ product_name = product_name ,
87114 )
88- if running :
89- raise ProjectRunningConflictError (
90- project_uuid = project_id ,
91- user_id = user_id ,
92- product_name = product_name ,
93- )
94115
95116 # mark as trash
96117 await projects_api .patch_project (
@@ -109,6 +130,7 @@ async def untrash_project(
109130 user_id : UserID ,
110131 project_id : ProjectID ,
111132):
133+ # NOTE: check_user_project_permission is inside projects_api.patch_project
112134 await projects_api .patch_project (
113135 app ,
114136 user_id = user_id ,
0 commit comments