Skip to content

Commit 722f3e5

Browse files
committed
server-client rpc interface
1 parent 9ea3cb6 commit 722f3e5

File tree

4 files changed

+178
-9
lines changed

4 files changed

+178
-9
lines changed
Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,38 @@
11
import logging
22

3+
from models_library.api_schemas_webserver import WEBSERVER_RPC_NAMESPACE
4+
from models_library.products import ProductName
35
from models_library.projects import ProjectID
4-
from pydantic import TypeAdapter
6+
from models_library.rabbitmq_basic_types import RPCMethodName
7+
from models_library.users import UserID
8+
from pydantic import TypeAdapter, validate_call
59
from servicelib.logging_utils import log_decorator
610
from servicelib.rabbitmq import RabbitMQRPCClient
711

812
_logger = logging.getLogger(__name__)
913

1014

1115
@log_decorator(_logger, level=logging.DEBUG)
16+
@validate_call(config={"arbitrary_types_allowed": True})
1217
async def mark_project_as_job(
1318
rpc_client: RabbitMQRPCClient,
1419
*,
20+
product_name: ProductName,
21+
user_id: UserID,
1522
project_uuid: ProjectID,
1623
job_parent_resource_name: str,
1724
) -> None:
1825

19-
assert rpc_client
20-
2126
assert not job_parent_resource_name.startswith("/") # nosec
2227
assert "/" in job_parent_resource_name # nosec
2328
assert not job_parent_resource_name.endswith("/") # nosec
2429

25-
TypeAdapter(ProjectID).validate_python(project_uuid)
26-
27-
raise NotImplementedError
30+
result = await rpc_client.request(
31+
WEBSERVER_RPC_NAMESPACE,
32+
TypeAdapter(RPCMethodName).validate_python("mark_project_as_job"),
33+
product_name=product_name,
34+
user_id=user_id,
35+
project_uuid=project_uuid,
36+
job_parent_resource_name=job_parent_resource_name,
37+
)
38+
assert result is None
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from aiohttp import web
2+
from models_library.api_schemas_webserver import WEBSERVER_RPC_NAMESPACE
3+
from models_library.products import ProductName
4+
from models_library.projects import ProjectID
5+
from models_library.users import UserID
6+
from pydantic import validate_call
7+
from servicelib.rabbitmq import RPCRouter
8+
9+
from ...rabbitmq import get_rabbitmq_rpc_server
10+
11+
router = RPCRouter()
12+
13+
14+
@router.expose()
15+
@validate_call(config={"arbitrary_types_allowed": True})
16+
async def mark_project_as_job(
17+
app: web.Application,
18+
*,
19+
product_name: ProductName,
20+
user_id: UserID,
21+
project_uuid: ProjectID,
22+
job_parent_resource_name: str,
23+
) -> None:
24+
msg = (
25+
f"You have reached {__name__} but this feature is still not implemented. "
26+
f"Inputs: {app=}, {product_name=}, {user_id=}, {project_uuid=}, {job_parent_resource_name=}"
27+
)
28+
raise NotImplementedError(msg)
29+
30+
31+
async def register_rpc_routes_on_startup(app: web.Application):
32+
rpc_server = get_rabbitmq_rpc_server(app)
33+
await rpc_server.register_router(router, WEBSERVER_RPC_NAMESPACE, app)

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@
1919
nodes_rest,
2020
ports_rest,
2121
projects_rest,
22+
projects_rpc,
23+
projects_slot,
2224
projects_states_rest,
2325
tags_rest,
2426
trash_rest,
2527
wallets_rest,
2628
workspaces_rest,
2729
)
28-
from ._controller.projects_slot import setup_project_observer_events
2930
from ._projects_repository_legacy import setup_projects_db
3031
from ._security_service import setup_projects_access
3132

@@ -48,9 +49,13 @@ def setup_projects(app: web.Application) -> bool:
4849
# database API
4950
setup_projects_db(app)
5051

51-
# registers event handlers (e.g. on_user_disconnect)
52-
setup_project_observer_events(app)
52+
# setup SLOT-controllers
53+
projects_slot.setup_project_observer_events(app)
5354

55+
# setup RPC-controllers
56+
app.on_startup.append(projects_rpc.register_rpc_routes_on_startup)
57+
58+
# setup REST-controllers
5459
app.router.add_routes(projects_states_rest.routes)
5560
app.router.add_routes(projects_rest.routes)
5661
app.router.add_routes(comments_rest.routes)
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# pylint: disable=redefined-outer-name
2+
# pylint: disable=unused-argument
3+
# pylint: disable=unused-variable
4+
# pylint: disable=too-many-arguments
5+
6+
7+
from collections.abc import AsyncIterator, Awaitable, Callable
8+
from uuid import UUID
9+
10+
import pytest
11+
from aiohttp.test_utils import TestClient
12+
from common_library.users_enums import UserRole
13+
from models_library.products import ProductName
14+
from models_library.projects import ProjectID
15+
from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict
16+
from pytest_simcore.helpers.typing_env import EnvVarsDict
17+
from pytest_simcore.helpers.webserver_login import NewUser, UserInfoDict
18+
from servicelib.rabbitmq import RabbitMQRPCClient
19+
from servicelib.rabbitmq.rpc_interfaces.webserver import projects as projects_rpc
20+
from settings_library.rabbit import RabbitSettings
21+
from simcore_service_webserver.application_settings import ApplicationSettings
22+
from simcore_service_webserver.projects.models import ProjectDict
23+
24+
pytest_simcore_core_services_selection = [
25+
"rabbit",
26+
]
27+
28+
29+
@pytest.fixture
30+
def user_role() -> UserRole:
31+
# for logged_user
32+
return UserRole.USER
33+
34+
35+
@pytest.fixture
36+
def app_environment(
37+
rabbit_service: RabbitSettings,
38+
app_environment: EnvVarsDict,
39+
monkeypatch: pytest.MonkeyPatch,
40+
):
41+
new_envs = setenvs_from_dict(
42+
monkeypatch,
43+
{
44+
**app_environment,
45+
"RABBIT_HOST": rabbit_service.RABBIT_HOST,
46+
"RABBIT_PORT": f"{rabbit_service.RABBIT_PORT}",
47+
"RABBIT_USER": rabbit_service.RABBIT_USER,
48+
"RABBIT_SECURE": f"{rabbit_service.RABBIT_SECURE}",
49+
"RABBIT_PASSWORD": rabbit_service.RABBIT_PASSWORD.get_secret_value(),
50+
},
51+
)
52+
53+
settings = ApplicationSettings.create_from_envs()
54+
assert settings.WEBSERVER_RABBITMQ
55+
56+
return new_envs
57+
58+
59+
@pytest.fixture
60+
async def rpc_client(
61+
rabbitmq_rpc_client: Callable[[str], Awaitable[RabbitMQRPCClient]],
62+
) -> RabbitMQRPCClient:
63+
return await rabbitmq_rpc_client("client")
64+
65+
66+
@pytest.fixture
67+
async def other_user(
68+
client: TestClient,
69+
logged_user: UserInfoDict,
70+
) -> AsyncIterator[UserInfoDict]:
71+
72+
async with NewUser(
73+
user_data={
74+
"name": "other-user",
75+
"email": "other-user" + logged_user["email"],
76+
},
77+
app=client.app,
78+
) as other_user_info:
79+
80+
assert other_user_info["name"] != logged_user["name"]
81+
yield other_user_info
82+
83+
84+
async def test_rpc_client_mark_project_as_job(
85+
rpc_client: RabbitMQRPCClient,
86+
product_name: ProductName,
87+
logged_user: UserInfoDict,
88+
other_user: UserInfoDict,
89+
user_project: ProjectDict,
90+
):
91+
# `logged_user` OWNS the `user_project` but not `other_user`
92+
project_uuid: ProjectID = UUID(user_project["uuid"])
93+
user_id = logged_user["id"]
94+
other_user_id = other_user["id"]
95+
96+
await projects_rpc.mark_project_as_job(
97+
rpc_client=rpc_client,
98+
product_name=product_name,
99+
user_id=user_id,
100+
project_uuid=project_uuid,
101+
job_parent_resource_name="solvers/solver123/version/1.2.3",
102+
)
103+
104+
with pytest.raises(Exception, match="no access"):
105+
await projects_rpc.mark_project_as_job(
106+
rpc_client=rpc_client,
107+
product_name=product_name,
108+
user_id=other_user_id, # <-- no access
109+
project_uuid=project_uuid,
110+
job_parent_resource_name="solvers/solver123/version/1.2.3",
111+
)
112+
113+
with pytest.raises(Exception, match="not found"):
114+
await projects_rpc.mark_project_as_job(
115+
rpc_client=rpc_client,
116+
product_name=product_name,
117+
user_id=logged_user["id"],
118+
project_uuid=UUID("00000000-0000-0000-0000-000000000000"), # <-- wont find
119+
job_parent_resource_name="solvers/solver123/version/1.2.3",
120+
)

0 commit comments

Comments
 (0)