Skip to content

Commit 83c04f5

Browse files
author
Andrei Neagu
committed
redirect get_project_inactivity
1 parent 92ddd6c commit 83c04f5

File tree

11 files changed

+143
-30
lines changed

11 files changed

+143
-30
lines changed

packages/models-library/src/models_library/api_schemas_directorv2/dynamic_services.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,5 @@ class DynamicServiceCreate(ServiceDetails):
7979

8080
class GetProjectInactivityResponse(BaseModel):
8181
is_inactive: bool
82+
83+
model_config = ConfigDict(json_schema_extra={"example": {"is_inactive": "false"}})

packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/dynamic_scheduler/services.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import logging
22
from typing import Final
33

4-
from models_library.api_schemas_directorv2.dynamic_services import DynamicServiceGet
4+
from models_library.api_schemas_directorv2.dynamic_services import (
5+
DynamicServiceGet,
6+
GetProjectInactivityResponse,
7+
)
58
from models_library.api_schemas_dynamic_scheduler import DYNAMIC_SCHEDULER_RPC_NAMESPACE
69
from models_library.api_schemas_dynamic_scheduler.dynamic_services import (
710
DynamicServiceStart,
@@ -93,3 +96,21 @@ async def stop_dynamic_service(
9396
timeout_s=timeout_s,
9497
)
9598
assert result is None # nosec
99+
100+
101+
@log_decorator(_logger, level=logging.DEBUG)
102+
async def get_project_inactivity(
103+
rabbitmq_rpc_client: RabbitMQRPCClient,
104+
*,
105+
project_id: ProjectID,
106+
max_inactivity_seconds: NonNegativeInt,
107+
) -> GetProjectInactivityResponse:
108+
result = await rabbitmq_rpc_client.request(
109+
DYNAMIC_SCHEDULER_RPC_NAMESPACE,
110+
_RPC_METHOD_NAME_ADAPTER.validate_python("get_project_inactivity"),
111+
project_id=project_id,
112+
max_inactivity_seconds=max_inactivity_seconds,
113+
timeout_s=_RPC_DEFAULT_TIMEOUT_S,
114+
)
115+
assert isinstance(result, GetProjectInactivityResponse) # nosec
116+
return result

services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/api/rpc/_services.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
from fastapi import FastAPI
2-
from models_library.api_schemas_directorv2.dynamic_services import DynamicServiceGet
2+
from models_library.api_schemas_directorv2.dynamic_services import (
3+
DynamicServiceGet,
4+
GetProjectInactivityResponse,
5+
)
36
from models_library.api_schemas_dynamic_scheduler.dynamic_services import (
47
DynamicServiceStart,
58
DynamicServiceStop,
@@ -8,6 +11,7 @@
811
from models_library.projects import ProjectID
912
from models_library.projects_nodes_io import NodeID
1013
from models_library.users import UserID
14+
from pydantic import NonNegativeInt
1115
from servicelib.rabbitmq import RPCRouter
1216
from servicelib.rabbitmq.rpc_interfaces.dynamic_scheduler.errors import (
1317
ServiceWaitingForManualInterventionError,
@@ -56,3 +60,12 @@ async def stop_dynamic_service(
5660
return await scheduler_interface.stop_dynamic_service(
5761
app, dynamic_service_stop=dynamic_service_stop
5862
)
63+
64+
65+
@router.expose()
66+
async def get_project_inactivity(
67+
app: FastAPI, *, project_id: ProjectID, max_inactivity_seconds: NonNegativeInt
68+
) -> GetProjectInactivityResponse:
69+
return await scheduler_interface.get_project_inactivity(
70+
app, project_id=project_id, max_inactivity_seconds=max_inactivity_seconds
71+
)

services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/services/director_v2/_public_client.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@
22
from typing import Any
33

44
from fastapi import FastAPI, status
5-
from models_library.api_schemas_directorv2.dynamic_services import DynamicServiceGet
5+
from models_library.api_schemas_directorv2.dynamic_services import (
6+
DynamicServiceGet,
7+
GetProjectInactivityResponse,
8+
)
69
from models_library.api_schemas_dynamic_scheduler.dynamic_services import (
710
DynamicServiceStart,
811
)
912
from models_library.api_schemas_webserver.projects_nodes import NodeGet, NodeGetIdle
1013
from models_library.projects import ProjectID
1114
from models_library.projects_nodes_io import NodeID
1215
from models_library.users import UserID
13-
from pydantic import TypeAdapter
16+
from pydantic import NonNegativeInt, TypeAdapter
1417
from servicelib.fastapi.app_state import SingletonInAppStateMixin
1518
from servicelib.fastapi.http_client import AttachLifespanMixin, HasClientSetupInterface
1619
from servicelib.fastapi.http_client_thin import UnexpectedStatusError
@@ -108,6 +111,16 @@ async def list_tracked_dynamic_services(
108111
)
109112
return TypeAdapter(list[DynamicServiceGet]).validate_python(response.json())
110113

114+
async def get_project_inactivity(
115+
self, *, project_id: ProjectID, max_inactivity_seconds: NonNegativeInt
116+
) -> GetProjectInactivityResponse:
117+
response = await self.thin_client.get_projects_inactivity(
118+
project_id=project_id, max_inactivity_seconds=max_inactivity_seconds
119+
)
120+
return TypeAdapter(GetProjectInactivityResponse).validate_python(
121+
response.json()
122+
)
123+
111124

112125
def setup_director_v2(app: FastAPI) -> None:
113126
public_client = DirectorV2Client(app)

services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/services/director_v2/_thin_client.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from models_library.projects_nodes_io import NodeID
1313
from models_library.services_resources import ServiceResourcesDictHelpers
1414
from models_library.users import UserID
15+
from pydantic import NonNegativeInt
1516
from servicelib.common_headers import (
1617
X_DYNAMIC_SIDECAR_REQUEST_DNS,
1718
X_DYNAMIC_SIDECAR_REQUEST_SCHEME,
@@ -124,3 +125,13 @@ async def get_dynamic_services(
124125
"/dynamic_services",
125126
params=as_dict_exclude_unset(user_id=user_id, project_id=project_id),
126127
)
128+
129+
@retry_on_errors()
130+
@expect_status(status.HTTP_200_OK)
131+
async def get_projects_inactivity(
132+
self, *, project_id: ProjectID, max_inactivity_seconds: NonNegativeInt
133+
) -> Response:
134+
return await self.client.get(
135+
f"/dynamic_services/projects/{project_id}/inactivity",
136+
params={"max_inactivity_seconds": max_inactivity_seconds},
137+
)

services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/services/scheduler_interface.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
from fastapi import FastAPI
2-
from models_library.api_schemas_directorv2.dynamic_services import DynamicServiceGet
2+
from models_library.api_schemas_directorv2.dynamic_services import (
3+
DynamicServiceGet,
4+
GetProjectInactivityResponse,
5+
)
36
from models_library.api_schemas_dynamic_scheduler.dynamic_services import (
47
DynamicServiceStart,
58
DynamicServiceStop,
@@ -8,6 +11,7 @@
811
from models_library.projects import ProjectID
912
from models_library.projects_nodes_io import NodeID
1013
from models_library.users import UserID
14+
from pydantic import NonNegativeInt
1115

1216
from ..core.settings import ApplicationSettings
1317
from .director_v2 import DirectorV2Client
@@ -73,3 +77,19 @@ async def stop_dynamic_service(
7377
)
7478

7579
await set_request_as_stopped(app, dynamic_service_stop)
80+
81+
82+
async def get_project_inactivity(
83+
app: FastAPI, *, project_id: ProjectID, max_inactivity_seconds: NonNegativeInt
84+
) -> GetProjectInactivityResponse:
85+
settings: ApplicationSettings = app.state.settings
86+
if settings.DYNAMIC_SCHEDULER_USE_INTERNAL_SCHEDULER:
87+
raise NotImplementedError
88+
89+
director_v2_client = DirectorV2Client.get_from_app_state(app)
90+
response: GetProjectInactivityResponse = (
91+
await director_v2_client.get_project_inactivity(
92+
project_id=project_id, max_inactivity_seconds=max_inactivity_seconds
93+
)
94+
)
95+
return response

services/dynamic-scheduler/tests/unit/api_rpc/test_api_rpc__services.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99
from faker import Faker
1010
from fastapi import FastAPI, status
1111
from fastapi.encoders import jsonable_encoder
12-
from models_library.api_schemas_directorv2.dynamic_services import DynamicServiceGet
12+
from models_library.api_schemas_directorv2.dynamic_services import (
13+
DynamicServiceGet,
14+
GetProjectInactivityResponse,
15+
)
1316
from models_library.api_schemas_dynamic_scheduler.dynamic_services import (
1417
DynamicServiceStart,
1518
DynamicServiceStop,
@@ -490,3 +493,37 @@ async def test_stop_dynamic_service_serializes_generic_errors(
490493
),
491494
timeout_s=5,
492495
)
496+
497+
498+
@pytest.fixture
499+
def inactivity_response() -> GetProjectInactivityResponse:
500+
return TypeAdapter(GetProjectInactivityResponse).validate_python(
501+
GetProjectInactivityResponse.model_json_schema()["example"]
502+
)
503+
504+
505+
@pytest.fixture
506+
def mock_director_v2_get_project_inactivity(
507+
project_id: ProjectID, inactivity_response: GetProjectInactivityResponse
508+
) -> Iterator[None]:
509+
with respx.mock(
510+
base_url="http://director-v2:8000/v2",
511+
assert_all_called=False,
512+
assert_all_mocked=True, # IMPORTANT: KEEP always True!
513+
) as mock:
514+
mock.get(f"/dynamic_services/projects/{project_id}/inactivity").respond(
515+
status.HTTP_200_OK, text=inactivity_response.model_dump_json()
516+
)
517+
yield None
518+
519+
520+
async def test_get_project_inactivity(
521+
mock_director_v2_get_project_inactivity: None,
522+
rpc_client: RabbitMQRPCClient,
523+
project_id: ProjectID,
524+
inactivity_response: GetProjectInactivityResponse,
525+
):
526+
result = await services.get_project_inactivity(
527+
rpc_client, project_id=project_id, max_inactivity_seconds=5
528+
)
529+
assert result == inactivity_response

services/web/server/src/simcore_service_webserver/director_v2/_core_dynamic_services.py

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from aiohttp import web
1010
from models_library.projects import ProjectID
1111
from models_library.services import ServicePortKey
12-
from pydantic import NonNegativeInt
1312
from servicelib.logging_utils import log_decorator
1413
from yarl import URL
1514

@@ -94,20 +93,3 @@ async def update_dynamic_service_networks_in_project(
9493
await request_director_v2(
9594
app, "PATCH", backend_url, expected_status=web.HTTPNoContent
9695
)
97-
98-
99-
@log_decorator(logger=_log)
100-
async def get_project_inactivity(
101-
app: web.Application,
102-
project_id: ProjectID,
103-
max_inactivity_seconds: NonNegativeInt,
104-
) -> DataType:
105-
settings: DirectorV2Settings = get_plugin_settings(app)
106-
backend_url = (
107-
URL(settings.base_url) / f"dynamic_services/projects/{project_id}/inactivity"
108-
).update_query(max_inactivity_seconds=max_inactivity_seconds)
109-
result = await request_director_v2(
110-
app, "GET", backend_url, expected_status=web.HTTPOk
111-
)
112-
assert isinstance(result, dict) # nosec
113-
return result

services/web/server/src/simcore_service_webserver/director_v2/api.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
stop_pipeline,
1818
)
1919
from ._core_dynamic_services import (
20-
get_project_inactivity,
2120
request_retrieve_dyn_service,
2221
restart_dynamic_service,
2322
retrieve,
@@ -34,7 +33,6 @@
3433
"DirectorServiceError",
3534
"get_batch_tasks_outputs",
3635
"get_computation_task",
37-
"get_project_inactivity",
3836
"get_project_run_policy",
3937
"is_healthy",
4038
"is_pipeline_running",

services/web/server/src/simcore_service_webserver/dynamic_scheduler/api.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
from functools import partial
44

55
from aiohttp import web
6-
from models_library.api_schemas_directorv2.dynamic_services import DynamicServiceGet
6+
from models_library.api_schemas_directorv2.dynamic_services import (
7+
DynamicServiceGet,
8+
GetProjectInactivityResponse,
9+
)
710
from models_library.api_schemas_dynamic_scheduler.dynamic_services import (
811
DynamicServiceStart,
912
DynamicServiceStop,
@@ -19,6 +22,7 @@
1922
from models_library.projects_nodes_io import NodeID
2023
from models_library.rabbitmq_messages import ProgressRabbitMessageProject, ProgressType
2124
from models_library.users import UserID
25+
from pydantic import NonNegativeInt
2226
from pydantic.types import PositiveInt
2327
from servicelib.progress_bar import ProgressBarData
2428
from servicelib.rabbitmq import RabbitMQClient, RPCServerError
@@ -148,3 +152,16 @@ async def stop_dynamic_services_in_project(
148152
]
149153

150154
await logged_gather(*services_to_stop)
155+
156+
157+
async def get_project_inactivity(
158+
app: web.Application,
159+
*,
160+
project_id: ProjectID,
161+
max_inactivity_seconds: NonNegativeInt,
162+
) -> GetProjectInactivityResponse:
163+
return await services.get_project_inactivity(
164+
get_rabbitmq_rpc_client(app),
165+
project_id=project_id,
166+
max_inactivity_seconds=max_inactivity_seconds,
167+
)

0 commit comments

Comments
 (0)