Skip to content

Commit f9f8598

Browse files
committed
✨ Implement WebServer RPC Client and subclients for projects, licenses, and functions
1 parent 3d1e310 commit f9f8598

File tree

6 files changed

+276
-0
lines changed

6 files changed

+276
-0
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""WebServer RPC Client Package
2+
3+
Provides a class-based, modular RPC client for webserver services.
4+
"""
5+
6+
from .client import WebServerRpcClient
7+
from .functions import FunctionsRpcApi
8+
from .licenses import LicensesRpcApi
9+
from .projects import ProjectsRpcApi
10+
11+
__all__: tuple[str, ...] = (
12+
"WebServerRpcClient",
13+
"ProjectsRpcApi",
14+
"LicensesRpcApi",
15+
"FunctionsRpcApi",
16+
)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""Base classes and shared functionality for RPC subclients."""
2+
3+
from typing import Any
4+
5+
from models_library.products import ProductName
6+
from models_library.rabbitmq_basic_types import RPCMethodName, RPCNamespace
7+
from models_library.users import UserID
8+
from servicelib.rabbitmq import RabbitMQRPCClient
9+
10+
11+
class BaseRpcApi:
12+
"""Base class for all RPC API subclients."""
13+
14+
def __init__(self, rpc_client: RabbitMQRPCClient, namespace: RPCNamespace):
15+
self._rpc_client = rpc_client
16+
self._namespace = namespace
17+
18+
async def _request(
19+
self,
20+
method_name: RPCMethodName,
21+
*,
22+
product_name: ProductName,
23+
user_id: UserID,
24+
**kwargs: Any
25+
) -> Any:
26+
return await self._rpc_client.request(
27+
self._namespace,
28+
method_name,
29+
product_name=product_name,
30+
user_id=user_id,
31+
**kwargs
32+
)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""Main WebServer RPC Client."""
2+
3+
from models_library.rabbitmq_basic_types import RPCNamespace
4+
from models_library.rpc.webserver import WEBSERVER_RPC_NAMESPACE
5+
from servicelib.rabbitmq import RabbitMQRPCClient
6+
7+
from .functions import FunctionsRpcApi
8+
from .licenses import LicensesRpcApi
9+
from .projects import ProjectsRpcApi
10+
11+
12+
class WebServerRpcClient:
13+
"""Main RPC client for webserver services."""
14+
15+
def __init__(
16+
self,
17+
rpc_client: RabbitMQRPCClient,
18+
namespace: RPCNamespace = WEBSERVER_RPC_NAMESPACE,
19+
):
20+
self._rpc_client = rpc_client
21+
self._namespace = namespace
22+
23+
# Initialize subclients
24+
self.projects = ProjectsRpcApi(rpc_client, namespace)
25+
self.licenses = LicensesRpcApi(rpc_client, namespace)
26+
self.functions = FunctionsRpcApi(rpc_client, namespace)
27+
28+
@property
29+
def namespace(self) -> RPCNamespace:
30+
return self._namespace
31+
32+
async def close(self) -> None:
33+
"""Close the underlying RPC client connection."""
34+
# Delegate to the underlying client if it has a close method
35+
if hasattr(self._rpc_client, "close"):
36+
await self._rpc_client.close()
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
"""Functions RPC API subclient."""
2+
3+
from typing import Any
4+
5+
from ._base import BaseRpcApi
6+
7+
8+
class FunctionsRpcApi(BaseRpcApi):
9+
"""RPC client for function-related operations."""
10+
11+
async def get_function(self, user_id: int, function_id: str) -> dict[str, Any]:
12+
"""Get a function by ID."""
13+
return await self._request(
14+
"get_function", user_id=user_id, function_id=function_id
15+
)
16+
17+
async def list_functions(
18+
self, user_id: int, *, limit: int | None = None, offset: int = 0
19+
) -> list[dict[str, Any]]:
20+
"""List available functions."""
21+
return await self._request(
22+
"list_functions", user_id=user_id, limit=limit, offset=offset
23+
)
24+
25+
async def create_function(
26+
self, user_id: int, function_data: dict[str, Any]
27+
) -> dict[str, Any]:
28+
"""Create a new function."""
29+
return await self._request(
30+
"create_function", user_id=user_id, function_data=function_data
31+
)
32+
33+
async def update_function(
34+
self, user_id: int, function_id: str, function_patch: dict[str, Any]
35+
) -> dict[str, Any]:
36+
"""Update an existing function."""
37+
return await self._request(
38+
"update_function",
39+
user_id=user_id,
40+
function_id=function_id,
41+
function_patch=function_patch,
42+
)
43+
44+
async def delete_function(self, user_id: int, function_id: str) -> None:
45+
"""Delete a function."""
46+
return await self._request(
47+
"delete_function", user_id=user_id, function_id=function_id
48+
)
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""Licenses RPC API subclient."""
2+
3+
from typing import Any
4+
5+
from ._base import BaseRpcApi
6+
7+
8+
class LicensesRpcApi(BaseRpcApi):
9+
"""RPC client for license-related operations."""
10+
11+
async def get_licensed_items_for_wallet(
12+
self, user_id: int, product_name: str, wallet_id: int | None = None
13+
) -> list[dict[str, Any]]:
14+
"""Get licensed items for a wallet."""
15+
return await self._request(
16+
"get_licensed_items_for_wallet",
17+
user_id=user_id,
18+
product_name=product_name,
19+
wallet_id=wallet_id,
20+
)
21+
22+
async def get_licensed_items_checkouts_for_wallet(
23+
self, user_id: int, product_name: str, wallet_id: int | None = None
24+
) -> list[dict[str, Any]]:
25+
"""Get licensed items checkouts for a wallet."""
26+
return await self._request(
27+
"get_licensed_items_checkouts_for_wallet",
28+
user_id=user_id,
29+
product_name=product_name,
30+
wallet_id=wallet_id,
31+
)
32+
33+
async def checkout_licensed_item_for_wallet(
34+
self,
35+
user_id: int,
36+
product_name: str,
37+
licensed_item_id: str,
38+
wallet_id: int,
39+
num_of_seats: int,
40+
service_run_id: str,
41+
) -> dict[str, Any]:
42+
"""Checkout a licensed item for a wallet."""
43+
return await self._request(
44+
"checkout_licensed_item_for_wallet",
45+
user_id=user_id,
46+
product_name=product_name,
47+
licensed_item_id=licensed_item_id,
48+
wallet_id=wallet_id,
49+
num_of_seats=num_of_seats,
50+
service_run_id=service_run_id,
51+
)
52+
53+
async def release_licensed_item_for_wallet(
54+
self,
55+
user_id: int,
56+
product_name: str,
57+
licensed_item_checkout_id: str,
58+
wallet_id: int,
59+
) -> None:
60+
"""Release a licensed item checkout for a wallet."""
61+
return await self._request(
62+
"release_licensed_item_for_wallet",
63+
user_id=user_id,
64+
product_name=product_name,
65+
licensed_item_checkout_id=licensed_item_checkout_id,
66+
wallet_id=wallet_id,
67+
)
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
"""Projects RPC API subclient."""
2+
3+
from typing import Any
4+
from uuid import UUID
5+
6+
from ._base import BaseRpcApi
7+
8+
9+
class ProjectsRpcApi(BaseRpcApi):
10+
"""RPC client for project-related operations."""
11+
12+
async def get_project(
13+
self, user_id: int, project_uuid: UUID, *, include: list[str] | None = None
14+
) -> dict[str, Any]:
15+
"""Get a project by UUID."""
16+
return await self._request(
17+
"get_project",
18+
user_id=user_id,
19+
project_uuid=project_uuid,
20+
include=include or [],
21+
)
22+
23+
async def list_projects(
24+
self,
25+
user_id: int,
26+
*,
27+
filter_by_services: list[str] | None = None,
28+
filter_by_study_services: list[str] | None = None,
29+
offset: int = 0,
30+
limit: int | None = None,
31+
order_by: dict[str, str] | None = None,
32+
search: str | None = None
33+
) -> dict[str, Any]:
34+
"""List projects for a user."""
35+
return await self._request(
36+
"list_projects",
37+
user_id=user_id,
38+
filter_by_services=filter_by_services or [],
39+
filter_by_study_services=filter_by_study_services or [],
40+
offset=offset,
41+
limit=limit,
42+
order_by=order_by or {},
43+
search=search,
44+
)
45+
46+
async def create_project(
47+
self, user_id: int, project: dict[str, Any], *, as_template: bool = False
48+
) -> dict[str, Any]:
49+
"""Create a new project."""
50+
return await self._request(
51+
"create_project", user_id=user_id, project=project, as_template=as_template
52+
)
53+
54+
async def update_project(
55+
self, user_id: int, project_uuid: UUID, project_patch: dict[str, Any]
56+
) -> dict[str, Any]:
57+
"""Update an existing project."""
58+
return await self._request(
59+
"update_project",
60+
user_id=user_id,
61+
project_uuid=project_uuid,
62+
project_patch=project_patch,
63+
)
64+
65+
async def delete_project(self, user_id: int, project_uuid: UUID) -> None:
66+
"""Delete a project."""
67+
return await self._request(
68+
"delete_project", user_id=user_id, project_uuid=project_uuid
69+
)
70+
71+
async def clone_project(
72+
self, user_id: int, project_uuid: UUID, *, hidden: bool = False
73+
) -> dict[str, Any]:
74+
"""Clone an existing project."""
75+
return await self._request(
76+
"clone_project", user_id=user_id, project_uuid=project_uuid, hidden=hidden
77+
)

0 commit comments

Comments
 (0)