Skip to content

Commit e98d50b

Browse files
committed
fixes wrong product_name
1 parent 69ab5a0 commit e98d50b

File tree

2 files changed

+84
-32
lines changed

2 files changed

+84
-32
lines changed

services/api-server/src/simcore_service_api_server/api/dependencies/webserver_http.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,17 @@
55
from cryptography.fernet import Fernet
66
from fastapi import Depends, FastAPI, HTTPException, status
77
from fastapi.requests import Request
8-
from servicelib.rest_constants import X_PRODUCT_NAME_HEADER
98

109
from ..._constants import MSG_BACKEND_SERVICE_UNAVAILABLE
1110
from ...core.settings import ApplicationSettings, WebServerSettings
1211
from ...services_http.webserver import AuthSession
1312
from .application import get_app, get_settings
14-
from .authentication import Identity, get_active_user_email, get_current_identity
13+
from .authentication import (
14+
Identity,
15+
get_active_user_email,
16+
get_current_identity,
17+
get_product_name,
18+
)
1519

1620

1721
def _get_settings(
@@ -63,19 +67,18 @@ def get_webserver_session(
6367
app: Annotated[FastAPI, Depends(get_app)],
6468
session_cookies: Annotated[dict, Depends(get_session_cookie)],
6569
identity: Annotated[Identity, Depends(get_current_identity)],
70+
product_name: Annotated[str, Depends(get_product_name)],
6671
) -> AuthSession:
6772
"""
6873
Lifetime of AuthSession wrapper is one request because it needs different session cookies
6974
Lifetime of embedded client is attached to the app lifetime
7075
"""
71-
product_header: dict[str, str] = {X_PRODUCT_NAME_HEADER: f"{identity.product_name}"}
72-
session = AuthSession.create(app, session_cookies, product_header)
76+
assert identity.product_name == product_name # nosec
77+
session = AuthSession.create(
78+
app,
79+
session_cookies=session_cookies,
80+
product_name=product_name,
81+
user_id=identity.user_id,
82+
)
7383
assert isinstance(session, AuthSession) # nosec
7484
return session
75-
76-
77-
__all__: tuple[str, ...] = (
78-
"AuthSession",
79-
"get_session_cookie",
80-
"get_webserver_session",
81-
)

services/api-server/src/simcore_service_api_server/services_http/webserver.py

Lines changed: 70 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22

33
import logging
44
import urllib.parse
5-
from collections.abc import Mapping
65
from dataclasses import dataclass
76
from functools import partial
8-
from typing import Any
7+
from typing import Any, Self
98
from uuid import UUID
109

1110
import httpx
@@ -36,16 +35,19 @@
3635
)
3736
from models_library.api_schemas_webserver.wallets import WalletGet
3837
from models_library.generics import Envelope
38+
from models_library.products import ProductName
3939
from models_library.projects import ProjectID
4040
from models_library.projects_nodes_io import NodeID
4141
from models_library.rest_pagination import Page, PageLimitInt, PageOffsetInt
42+
from models_library.users import UserID
4243
from models_library.utils.fastapi_encoders import jsonable_encoder
4344
from pydantic import PositiveInt
4445
from servicelib.common_headers import (
4546
X_SIMCORE_PARENT_NODE_ID,
4647
X_SIMCORE_PARENT_PROJECT_UUID,
4748
)
4849
from servicelib.long_running_tasks.models import TaskStatus
50+
from servicelib.rest_constants import X_PRODUCT_NAME_HEADER
4951
from settings_library.tracing import TracingSettings
5052
from tenacity import TryAgain, retry_if_exception_type
5153
from tenacity.asyncio import AsyncRetrying
@@ -128,7 +130,7 @@ class LongRunningTasksClient(BaseServiceClientApi):
128130
"Client for requesting status and results of long running tasks"
129131

130132

131-
@dataclass
133+
@dataclass(frozen=True)
132134
class AuthSession:
133135
"""
134136
- wrapper around thin-client to simplify webserver's API
@@ -140,8 +142,12 @@ class AuthSession:
140142
SEE services/api-server/src/simcore_service_api_server/api/dependencies/webserver.py
141143
"""
142144

145+
_product_name: ProductName
146+
_user_id: UserID
147+
143148
_api: WebserverApi
144149
_long_running_task_client: LongRunningTasksClient
150+
145151
vtag: str
146152
session_cookies: dict | None = None
147153

@@ -150,26 +156,45 @@ def create(
150156
cls,
151157
app: FastAPI,
152158
session_cookies: dict,
153-
product_extra_headers: Mapping[str, str],
154-
) -> "AuthSession":
159+
user_id: UserID,
160+
product_name: ProductName,
161+
) -> Self:
162+
163+
# WARNING: this client lifespan is tied to the app
155164
api = WebserverApi.get_instance(app)
156165
assert api # nosec
157166
assert isinstance(api, WebserverApi) # nosec
158167

159-
api.client.headers = product_extra_headers # type: ignore[assignment]
168+
# WARNING: this client lifespan is tied to the app
160169
long_running_tasks_client = LongRunningTasksClient.get_instance(app=app)
161-
162170
assert long_running_tasks_client # nosec
163171
assert isinstance(long_running_tasks_client, LongRunningTasksClient) # nosec
164172

165-
long_running_tasks_client.client.headers = product_extra_headers # type: ignore[assignment]
166173
return cls(
174+
_product_name=product_name,
175+
_user_id=user_id,
167176
_api=api,
168177
_long_running_task_client=long_running_tasks_client,
169178
vtag=app.state.settings.API_SERVER_WEBSERVER.WEBSERVER_VTAG,
170179
session_cookies=session_cookies,
171180
)
172181

182+
def _get_session_headers(
183+
self,
184+
*,
185+
parent_project_uuid: ProjectID | None = None,
186+
parent_node_id: NodeID | None = None,
187+
) -> dict[str, str]:
188+
headers = {X_PRODUCT_NAME_HEADER: self._product_name}
189+
190+
if parent_project_uuid is not None:
191+
headers[X_SIMCORE_PARENT_PROJECT_UUID] = str(parent_project_uuid)
192+
193+
if parent_node_id is not None:
194+
headers[X_SIMCORE_PARENT_NODE_ID] = str(parent_node_id)
195+
196+
return headers
197+
173198
# OPERATIONS
174199

175200
@property
@@ -212,6 +237,7 @@ async def _page_projects(
212237
**optional,
213238
},
214239
cookies=self.session_cookies,
240+
headers=self._get_session_headers(),
215241
)
216242
resp.raise_for_status()
217243

@@ -230,7 +256,9 @@ async def _wait_for_long_running_task_results(self, lrt_response: httpx.Response
230256
):
231257
with attempt:
232258
get_response = await self.long_running_task_client.get(
233-
url=status_url, cookies=self.session_cookies
259+
url=status_url,
260+
cookies=self.session_cookies,
261+
headers=self._get_session_headers(),
234262
)
235263
get_response.raise_for_status(
236264
# NOTE: stops retrying if the response in not 2xx
@@ -244,7 +272,9 @@ async def _wait_for_long_running_task_results(self, lrt_response: httpx.Response
244272
raise TryAgain(msg)
245273

246274
result_response = await self.long_running_task_client.get(
247-
f"{result_url}", cookies=self.session_cookies
275+
f"{result_url}",
276+
cookies=self.session_cookies,
277+
headers=self._get_session_headers(),
248278
)
249279
result_response.raise_for_status()
250280
return Envelope.model_validate_json(result_response.text).data
@@ -253,7 +283,11 @@ async def _wait_for_long_running_task_results(self, lrt_response: httpx.Response
253283

254284
@_exception_mapper(http_status_map=_PROFILE_STATUS_MAP)
255285
async def get_me(self) -> Profile:
256-
response = await self.client.get("/me", cookies=self.session_cookies)
286+
response = await self.client.get(
287+
"/me",
288+
cookies=self.session_cookies,
289+
headers=self._get_session_headers(),
290+
)
257291
response.raise_for_status()
258292

259293
got: WebProfileGet | None = (
@@ -284,6 +318,7 @@ async def update_me(self, *, profile_update: ProfileUpdate) -> Profile:
284318
"/me",
285319
json=update.model_dump(exclude_unset=True),
286320
cookies=self.session_cookies,
321+
headers=self._get_session_headers(),
287322
)
288323
response.raise_for_status()
289324
profile: Profile = await self.get_me()
@@ -302,17 +337,15 @@ async def create_project(
302337
) -> ProjectGet:
303338
# POST /projects --> 202 Accepted
304339
query_params = {"hidden": is_hidden}
305-
headers = {
306-
X_SIMCORE_PARENT_PROJECT_UUID: parent_project_uuid,
307-
X_SIMCORE_PARENT_NODE_ID: parent_node_id,
308-
}
309340

310341
response = await self.client.post(
311342
"/projects",
312343
params=query_params,
313-
headers={k: f"{v}" for k, v in headers.items() if v is not None},
314344
json=jsonable_encoder(project, by_alias=True, exclude={"state"}),
315345
cookies=self.session_cookies,
346+
headers=self._get_session_headers(
347+
parent_project_uuid=parent_project_uuid, parent_node_id=parent_node_id
348+
),
316349
)
317350
response.raise_for_status()
318351
result = await self._wait_for_long_running_task_results(response)
@@ -329,16 +362,14 @@ async def clone_project(
329362
) -> ProjectGet:
330363
# POST /projects --> 202 Accepted
331364
query_params = {"from_study": project_id, "hidden": hidden}
332-
_headers = {
333-
X_SIMCORE_PARENT_PROJECT_UUID: parent_project_uuid,
334-
X_SIMCORE_PARENT_NODE_ID: parent_node_id,
335-
}
336365

337366
response = await self.client.post(
338367
"/projects",
339368
cookies=self.session_cookies,
340369
params=query_params,
341-
headers={k: f"{v}" for k, v in _headers.items() if v is not None},
370+
headers=self._get_session_headers(
371+
parent_project_uuid=parent_project_uuid, parent_node_id=parent_node_id
372+
),
342373
)
343374
response.raise_for_status()
344375
result = await self._wait_for_long_running_task_results(response)
@@ -349,6 +380,7 @@ async def get_project(self, *, project_id: UUID) -> ProjectGet:
349380
response = await self.client.get(
350381
f"/projects/{project_id}",
351382
cookies=self.session_cookies,
383+
headers=self._get_session_headers(),
352384
)
353385
response.raise_for_status()
354386
data = Envelope[ProjectGet].model_validate_json(response.text).data
@@ -379,6 +411,7 @@ async def delete_project(self, *, project_id: ProjectID) -> None:
379411
response = await self.client.delete(
380412
f"/projects/{project_id}",
381413
cookies=self.session_cookies,
414+
headers=self._get_session_headers(),
382415
)
383416
response.raise_for_status()
384417

@@ -395,6 +428,7 @@ async def get_project_metadata_ports(
395428
response = await self.client.get(
396429
f"/projects/{project_id}/metadata/ports",
397430
cookies=self.session_cookies,
431+
headers=self._get_session_headers(),
398432
)
399433
response.raise_for_status()
400434
data = Envelope[list[StudyPort]].model_validate_json(response.text).data
@@ -411,6 +445,7 @@ async def get_project_metadata(
411445
response = await self.client.get(
412446
f"/projects/{project_id}/metadata",
413447
cookies=self.session_cookies,
448+
headers=self._get_session_headers(),
414449
)
415450
response.raise_for_status()
416451
data = Envelope[ProjectMetadataGet].model_validate_json(response.text).data
@@ -422,6 +457,7 @@ async def patch_project(self, *, project_id: UUID, patch_params: ProjectPatch):
422457
response = await self.client.patch(
423458
f"/projects/{project_id}",
424459
cookies=self.session_cookies,
460+
headers=self._get_session_headers(),
425461
json=jsonable_encoder(patch_params, exclude_unset=True),
426462
)
427463
response.raise_for_status()
@@ -435,6 +471,7 @@ async def update_project_metadata(
435471
response = await self.client.patch(
436472
f"/projects/{project_id}/metadata",
437473
cookies=self.session_cookies,
474+
headers=self._get_session_headers(),
438475
json=jsonable_encoder(ProjectMetadataUpdate(custom=metadata)),
439476
)
440477
response.raise_for_status()
@@ -451,6 +488,7 @@ async def get_project_node_pricing_unit(
451488
response = await self.client.get(
452489
f"/projects/{project_id}/nodes/{node_id}/pricing-unit",
453490
cookies=self.session_cookies,
491+
headers=self._get_session_headers(),
454492
)
455493

456494
response.raise_for_status()
@@ -472,6 +510,7 @@ async def connect_pricing_unit_to_project_node(
472510
response = await self.client.put(
473511
f"/projects/{project_id}/nodes/{node_id}/pricing-plan/{pricing_plan}/pricing-unit/{pricing_unit}",
474512
cookies=self.session_cookies,
513+
headers=self._get_session_headers(),
475514
)
476515
response.raise_for_status()
477516

@@ -494,6 +533,7 @@ async def start_project(
494533
response = await self.client.post(
495534
f"/computations/{project_id}:start",
496535
cookies=self.session_cookies,
536+
headers=self._get_session_headers(),
497537
json=jsonable_encoder(body, exclude_unset=True, exclude_defaults=True),
498538
)
499539
response.raise_for_status()
@@ -508,6 +548,7 @@ async def update_project_inputs(
508548
response = await self.client.patch(
509549
f"/projects/{project_id}/inputs",
510550
cookies=self.session_cookies,
551+
headers=self._get_session_headers(),
511552
json=jsonable_encoder(new_inputs),
512553
)
513554
response.raise_for_status()
@@ -526,6 +567,7 @@ async def get_project_inputs(
526567
response = await self.client.get(
527568
f"/projects/{project_id}/inputs",
528569
cookies=self.session_cookies,
570+
headers=self._get_session_headers(),
529571
)
530572

531573
response.raise_for_status()
@@ -547,6 +589,7 @@ async def get_project_outputs(
547589
response = await self.client.get(
548590
f"/projects/{project_id}/outputs",
549591
cookies=self.session_cookies,
592+
headers=self._get_session_headers(),
550593
)
551594

552595
response.raise_for_status()
@@ -566,6 +609,7 @@ async def update_node_outputs(
566609
response = await self.client.patch(
567610
f"/projects/{project_id}/nodes/{node_id}/outputs",
568611
cookies=self.session_cookies,
612+
headers=self._get_session_headers(),
569613
json=jsonable_encoder(new_node_outputs),
570614
)
571615
response.raise_for_status()
@@ -577,6 +621,7 @@ async def get_default_wallet(self) -> WalletGetWithAvailableCreditsLegacy:
577621
response = await self.client.get(
578622
"/wallets/default",
579623
cookies=self.session_cookies,
624+
headers=self._get_session_headers(),
580625
)
581626
response.raise_for_status()
582627
data = (
@@ -594,6 +639,7 @@ async def get_wallet(
594639
response = await self.client.get(
595640
f"/wallets/{wallet_id}",
596641
cookies=self.session_cookies,
642+
headers=self._get_session_headers(),
597643
)
598644
response.raise_for_status()
599645
data = (
@@ -609,6 +655,7 @@ async def get_project_wallet(self, *, project_id: ProjectID) -> WalletGet:
609655
response = await self.client.get(
610656
f"/projects/{project_id}/wallet",
611657
cookies=self.session_cookies,
658+
headers=self._get_session_headers(),
612659
)
613660
response.raise_for_status()
614661
data = Envelope[WalletGet].model_validate_json(response.text).data
@@ -624,6 +671,7 @@ async def get_product_price(self) -> GetCreditPriceLegacy:
624671
response = await self.client.get(
625672
"/credits-price",
626673
cookies=self.session_cookies,
674+
headers=self._get_session_headers(),
627675
)
628676
response.raise_for_status()
629677
data = Envelope[GetCreditPriceLegacy].model_validate_json(response.text).data
@@ -643,6 +691,7 @@ async def get_service_pricing_plan(
643691
response = await self.client.get(
644692
f"/catalog/services/{service_key}/{version}/pricing-plan",
645693
cookies=self.session_cookies,
694+
headers=self._get_session_headers(),
646695
)
647696
response.raise_for_status()
648697
pricing_plan_get = (

0 commit comments

Comments
 (0)