Skip to content

Commit adf5bfb

Browse files
committed
add check that headers are passed
1 parent bfee222 commit adf5bfb

File tree

2 files changed

+55
-7
lines changed

2 files changed

+55
-7
lines changed

services/api-server/src/simcore_service_api_server/api/routes/functions_routes.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from typing import Annotated, Final
33

44
import jsonschema
5-
from fastapi import APIRouter, Depends, Request, status
5+
from fastapi import APIRouter, Depends, Header, Request, status
66
from fastapi_pagination.api import create_page
77
from jsonschema import ValidationError
88
from models_library.api_schemas_api_server.functions import (
@@ -29,6 +29,8 @@
2929
UnsupportedFunctionClassError,
3030
)
3131
from models_library.products import ProductName
32+
from models_library.projects import ProjectID
33+
from models_library.projects_nodes_io import NodeID
3234
from models_library.projects_state import RunningState
3335
from models_library.users import UserID
3436
from servicelib.fastapi.dependencies import get_reverse_url_mapper
@@ -372,6 +374,8 @@ async def run_function( # noqa: PLR0913
372374
product_name: Annotated[str, Depends(get_product_name)],
373375
solver_service: Annotated[SolverService, Depends(get_solver_service)],
374376
job_service: Annotated[JobService, Depends(get_job_service)],
377+
x_simcore_parent_project_uuid: Annotated[ProjectID | None, Header()],
378+
x_simcore_parent_node_id: Annotated[NodeID | None, Header()],
375379
) -> RegisteredFunctionJob:
376380

377381
user_permissions: FunctionUserAccessRights = (
@@ -436,8 +440,8 @@ async def run_function( # noqa: PLR0913
436440
webserver_api=webserver_api,
437441
wb_api_rpc=wb_api_rpc,
438442
url_for=url_for,
439-
x_simcore_parent_project_uuid=None,
440-
x_simcore_parent_node_id=None,
443+
x_simcore_parent_project_uuid=x_simcore_parent_project_uuid,
444+
x_simcore_parent_node_id=x_simcore_parent_node_id,
441445
user_id=user_id,
442446
product_name=product_name,
443447
)
@@ -471,8 +475,8 @@ async def run_function( # noqa: PLR0913
471475
solver_service=solver_service,
472476
job_service=job_service,
473477
url_for=url_for,
474-
x_simcore_parent_project_uuid=None,
475-
x_simcore_parent_node_id=None,
478+
x_simcore_parent_project_uuid=x_simcore_parent_project_uuid,
479+
x_simcore_parent_node_id=x_simcore_parent_node_id,
476480
)
477481
await solvers_jobs.start_job(
478482
request=request,

services/api-server/tests/unit/api_functions/test_api_routers_functions.py

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
from uuid import uuid4
99

1010
import httpx
11+
import pytest
1112
import respx
13+
from faker import Faker
1214
from httpx import AsyncClient
1315
from models_library.api_schemas_webserver.functions import (
1416
FunctionJobCollection,
@@ -26,9 +28,16 @@
2628
from models_library.rest_pagination import PageMetaInfoLimitOffset
2729
from models_library.users import UserID
2830
from pytest_mock import MockType
31+
from pytest_simcore.helpers.httpx_calls_capture_models import HttpApiCallCaptureModel
2932
from servicelib.aiohttp import status
33+
from servicelib.common_headers import (
34+
X_SIMCORE_PARENT_NODE_ID,
35+
X_SIMCORE_PARENT_PROJECT_UUID,
36+
)
3037
from simcore_service_api_server._meta import API_VTAG
3138

39+
_faker = Faker()
40+
3241

3342
async def test_register_function(
3443
client: AsyncClient,
@@ -618,6 +627,15 @@ async def test_run_function_not_allowed(
618627
)
619628

620629

630+
@pytest.mark.parametrize(
631+
"parent_project_uuid, parent_node_uuid, expected_status_code",
632+
[
633+
(None, None, status.HTTP_422_UNPROCESSABLE_ENTITY),
634+
(f"{_faker.uuid4()}", None, status.HTTP_422_UNPROCESSABLE_ENTITY),
635+
(None, f"{_faker.uuid4()}", status.HTTP_422_UNPROCESSABLE_ENTITY),
636+
(f"{_faker.uuid4()}", f"{_faker.uuid4()}", status.HTTP_200_OK),
637+
],
638+
)
621639
async def test_run_function_parent_info(
622640
client: AsyncClient,
623641
mock_handler_in_functions_rpc_interface: Callable[[str, Any], None],
@@ -630,13 +648,33 @@ async def test_run_function_parent_info(
630648
mocked_webserver_rpc_api: dict[str, MockType],
631649
create_respx_mock_from_capture,
632650
project_tests_dir: Path,
651+
parent_project_uuid: str | None,
652+
parent_node_uuid: str | None,
653+
expected_status_code: int,
633654
) -> None:
634655

635656
capture = "run_function_parent_info.json"
657+
658+
def _default_side_effect(
659+
request: httpx.Request,
660+
path_params: dict[str, Any],
661+
capture: HttpApiCallCaptureModel,
662+
) -> Any:
663+
if request.method == "POST" and request.url.path.endswith("/projects"):
664+
if parent_project_uuid:
665+
_parent_uuid = request.headers.get(X_SIMCORE_PARENT_PROJECT_UUID)
666+
assert _parent_uuid is not None
667+
assert parent_project_uuid == _parent_uuid
668+
if parent_node_uuid:
669+
_parent_node_uuid = request.headers.get(X_SIMCORE_PARENT_NODE_ID)
670+
assert _parent_node_uuid is not None
671+
assert parent_node_uuid == _parent_node_uuid
672+
return capture.response_body
673+
636674
create_respx_mock_from_capture(
637675
respx_mocks=[mocked_webserver_rest_api_base, mocked_directorv2_rest_api_base],
638676
capture_path=project_tests_dir / "mocks" / capture,
639-
side_effects_callbacks=[],
677+
side_effects_callbacks=[_default_side_effect] * 50,
640678
)
641679

642680
mock_handler_in_functions_rpc_interface(
@@ -654,9 +692,15 @@ async def test_run_function_parent_info(
654692
"register_function_job", mock_registered_function_job
655693
)
656694

695+
headers = dict()
696+
if parent_project_uuid:
697+
headers[X_SIMCORE_PARENT_PROJECT_UUID] = parent_project_uuid
698+
if parent_node_uuid:
699+
headers[X_SIMCORE_PARENT_NODE_ID] = parent_node_uuid
657700
response = await client.post(
658701
f"{API_VTAG}/functions/{mock_registered_function.uid}:run",
659702
json={},
660703
auth=auth,
704+
headers=headers,
661705
)
662-
assert response.status_code == status.HTTP_200_OK
706+
assert response.status_code == expected_status_code

0 commit comments

Comments
 (0)