From 3ae7bfac75c3a32d24770ed0d616ae078696fddd Mon Sep 17 00:00:00 2001 From: sanderegg <35365065+sanderegg@users.noreply.github.com> Date: Fri, 17 Jan 2025 08:52:29 +0100 Subject: [PATCH] moving decorator out of api --- .../exporter/_handlers.py | 13 +++++++---- .../projects/_crud_api_create.py | 13 +++++++---- .../projects/_projects_locks_utils.py | 23 +++++++------------ .../simcore_service_webserver/projects/api.py | 2 ++ .../projects/projects_api.py | 19 ++++++++++----- .../02/test_projects_crud_handlers__delete.py | 11 ++++----- 6 files changed, 46 insertions(+), 35 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/exporter/_handlers.py b/services/web/server/src/simcore_service_webserver/exporter/_handlers.py index a730150dbe7..c706b16405f 100644 --- a/services/web/server/src/simcore_service_webserver/exporter/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/exporter/_handlers.py @@ -6,13 +6,15 @@ from aiofiles.tempfile import TemporaryDirectory as AioTemporaryDirectory from aiohttp import web +from models_library.projects_access import Owner from models_library.projects_state import ProjectStatus from servicelib.request_keys import RQT_USERID_KEY from .._constants import RQ_PRODUCT_KEY from .._meta import API_VTAG from ..login.decorators import login_required -from ..projects.projects_api import with_project_locked_and_notify +from ..projects.api import with_project_locked_and_notify +from ..projects.projects_api import retrieve_and_notify_project_locked_state from ..security.decorators import permission_required from ..users.api import get_user_fullname from ._formatter.archive import get_sds_archive_path @@ -47,9 +49,12 @@ async def export_project(request: web.Request): request.app, project_uuid=project_uuid, status=ProjectStatus.EXPORTING, - user_id=user_id, - user_name=await get_user_fullname(request.app, user_id=user_id), - notify_users=True, + owner=Owner( + user_id=user_id, **await get_user_fullname(request.app, user_id=user_id) + ), + notification_cb=retrieve_and_notify_project_locked_state( + user_id, project_uuid, request.app + ), ) async def _() -> tuple[Callable[[], Coroutine[Any, Any, None]], Path]: # @GitHK what is this supposed to be doing?? diff --git a/services/web/server/src/simcore_service_webserver/projects/_crud_api_create.py b/services/web/server/src/simcore_service_webserver/projects/_crud_api_create.py index 9e56b7b7dcb..5380a59ff5e 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_crud_api_create.py +++ b/services/web/server/src/simcore_service_webserver/projects/_crud_api_create.py @@ -9,6 +9,7 @@ from models_library.api_schemas_long_running_tasks.base import ProgressPercent from models_library.api_schemas_webserver.projects import ProjectGet from models_library.projects import ProjectID +from models_library.projects_access import Owner from models_library.projects_nodes_io import NodeID, NodeIDStr from models_library.projects_state import ProjectStatus from models_library.users import UserID @@ -39,6 +40,7 @@ from . import projects_api from ._metadata_api import set_project_ancestors from ._permalink_api import update_or_pop_permalink_in_project +from ._projects_locks_utils import with_project_locked_and_notify from .db import ProjectDBAPI from .exceptions import ( ParentNodeNotFoundError, @@ -180,13 +182,16 @@ async def _copy() -> None: await long_running_task.result() if needs_lock_source_project: - await projects_api.with_project_locked_and_notify( + await with_project_locked_and_notify( app, project_uuid=source_project["uuid"], status=ProjectStatus.CLONING, - user_id=user_id, - user_name=await get_user_fullname(app, user_id=user_id), - notify_users=True, + owner=Owner( + user_id=user_id, **await get_user_fullname(app, user_id=user_id) + ), + notification_cb=projects_api.retrieve_and_notify_project_locked_state( + user_id, source_project["uuid"], app + ), )(_copy)() else: await _copy() diff --git a/services/web/server/src/simcore_service_webserver/projects/_projects_locks_utils.py b/services/web/server/src/simcore_service_webserver/projects/_projects_locks_utils.py index c666c274f1c..b0a01765dc0 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_projects_locks_utils.py +++ b/services/web/server/src/simcore_service_webserver/projects/_projects_locks_utils.py @@ -1,6 +1,6 @@ from collections.abc import Callable, Coroutine from functools import wraps -from typing import Any, ParamSpec, TypeVar +from typing import Any, Awaitable, ParamSpec, TypeVar from aiohttp import web from models_library.projects_access import Owner @@ -8,8 +8,6 @@ from servicelib.redis._project_lock import with_project_locked from ..redis import get_redis_lock_manager_client_sdk -from ..users.api import FullNameDict -from .projects_api import retrieve_and_notify_project_locked_state P = ParamSpec("P") R = TypeVar("R") @@ -20,9 +18,8 @@ def with_project_locked_and_notify( *, project_uuid: str, status: ProjectStatus, - user_id: int, - user_name: FullNameDict, - notify_users: bool, + owner: Owner, + notification_cb: Awaitable | None, ) -> Callable[ [Callable[P, Coroutine[Any, Any, R]]], Callable[P, Coroutine[Any, Any, R]] ]: @@ -35,21 +32,17 @@ async def _wrapper(*args: P.args, **kwargs: P.kwargs) -> R: get_redis_lock_manager_client_sdk(app), project_uuid=project_uuid, status=status, - owner=Owner(user_id=user_id, **user_name), + owner=owner, ) async def _locked_func() -> R: - if notify_users: - await retrieve_and_notify_project_locked_state( - user_id, project_uuid, app - ) + if notification_cb is not None: + await notification_cb return await func(*args, **kwargs) result = await _locked_func() - if notify_users: - await retrieve_and_notify_project_locked_state( - user_id, project_uuid, app - ) + if notification_cb is not None: + await notification_cb return result return _wrapper diff --git a/services/web/server/src/simcore_service_webserver/projects/api.py b/services/web/server/src/simcore_service_webserver/projects/api.py index c7b44426c83..d965a8e1a02 100644 --- a/services/web/server/src/simcore_service_webserver/projects/api.py +++ b/services/web/server/src/simcore_service_webserver/projects/api.py @@ -11,6 +11,7 @@ ) from ._permalink_api import ProjectPermalink from ._permalink_api import register_factory as register_permalink_factory +from ._projects_locks_utils import with_project_locked_and_notify from ._wallets_api import connect_wallet_to_project, get_project_wallet __all__: tuple[str, ...] = ( @@ -22,6 +23,7 @@ "has_user_project_access_rights", "ProjectPermalink", "register_permalink_factory", + "with_project_locked_and_notify", ) diff --git a/services/web/server/src/simcore_service_webserver/projects/projects_api.py b/services/web/server/src/simcore_service_webserver/projects/projects_api.py index 5d3b66adbd0..84570c548ce 100644 --- a/services/web/server/src/simcore_service_webserver/projects/projects_api.py +++ b/services/web/server/src/simcore_service_webserver/projects/projects_api.py @@ -128,6 +128,7 @@ ) from ._db_utils import PermissionStr from ._nodes_utils import set_reservation_same_as_limit, validate_new_service_resources +from ._projects_locks_utils import with_project_locked_and_notify from ._wallets_api import connect_wallet_to_project, get_project_wallet from .db import APP_PROJECT_DBAPI, ProjectDBAPI from .exceptions import ( @@ -1260,9 +1261,12 @@ async def try_open_project_for_user( app, project_uuid=project_uuid, status=ProjectStatus.OPENING, - user_id=user_id, - user_name=await get_user_fullname(app, user_id=user_id), - notify_users=True, + owner=Owner( + user_id=user_id, **await get_user_fullname(app, user_id=user_id) + ), + notification_cb=retrieve_and_notify_project_locked_state( + user_id, project_uuid, app + ), ) async def _open_project() -> bool: with managed_resource(user_id, client_session_id, app) as user_session: @@ -1756,9 +1760,12 @@ async def remove_project_dynamic_services( app, project_uuid=project_uuid, status=ProjectStatus.CLOSING, - user_id=user_id, - user_name=user_name_data, - notify_users=notify_users, + owner=Owner(user_id=user_id, **user_name_data), + notification_cb=( + retrieve_and_notify_project_locked_state(user_id, project_uuid, app) + if notify_users + else None + ), ) async def _locked_stop_dynamic_serivces_in_project() -> None: # save the state if the user is not a guest. if we do not know we save in any case. diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__delete.py b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__delete.py index 24bd7d506bd..28f34a02e8f 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__delete.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__delete.py @@ -20,6 +20,7 @@ DynamicServiceStop, ) from models_library.projects import ProjectID +from models_library.projects_access import Owner from models_library.projects_state import ProjectStatus from pytest_simcore.helpers.assert_checks import assert_status from pytest_simcore.helpers.webserver_login import UserInfoDict @@ -34,11 +35,9 @@ from simcore_postgres_database.models.projects_to_products import projects_to_products from simcore_service_webserver._meta import api_version_prefix from simcore_service_webserver.db.models import UserRole +from simcore_service_webserver.exporter._handlers import with_project_locked_and_notify from simcore_service_webserver.projects import _crud_api_delete from simcore_service_webserver.projects.models import ProjectDict -from simcore_service_webserver.projects.projects_api import ( - with_project_locked_and_notify, -) from socketio.exceptions import ConnectionError as SocketConnectionError @@ -225,6 +224,7 @@ async def test_delete_project_while_it_is_locked_raises_error( logged_user: UserInfoDict, user_project: ProjectDict, expected: ExpectedResponse, + faker: Faker, ): assert client.app @@ -234,7 +234,6 @@ async def test_delete_project_while_it_is_locked_raises_error( app=client.app, project_uuid=project_uuid, status=ProjectStatus.CLOSING, - user_id=user_id, - user_name={"first_name": "test", "last_name": "test"}, - notify_users=False, + owner=Owner(user_id=user_id, first_name=faker.name(), last_name=faker.name()), + notification_cb=None, )(_request_delete_project)(client, user_project, expected.conflict)