Skip to content

Commit e9b432a

Browse files
fix issues after resolving conflicts
1 parent 44b4f36 commit e9b432a

File tree

1 file changed

+88
-7
lines changed

1 file changed

+88
-7
lines changed

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

Lines changed: 88 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,17 @@
2929
DynamicServiceStart,
3030
DynamicServiceStop,
3131
)
32-
from models_library.api_schemas_webserver.projects import ProjectGet, ProjectPatch
32+
from models_library.api_schemas_webserver.projects import (
33+
ProjectDocument,
34+
ProjectGet,
35+
ProjectPatch,
36+
)
3337
from models_library.basic_types import KeyIDStr
3438
from models_library.errors import ErrorDict
3539
from models_library.groups import GroupID
3640
from models_library.products import ProductName
37-
from models_library.projects import (
38-
Project,
39-
ProjectID,
40-
)
41+
from models_library.projects import Project, ProjectID, ProjectTemplateType
42+
from models_library.projects import ProjectType as ProjectTypeAPI
4143
from models_library.projects_access import Owner
4244
from models_library.projects_nodes import Node, NodeState, PartialNode
4345
from models_library.projects_nodes_io import NodeID, NodeIDStr, PortLink
@@ -82,8 +84,10 @@
8284
ServiceWasNotFoundError,
8385
)
8486
from servicelib.redis import (
87+
PROJECT_DB_UPDATE_REDIS_LOCK_KEY,
8588
exclusive,
8689
get_project_locked_state,
90+
increment_and_return_project_document_version,
8791
is_project_locked,
8892
with_project_locked,
8993
)
@@ -103,6 +107,7 @@
103107
from ..products import products_web
104108
from ..rabbitmq import get_rabbitmq_rpc_client
105109
from ..redis import (
110+
get_redis_document_manager_client_sdk,
106111
get_redis_lock_manager_client_sdk,
107112
)
108113
from ..resource_manager.user_sessions import (
@@ -142,6 +147,7 @@
142147
from ._nodes_utils import set_reservation_same_as_limit, validate_new_service_resources
143148
from ._projects_repository_legacy import APP_PROJECT_DBAPI, ProjectDBAPI
144149
from ._projects_repository_legacy_utils import PermissionStr
150+
from ._socketio import notify_project_document_updated
145151
from .exceptions import (
146152
ClustersKeeperNotAvailableError,
147153
DefaultPricingUnitNotFoundError,
@@ -169,6 +175,81 @@
169175
PROJECT_REDIS_LOCK_KEY: str = "project:{}"
170176

171177

178+
async def patch_project_and_notify_users(
179+
app: web.Application,
180+
*,
181+
project_uuid: ProjectID,
182+
patch_project_data: dict[str, Any],
183+
user_primary_gid: GroupID,
184+
) -> None:
185+
"""
186+
Patches a project and notifies users involved in the project with version control.
187+
188+
This function performs the following operations atomically:
189+
1. Patches the project in the database
190+
2. Retrieves the updated project with workbench
191+
3. Creates a project document
192+
4. Increments the document version
193+
5. Notifies users about the project update
194+
195+
Args:
196+
app: The web application instance
197+
project_uuid: The project UUID to patch
198+
patch_project_data: Dictionary containing the project data to patch
199+
user_primary_gid: Primary group ID of the user making the change
200+
201+
Note:
202+
This function is decorated with Redis exclusive lock to ensure
203+
thread-safe operations on the project document.
204+
"""
205+
206+
@exclusive(
207+
get_redis_lock_manager_client_sdk(app),
208+
lock_key=PROJECT_DB_UPDATE_REDIS_LOCK_KEY.format(project_uuid),
209+
blocking=True,
210+
blocking_timeout=datetime.timedelta(seconds=30),
211+
)
212+
async def _patch_and_notify() -> None:
213+
await _projects_repository.patch_project(
214+
app=app,
215+
project_uuid=project_uuid,
216+
new_partial_project_data=patch_project_data,
217+
)
218+
project_with_workbench = await _projects_repository.get_project_with_workbench(
219+
app=app, project_uuid=project_uuid
220+
)
221+
project_document = ProjectDocument(
222+
uuid=project_with_workbench.uuid,
223+
workspace_id=project_with_workbench.workspace_id,
224+
name=project_with_workbench.name,
225+
description=project_with_workbench.description,
226+
thumbnail=project_with_workbench.thumbnail,
227+
last_change_date=project_with_workbench.last_change_date,
228+
classifiers=project_with_workbench.classifiers,
229+
dev=project_with_workbench.dev,
230+
quality=project_with_workbench.quality,
231+
workbench=project_with_workbench.workbench,
232+
ui=project_with_workbench.ui,
233+
type=cast(ProjectTypeAPI, project_with_workbench.type),
234+
template_type=cast(
235+
ProjectTemplateType, project_with_workbench.template_type
236+
),
237+
)
238+
redis_client_sdk = get_redis_document_manager_client_sdk(app)
239+
document_version = await increment_and_return_project_document_version(
240+
redis_client=redis_client_sdk, project_uuid=project_uuid
241+
)
242+
await notify_project_document_updated(
243+
app=app,
244+
project_id=project_uuid,
245+
user_primary_gid=user_primary_gid,
246+
version=document_version,
247+
document=project_document,
248+
)
249+
250+
await _patch_and_notify()
251+
252+
172253
def _is_node_dynamic(node_key: str) -> bool:
173254
return "/dynamic/" in node_key
174255

@@ -349,10 +430,10 @@ async def patch_project_for_user(
349430

350431
# 5. If patching template type
351432
if new_template_type := patch_project_data.get("template_type"):
352-
# 4.1 Check if user is a tester
433+
# 5.1 Check if user is a tester
353434
if UserRole(current_user["role"]) < UserRole.TESTER:
354435
raise InsufficientRoleForProjectTemplateTypeUpdateError
355-
# 4.2 Check the compatibility of the template type with the project
436+
# 5.2 Check the compatibility of the template type with the project
356437
if project_db.type == ProjectType.STANDARD and new_template_type is not None:
357438
raise ProjectTypeAndTemplateIncompatibilityError(
358439
project_uuid=project_uuid,

0 commit comments

Comments
 (0)