Skip to content

Commit fea0985

Browse files
committed
client rpc: implement get_service_ports function to retrieve service ports for a specific service version with error handling
1 parent aac18d1 commit fea0985

File tree

3 files changed

+201
-0
lines changed

3 files changed

+201
-0
lines changed

packages/pytest-simcore/src/pytest_simcore/helpers/catalog_rpc_server.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
ServiceGetV2,
1111
ServiceListFilters,
1212
)
13+
from models_library.api_schemas_catalog.services_ports import ServicePortGet
1314
from models_library.api_schemas_webserver.catalog import (
1415
CatalogServiceUpdate,
1516
)
@@ -136,3 +137,22 @@ async def list_my_service_history_paginated(
136137
limit=limit,
137138
offset=offset,
138139
)
140+
141+
async def get_service_ports(
142+
self,
143+
rpc_client: RabbitMQRPCClient,
144+
*,
145+
product_name: ProductName,
146+
user_id: UserID,
147+
service_key: ServiceKey,
148+
service_version: ServiceVersion,
149+
) -> list[ServicePortGet]:
150+
assert rpc_client
151+
assert product_name
152+
assert user_id
153+
assert service_key
154+
assert service_version
155+
156+
return TypeAdapter(list[ServicePortGet]).validate_python(
157+
ServicePortGet.model_json_schema()["examples"],
158+
)

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
ServiceRelease,
2323
ServiceUpdateV2,
2424
)
25+
from models_library.api_schemas_catalog.services_ports import ServicePortGet
2526
from models_library.products import ProductName
2627
from models_library.rabbitmq_basic_types import RPCMethodName
2728
from models_library.rest_pagination import PageOffsetInt
@@ -222,3 +223,34 @@ async def list_my_service_history_paginated( # pylint: disable=too-many-argumen
222223
TypeAdapter(PageRpcServiceRelease).validate_python(result) is not None
223224
)
224225
return cast(PageRpc[ServiceRelease], result)
226+
227+
228+
@validate_call(config={"arbitrary_types_allowed": True})
229+
@log_decorator(_logger, level=logging.DEBUG)
230+
async def get_service_ports(
231+
rpc_client: RabbitMQRPCClient,
232+
*,
233+
product_name: ProductName,
234+
user_id: UserID,
235+
service_key: ServiceKey,
236+
service_version: ServiceVersion,
237+
) -> list[ServicePortGet]:
238+
"""Gets service ports (inputs and outputs) for a specific service version
239+
240+
Raises:
241+
ValidationError: on invalid arguments
242+
CatalogItemNotFoundError: service not found in catalog
243+
CatalogForbiddenError: not access rights to read this service
244+
"""
245+
result = await rpc_client.request(
246+
CATALOG_RPC_NAMESPACE,
247+
TypeAdapter(RPCMethodName).validate_python("get_service_ports"),
248+
product_name=product_name,
249+
user_id=user_id,
250+
service_key=service_key,
251+
service_version=service_version,
252+
)
253+
assert (
254+
TypeAdapter(list[ServicePortGet]).validate_python(result) is not None
255+
) # nosec
256+
return cast(list[ServicePortGet], result)

services/catalog/tests/unit/with_dbs/test_api_rpc.py

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,3 +582,152 @@ async def test_rpc_list_my_service_history_paginated(
582582
assert len(release_history) == 2
583583
assert release_history[0].version == service_version_2, "expected newest first"
584584
assert release_history[1].version == service_version_1
585+
586+
587+
async def test_rpc_get_service_ports_successful_retrieval(
588+
background_sync_task_mocked: None,
589+
mocked_director_rest_api: MockRouter,
590+
rpc_client: RabbitMQRPCClient,
591+
product_name: ProductName,
592+
user_id: UserID,
593+
app: FastAPI,
594+
create_fake_service_data: Callable,
595+
services_db_tables_injector: Callable,
596+
):
597+
"""Tests successful retrieval of service ports for a specific service version"""
598+
assert app
599+
600+
# Create a service with known ports
601+
service_key = "simcore/services/comp/test-service-ports"
602+
service_version = "1.0.0"
603+
604+
# Create and inject the service
605+
fake_service = create_fake_service_data(
606+
service_key,
607+
service_version,
608+
team_access=None,
609+
everyone_access=None,
610+
product=product_name,
611+
)
612+
await services_db_tables_injector([fake_service])
613+
614+
# Import the get_service_ports function from the client
615+
from servicelib.rabbitmq.rpc_interfaces.catalog.services import get_service_ports
616+
617+
# Call the RPC function to get service ports
618+
ports = await get_service_ports(
619+
rpc_client,
620+
product_name=product_name,
621+
user_id=user_id,
622+
service_key=service_key,
623+
service_version=service_version,
624+
)
625+
626+
# Validate the response
627+
assert isinstance(ports, list)
628+
# Each port should have expected fields
629+
for port in ports:
630+
assert hasattr(port, "kind")
631+
assert hasattr(port, "key")
632+
assert hasattr(port, "port")
633+
634+
635+
async def test_rpc_get_service_ports_not_found(
636+
background_sync_task_mocked: None,
637+
mocked_director_rest_api: MockRouter,
638+
rpc_client: RabbitMQRPCClient,
639+
product_name: ProductName,
640+
user_id: UserID,
641+
app: FastAPI,
642+
):
643+
"""Tests that appropriate error is raised when service does not exist"""
644+
assert app
645+
646+
# Import the get_service_ports function from the client
647+
from servicelib.rabbitmq.rpc_interfaces.catalog.services import get_service_ports
648+
649+
service_version = "1.0.0"
650+
non_existent_key = "simcore/services/comp/non-existent-service"
651+
652+
# Test service not found scenario
653+
with pytest.raises(CatalogItemNotFoundError, match="non-existent-service"):
654+
await get_service_ports(
655+
rpc_client,
656+
product_name=product_name,
657+
user_id=user_id,
658+
service_key=non_existent_key,
659+
service_version=service_version,
660+
)
661+
662+
663+
async def test_rpc_get_service_ports_permission_denied(
664+
background_sync_task_mocked: None,
665+
mocked_director_rest_api: MockRouter,
666+
rpc_client: RabbitMQRPCClient,
667+
product_name: ProductName,
668+
user: dict[str, Any],
669+
user_id: UserID,
670+
app: FastAPI,
671+
create_fake_service_data: Callable,
672+
services_db_tables_injector: Callable,
673+
):
674+
"""Tests that appropriate error is raised when user doesn't have permission"""
675+
assert app
676+
677+
# Import the get_service_ports function from the client
678+
from servicelib.rabbitmq.rpc_interfaces.catalog.services import get_service_ports
679+
680+
# Create a service with restricted access
681+
restricted_service_key = "simcore/services/comp/restricted-service"
682+
service_version = "1.0.0"
683+
684+
fake_restricted_service = create_fake_service_data(
685+
restricted_service_key,
686+
service_version,
687+
team_access=None,
688+
everyone_access=None,
689+
product=product_name,
690+
)
691+
692+
# Modify access rights to restrict access
693+
if "access_rights" in fake_restricted_service:
694+
# Remove user's access if present
695+
if user["primary_gid"] in fake_restricted_service["access_rights"]:
696+
fake_restricted_service["access_rights"].pop(user["primary_gid"])
697+
698+
await services_db_tables_injector([fake_restricted_service])
699+
700+
# Attempt to access without permission
701+
with pytest.raises(CatalogForbiddenError):
702+
await get_service_ports(
703+
rpc_client,
704+
product_name=product_name,
705+
user_id=UserID("different-user"), # Use a different user ID
706+
service_key=restricted_service_key,
707+
service_version=service_version,
708+
)
709+
710+
711+
async def test_rpc_get_service_ports_validation_error(
712+
background_sync_task_mocked: None,
713+
mocked_director_rest_api: MockRouter,
714+
rpc_client: RabbitMQRPCClient,
715+
product_name: ProductName,
716+
user_id: UserID,
717+
app: FastAPI,
718+
):
719+
"""Tests validation error handling for invalid service key format"""
720+
assert app
721+
722+
# Import the get_service_ports function from the client
723+
from servicelib.rabbitmq.rpc_interfaces.catalog.services import get_service_ports
724+
725+
# Test with invalid service key format
726+
with pytest.raises(ValidationError, match="service_key"):
727+
await get_service_ports(
728+
rpc_client,
729+
product_name=product_name,
730+
user_id=user_id,
731+
service_key="invalid-service-key-format",
732+
service_version="1.0.0",
733+
)

0 commit comments

Comments
 (0)