Skip to content

Commit 0d5a6c3

Browse files
1 parent ae5be6f commit 0d5a6c3

File tree

6 files changed

+47
-30
lines changed

6 files changed

+47
-30
lines changed

services/web/server/src/simcore_service_webserver/folders/_workspaces_rest.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22

33
from aiohttp import web
44
from servicelib.aiohttp import status
5-
from servicelib.aiohttp.requests_validation import parse_request_path_parameters_as
6-
from servicelib.rest_constants import X_CLIENT_SESSION_ID_HEADER
5+
from servicelib.aiohttp.requests_validation import (
6+
parse_request_headers_as,
7+
parse_request_path_parameters_as,
8+
)
79

810
from .._meta import api_version_prefix as VTAG
911
from ..login.decorators import login_required
12+
from ..models import ClientSessionHeaderParams
1013
from ..security.decorators import permission_required
1114
from . import _workspaces_repository
1215
from ._common.exceptions_handlers import handle_plugin_requests_exceptions
@@ -28,15 +31,14 @@
2831
async def move_folder_to_workspace(request: web.Request):
2932
req_ctx = FoldersRequestContext.model_validate(request)
3033
path_params = parse_request_path_parameters_as(FolderWorkspacesPathParams, request)
31-
32-
client_session_id: str | None = request.headers.get(X_CLIENT_SESSION_ID_HEADER)
34+
header_params = parse_request_headers_as(ClientSessionHeaderParams, request)
3335

3436
await _workspaces_repository.move_folder_into_workspace(
3537
app=request.app,
3638
user_id=req_ctx.user_id,
3739
folder_id=path_params.folder_id,
3840
workspace_id=path_params.workspace_id,
3941
product_name=req_ctx.product_name,
40-
client_session_id=client_session_id,
42+
client_session_id=header_params.client_session_id,
4143
)
4244
return web.json_response(status=status.HTTP_204_NO_CONTENT)

services/web/server/src/simcore_service_webserver/models.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from pydantic import ConfigDict, Field
77
from pydantic_extra_types.phone_numbers import PhoneNumberValidator
88
from servicelib.request_keys import RQT_USERID_KEY
9+
from servicelib.rest_constants import X_CLIENT_SESSION_ID_HEADER
910

1011
from .constants import RQ_PRODUCT_KEY
1112

@@ -25,3 +26,18 @@ class AuthenticatedRequestContext(RequestParameters):
2526
model_config = ConfigDict(
2627
frozen=True # prevents modifications after middlewares creates this model
2728
)
29+
30+
31+
class ClientSessionHeaderParams(RequestParameters):
32+
"""Header parameters for client session tracking in collaborative features."""
33+
34+
client_session_id: str | None = Field(
35+
default=None,
36+
alias=X_CLIENT_SESSION_ID_HEADER,
37+
description="Client session identifier for collaborative features",
38+
)
39+
40+
model_config = ConfigDict(
41+
populate_by_name=True,
42+
extra="forbid",
43+
)

services/web/server/src/simcore_service_webserver/projects/_controller/nodes_rest.py

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from servicelib.aiohttp.long_running_tasks.server import start_long_running_task
3636
from servicelib.aiohttp.requests_validation import (
3737
parse_request_body_as,
38+
parse_request_headers_as,
3839
parse_request_path_parameters_as,
3940
parse_request_query_parameters_as,
4041
)
@@ -50,7 +51,6 @@
5051
ServiceWaitingForManualInterventionError,
5152
ServiceWasNotFoundError,
5253
)
53-
from servicelib.rest_constants import X_CLIENT_SESSION_ID_HEADER
5454
from servicelib.services_utils import get_status_as_dict
5555
from simcore_postgres_database.models.users import UserRole
5656

@@ -60,6 +60,7 @@
6060
from ...groups.api import get_group_from_gid, list_all_user_groups_ids
6161
from ...groups.exceptions import GroupNotFoundError
6262
from ...login.decorators import login_required
63+
from ...models import ClientSessionHeaderParams
6364
from ...security.decorators import permission_required
6465
from ...users import users_service
6566
from ...utils_aiohttp import envelope_json_response, get_api_base_url
@@ -97,8 +98,7 @@ async def create_node(request: web.Request) -> web.Response:
9798
req_ctx = AuthenticatedRequestContext.model_validate(request)
9899
path_params = parse_request_path_parameters_as(ProjectPathParams, request)
99100
body = await parse_request_body_as(NodeCreate, request)
100-
101-
client_session_id = request.headers.get(X_CLIENT_SESSION_ID_HEADER)
101+
header_params = parse_request_headers_as(ClientSessionHeaderParams, request)
102102

103103
if await _projects_service.is_service_deprecated(
104104
request.app,
@@ -127,7 +127,7 @@ async def create_node(request: web.Request) -> web.Response:
127127
body.service_key,
128128
body.service_version,
129129
body.service_id,
130-
client_session_id=client_session_id,
130+
client_session_id=header_params.client_session_id,
131131
)
132132
}
133133
assert NodeCreated.model_validate(data) is not None # nosec
@@ -182,8 +182,7 @@ async def patch_project_node(request: web.Request) -> web.Response:
182182
req_ctx = AuthenticatedRequestContext.model_validate(request)
183183
path_params = parse_request_path_parameters_as(NodePathParams, request)
184184
node_patch = await parse_request_body_as(NodePatch, request)
185-
186-
client_session_id = request.headers.get(X_CLIENT_SESSION_ID_HEADER)
185+
header_params = parse_request_headers_as(ClientSessionHeaderParams, request)
187186

188187
await _projects_service.patch_project_node(
189188
request.app,
@@ -193,7 +192,7 @@ async def patch_project_node(request: web.Request) -> web.Response:
193192
project_id=path_params.project_id,
194193
node_id=path_params.node_id,
195194
partial_node=node_patch.to_domain_model(),
196-
client_session_id=client_session_id,
195+
client_session_id=header_params.client_session_id,
197196
)
198197

199198
return web.json_response(status=status.HTTP_204_NO_CONTENT)
@@ -206,8 +205,7 @@ async def patch_project_node(request: web.Request) -> web.Response:
206205
async def delete_node(request: web.Request) -> web.Response:
207206
req_ctx = AuthenticatedRequestContext.model_validate(request)
208207
path_params = parse_request_path_parameters_as(NodePathParams, request)
209-
210-
client_session_id = request.headers.get(X_CLIENT_SESSION_ID_HEADER)
208+
header_params = parse_request_headers_as(ClientSessionHeaderParams, request)
211209

212210
# ensure the project exists
213211
await _projects_service.get_project_for_user(
@@ -222,7 +220,7 @@ async def delete_node(request: web.Request) -> web.Response:
222220
NodeIDStr(path_params.node_id),
223221
req_ctx.product_name,
224222
product_api_base_url=get_api_base_url(request),
225-
client_session_id=client_session_id,
223+
client_session_id=header_params.client_session_id,
226224
)
227225

228226
return web.json_response(status=status.HTTP_204_NO_CONTENT)
@@ -259,8 +257,7 @@ async def update_node_outputs(request: web.Request) -> web.Response:
259257
req_ctx = AuthenticatedRequestContext.model_validate(request)
260258
path_params = parse_request_path_parameters_as(NodePathParams, request)
261259
node_outputs = await parse_request_body_as(NodeOutputs, request)
262-
263-
client_session_id = request.headers.get(X_CLIENT_SESSION_ID_HEADER)
260+
header_params = parse_request_headers_as(ClientSessionHeaderParams, request)
264261

265262
ui_changed_keys = set()
266263
ui_changed_keys.add(f"{path_params.node_id}")
@@ -273,7 +270,7 @@ async def update_node_outputs(request: web.Request) -> web.Response:
273270
run_hash=None,
274271
node_errors=None,
275272
ui_changed_keys=ui_changed_keys,
276-
client_session_id=client_session_id,
273+
client_session_id=header_params.client_session_id,
277274
)
278275
return web.json_response(status=status.HTTP_204_NO_CONTENT)
279276

services/web/server/src/simcore_service_webserver/projects/_controller/ports_rest.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@
1717
from pydantic import BaseModel, Field, TypeAdapter
1818
from servicelib.aiohttp.requests_validation import (
1919
parse_request_body_as,
20+
parse_request_headers_as,
2021
parse_request_path_parameters_as,
2122
)
22-
from servicelib.rest_constants import X_CLIENT_SESSION_ID_HEADER
2323

2424
from ..._meta import API_VTAG as VTAG
2525
from ...login.decorators import login_required
26+
from ...models import ClientSessionHeaderParams
2627
from ...security.decorators import permission_required
2728
from ...utils_aiohttp import envelope_json_response
2829
from .. import _ports_service, _projects_service
@@ -89,8 +90,7 @@ async def update_project_inputs(request: web.Request) -> web.Response:
8990
req_ctx = AuthenticatedRequestContext.model_validate(request)
9091
path_params = parse_request_path_parameters_as(ProjectPathParams, request)
9192
inputs_updates = await parse_request_body_as(list[ProjectInputUpdate], request)
92-
93-
client_session_id: str | None = request.headers.get(X_CLIENT_SESSION_ID_HEADER)
93+
header_params = parse_request_headers_as(ClientSessionHeaderParams, request)
9494

9595
assert request.app # nosec
9696

@@ -126,7 +126,7 @@ async def update_project_inputs(request: web.Request) -> web.Response:
126126
project_uuid=path_params.project_id,
127127
product_name=req_ctx.product_name,
128128
partial_workbench_data=jsonable_encoder(partial_workbench_data),
129-
client_session_id=client_session_id,
129+
client_session_id=header_params.client_session_id,
130130
)
131131

132132
workbench = TypeAdapter(dict[NodeID, Node]).validate_python(

services/web/server/src/simcore_service_webserver/projects/_controller/projects_rest.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@
2525
X_SIMCORE_USER_AGENT,
2626
)
2727
from servicelib.redis import get_project_locked_state
28-
from servicelib.rest_constants import X_CLIENT_SESSION_ID_HEADER
2928

3029
from ..._meta import API_VTAG as VTAG
3130
from ...login.decorators import login_required
31+
from ...models import ClientSessionHeaderParams
3232
from ...redis import get_redis_lock_manager_client_sdk
3333
from ...resource_manager.user_sessions import PROJECT_ID_KEY, managed_resource
3434
from ...security import security_web
@@ -314,15 +314,15 @@ async def patch_project(request: web.Request):
314314
path_params = parse_request_path_parameters_as(ProjectPathParams, request)
315315
project_patch = await parse_request_body_as(ProjectPatch, request)
316316

317-
client_session_id: str | None = request.headers.get(X_CLIENT_SESSION_ID_HEADER)
317+
header_params = parse_request_headers_as(ClientSessionHeaderParams, request)
318318

319319
await _projects_service.patch_project_for_user(
320320
request.app,
321321
user_id=req_ctx.user_id,
322322
project_uuid=path_params.project_id,
323323
project_patch=project_patch,
324324
product_name=req_ctx.product_name,
325-
client_session_id=client_session_id,
325+
client_session_id=header_params.client_session_id,
326326
)
327327

328328
return web.json_response(status=status.HTTP_204_NO_CONTENT)

services/web/server/src/simcore_service_webserver/projects/_controller/workspaces_rest.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@
77
from models_library.workspaces import WorkspaceID
88
from pydantic import BaseModel, BeforeValidator, ConfigDict, Field
99
from servicelib.aiohttp import status
10-
from servicelib.aiohttp.requests_validation import parse_request_path_parameters_as
11-
from servicelib.rest_constants import X_CLIENT_SESSION_ID_HEADER
10+
from servicelib.aiohttp.requests_validation import (
11+
parse_request_headers_as,
12+
parse_request_path_parameters_as,
13+
)
1214

1315
from ..._meta import api_version_prefix as VTAG
1416
from ...login.decorators import login_required
17+
from ...models import ClientSessionHeaderParams
1518
from ...security.decorators import permission_required
1619
from .. import _workspaces_service
1720
from ._rest_exceptions import handle_plugin_requests_exceptions
@@ -44,15 +47,14 @@ async def move_project_to_workspace(request: web.Request):
4447
path_params = parse_request_path_parameters_as(
4548
_ProjectWorkspacesPathParams, request
4649
)
47-
48-
client_session_id: str | None = request.headers.get(X_CLIENT_SESSION_ID_HEADER)
50+
header_params = parse_request_headers_as(ClientSessionHeaderParams, request)
4951

5052
await _workspaces_service.move_project_into_workspace(
5153
app=request.app,
5254
user_id=req_ctx.user_id,
5355
project_id=path_params.project_id,
5456
workspace_id=path_params.workspace_id,
5557
product_name=req_ctx.product_name,
56-
client_session_id=client_session_id,
58+
client_session_id=header_params.client_session_id,
5759
)
5860
return web.json_response(status=status.HTTP_204_NO_CONTENT)

0 commit comments

Comments
 (0)