Skip to content

Commit

Permalink
✨ introduce webserver rpc endpoints for licenses (#6946)
Browse files Browse the repository at this point in the history
  • Loading branch information
matusdrobuliak66 authored Dec 11, 2024
1 parent ff6f85a commit e94782d
Show file tree
Hide file tree
Showing 6 changed files with 320 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ async def create_api_key(
product_name=product_name,
api_key=api_key,
)
assert isinstance(result, ApiKeyGet)
assert isinstance(result, ApiKeyGet) # nosec
return result


Expand All @@ -45,7 +45,7 @@ async def get_api_key(
product_name=product_name,
api_key_id=api_key_id,
)
assert isinstance(result, ApiKeyGet)
assert isinstance(result, ApiKeyGet) # nosec
return result


Expand All @@ -63,4 +63,4 @@ async def delete_api_key(
product_name=product_name,
api_key_id=api_key_id,
)
assert result is None
assert result is None # nosec
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import logging

from models_library.api_schemas_webserver import WEBSERVER_RPC_NAMESPACE
from models_library.api_schemas_webserver.licensed_items import (
LicensedItemGet,
LicensedItemGetPage,
)
from models_library.licensed_items import LicensedItemID
from models_library.products import ProductName
from models_library.rabbitmq_basic_types import RPCMethodName
from models_library.resource_tracker import ServiceRunId
from models_library.users import UserID
from models_library.wallets import WalletID
from pydantic import TypeAdapter
from servicelib.logging_utils import log_decorator
from servicelib.rabbitmq import RabbitMQRPCClient

_logger = logging.getLogger(__name__)


@log_decorator(_logger, level=logging.DEBUG)
async def get_licensed_items(
rabbitmq_rpc_client: RabbitMQRPCClient,
*,
product_name: str,
offset: int,
limit: int,
) -> LicensedItemGetPage:
result: LicensedItemGetPage = await rabbitmq_rpc_client.request(
WEBSERVER_RPC_NAMESPACE,
TypeAdapter(RPCMethodName).validate_python("get_licensed_items"),
product_name=product_name,
offset=offset,
limit=limit,
)
assert isinstance(result, LicensedItemGetPage)
return result


@log_decorator(_logger, level=logging.DEBUG)
async def get_licensed_items_for_wallet(
rabbitmq_rpc_client: RabbitMQRPCClient,
*,
user_id: UserID,
product_name: ProductName,
wallet_id: WalletID,
) -> LicensedItemGet:
result: LicensedItemGet = await rabbitmq_rpc_client.request(
WEBSERVER_RPC_NAMESPACE,
TypeAdapter(RPCMethodName).validate_python("get_licensed_items_for_wallet"),
user_id=user_id,
product_name=product_name,
wallet_id=wallet_id,
)
assert isinstance(result, LicensedItemGet) # nosec
return result


@log_decorator(_logger, level=logging.DEBUG)
async def checkout_licensed_item_for_wallet(
rabbitmq_rpc_client: RabbitMQRPCClient,
*,
user_id: UserID,
product_name: ProductName,
wallet_id: WalletID,
licensed_item_id: LicensedItemID,
num_of_seats: int,
service_run_id: ServiceRunId,
) -> None:
result = await rabbitmq_rpc_client.request(
WEBSERVER_RPC_NAMESPACE,
TypeAdapter(RPCMethodName).validate_python("checkout_licensed_item_for_wallet"),
user_id=user_id,
product_name=product_name,
wallet_id=wallet_id,
licensed_item_id=licensed_item_id,
num_of_seats=num_of_seats,
service_run_id=service_run_id,
)
assert result is None # nosec


@log_decorator(_logger, level=logging.DEBUG)
async def release_licensed_item_for_wallet(
rabbitmq_rpc_client: RabbitMQRPCClient,
*,
user_id: UserID,
product_name: ProductName,
wallet_id: WalletID,
licensed_item_id: LicensedItemID,
num_of_seats: int,
service_run_id: ServiceRunId,
) -> None:
result = await rabbitmq_rpc_client.request(
WEBSERVER_RPC_NAMESPACE,
TypeAdapter(RPCMethodName).validate_python("release_licensed_item_for_wallet"),
user_id=user_id,
product_name=product_name,
wallet_id=wallet_id,
licensed_item_id=licensed_item_id,
num_of_seats=num_of_seats,
service_run_id=service_run_id,
)
assert result is None # nosec
80 changes: 80 additions & 0 deletions services/web/server/src/simcore_service_webserver/licenses/_rpc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from aiohttp import web
from models_library.api_schemas_webserver import WEBSERVER_RPC_NAMESPACE
from models_library.api_schemas_webserver.licensed_items import LicensedItemGetPage
from models_library.basic_types import IDStr
from models_library.licensed_items import LicensedItemID
from models_library.products import ProductName
from models_library.resource_tracker import ServiceRunId
from models_library.rest_ordering import OrderBy
from models_library.users import UserID
from models_library.wallets import WalletID
from servicelib.rabbitmq import RPCRouter

from ..rabbitmq import get_rabbitmq_rpc_server
from . import _licensed_items_api

router = RPCRouter()


@router.expose()
async def get_licensed_items(
app: web.Application,
*,
product_name: ProductName,
offset: int,
limit: int,
) -> LicensedItemGetPage:
licensed_item_get_page: LicensedItemGetPage = (
await _licensed_items_api.list_licensed_items(
app=app,
product_name=product_name,
offset=offset,
limit=limit,
order_by=OrderBy(field=IDStr("name")),
)
)
return licensed_item_get_page


@router.expose(reraise_if_error_type=(NotImplementedError,))
async def get_licensed_items_for_wallet(
app: web.Application,
*,
user_id: UserID,
product_name: ProductName,
wallet_id: WalletID,
) -> None:
raise NotImplementedError


@router.expose(reraise_if_error_type=(NotImplementedError,))
async def checkout_licensed_item_for_wallet(
app: web.Application,
*,
user_id: UserID,
product_name: ProductName,
wallet_id: WalletID,
licensed_item_id: LicensedItemID,
num_of_seats: int,
service_run_id: ServiceRunId,
) -> None:
raise NotImplementedError


@router.expose(reraise_if_error_type=(NotImplementedError,))
async def release_licensed_item_for_wallet(
app: web.Application,
*,
user_id: str,
product_name: str,
wallet_id: WalletID,
licensed_item_id: LicensedItemID,
num_of_seats: int,
service_run_id: ServiceRunId,
) -> None:
raise NotImplementedError


async def register_rpc_routes_on_startup(app: web.Application):
rpc_server = get_rabbitmq_rpc_server(app)
await rpc_server.register_router(router, WEBSERVER_RPC_NAMESPACE, app)
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
from servicelib.aiohttp.application_keys import APP_SETTINGS_KEY
from servicelib.aiohttp.application_setup import ModuleCategory, app_module_setup

from . import _licensed_items_handlers, _licensed_items_purchases_handlers
from ..rabbitmq import setup_rabbitmq
from . import _licensed_items_handlers, _licensed_items_purchases_handlers, _rpc

_logger = logging.getLogger(__name__)

Expand All @@ -25,3 +26,7 @@ def setup_licenses(app: web.Application):
# routes
app.router.add_routes(_licensed_items_handlers.routes)
app.router.add_routes(_licensed_items_purchases_handlers.routes)

setup_rabbitmq(app)
if app[APP_SETTINGS_KEY].WEBSERVER_RABBITMQ:
app.on_startup.append(_rpc.register_rpc_routes_on_startup)
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# pylint: disable=redefined-outer-name
# pylint: disable=unused-argument
# pylint: disable=unused-variable


from collections.abc import Awaitable, Callable

import pytest
from aiohttp.test_utils import TestClient
from models_library.licensed_items import LicensedResourceType
from models_library.products import ProductName
from pytest_mock import MockerFixture
from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict
from pytest_simcore.helpers.typing_env import EnvVarsDict
from pytest_simcore.helpers.webserver_login import UserInfoDict
from servicelib.rabbitmq import RabbitMQRPCClient
from servicelib.rabbitmq.rpc_interfaces.webserver.licenses.licensed_items import (
checkout_licensed_item_for_wallet,
get_licensed_items,
get_licensed_items_for_wallet,
release_licensed_item_for_wallet,
)
from settings_library.rabbit import RabbitSettings
from simcore_postgres_database.models.users import UserRole
from simcore_service_webserver.application_settings import ApplicationSettings
from simcore_service_webserver.licenses import _licensed_items_db

pytest_simcore_core_services_selection = [
"rabbit",
]


@pytest.fixture
def app_environment(
rabbit_service: RabbitSettings,
app_environment: EnvVarsDict,
monkeypatch: pytest.MonkeyPatch,
):
new_envs = setenvs_from_dict(
monkeypatch,
{
**app_environment,
"RABBIT_HOST": rabbit_service.RABBIT_HOST,
"RABBIT_PORT": f"{rabbit_service.RABBIT_PORT}",
"RABBIT_USER": rabbit_service.RABBIT_USER,
"RABBIT_SECURE": f"{rabbit_service.RABBIT_SECURE}",
"RABBIT_PASSWORD": rabbit_service.RABBIT_PASSWORD.get_secret_value(),
},
)

settings = ApplicationSettings.create_from_envs()
assert settings.WEBSERVER_RABBITMQ

return new_envs


@pytest.fixture
def user_role() -> UserRole:
return UserRole.USER


@pytest.fixture
async def rpc_client(
rabbitmq_rpc_client: Callable[[str], Awaitable[RabbitMQRPCClient]],
mocker: MockerFixture,
) -> RabbitMQRPCClient:
return await rabbitmq_rpc_client("client")


async def test_api_keys_workflow(
client: TestClient,
rpc_client: RabbitMQRPCClient,
osparc_product_name: ProductName,
logged_user: UserInfoDict,
pricing_plan_id: int,
):
assert client.app

result = await get_licensed_items(
rpc_client, product_name=osparc_product_name, offset=0, limit=20
)
assert len(result.items) == 0
assert result.total == 0

await _licensed_items_db.create(
client.app,
product_name=osparc_product_name,
name="Model A",
licensed_resource_type=LicensedResourceType.VIP_MODEL,
pricing_plan_id=pricing_plan_id,
)

result = await get_licensed_items(
rpc_client, product_name=osparc_product_name, offset=0, limit=20
)
assert len(result.items) == 1
assert result.total == 1

with pytest.raises(NotImplementedError):
await get_licensed_items_for_wallet(
rpc_client,
user_id=logged_user["id"],
product_name=osparc_product_name,
wallet_id=1,
)

with pytest.raises(NotImplementedError):
await checkout_licensed_item_for_wallet(
rpc_client,
user_id=logged_user["id"],
product_name=osparc_product_name,
wallet_id=1,
licensed_item_id="c5139a2e-4e1f-4ebe-9bfd-d17f195111ee",
num_of_seats=1,
service_run_id="run_1",
)

with pytest.raises(NotImplementedError):
await release_licensed_item_for_wallet(
rpc_client,
user_id=logged_user["id"],
product_name=osparc_product_name,
wallet_id=1,
licensed_item_id="c5139a2e-4e1f-4ebe-9bfd-d17f195111ee",
num_of_seats=1,
service_run_id="run_1",
)

0 comments on commit e94782d

Please sign in to comment.