Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ introduce webserver rpc endpoints for licenses #6946

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
829eae9
db layer licensed items purchase
matusdrobuliak66 Dec 9, 2024
a975a63
rut part
matusdrobuliak66 Dec 10, 2024
fc62c0d
merge master
matusdrobuliak66 Dec 10, 2024
78ef2cc
renaming tests
matusdrobuliak66 Dec 10, 2024
a6f49cd
rpc interface
matusdrobuliak66 Dec 10, 2024
f5de79b
RUT unit tests
matusdrobuliak66 Dec 10, 2024
acd4c88
RUT unit tests
matusdrobuliak66 Dec 10, 2024
317cf1d
RUT unit tests
matusdrobuliak66 Dec 10, 2024
932fb00
webserver part
matusdrobuliak66 Dec 10, 2024
06f5743
webserver part
matusdrobuliak66 Dec 10, 2024
dbd1ffe
open api specs
matusdrobuliak66 Dec 10, 2024
6bec5ff
webserver part tests
matusdrobuliak66 Dec 10, 2024
754cd58
Merge branch 'master' into introduce-vip-models-pricing-4-part
matusdrobuliak66 Dec 10, 2024
3755dce
open api specs
matusdrobuliak66 Dec 10, 2024
8f264b1
Merge branch 'introduce-vip-models-pricing-4-part' of github.com:matu…
matusdrobuliak66 Dec 10, 2024
4185852
fix type
matusdrobuliak66 Dec 10, 2024
4c31cd0
fix type
matusdrobuliak66 Dec 10, 2024
530fda0
fix type
matusdrobuliak66 Dec 10, 2024
00d40bd
fix type
matusdrobuliak66 Dec 10, 2024
fa3c648
Merge branch 'master' into introduce-vip-models-pricing-4-part
matusdrobuliak66 Dec 10, 2024
12ebf32
Merge branch 'master' into introduce-vip-models-pricing-4-part
matusdrobuliak66 Dec 11, 2024
969f982
review @pcrespov
matusdrobuliak66 Dec 11, 2024
138412e
adding purchase item functionality
matusdrobuliak66 Dec 11, 2024
7e9de14
open api specs
matusdrobuliak66 Dec 11, 2024
4f1c5d6
improve error handling
matusdrobuliak66 Dec 11, 2024
26f9c57
open api specs
matusdrobuliak66 Dec 11, 2024
583b2b4
fix import
matusdrobuliak66 Dec 11, 2024
4a1008f
fix import
matusdrobuliak66 Dec 11, 2024
776806b
fix typecheck
matusdrobuliak66 Dec 11, 2024
fadfa1f
fix typecheck
matusdrobuliak66 Dec 11, 2024
e1337f0
Merge branch 'master' into introduce-vip-models-pricing-5-part
matusdrobuliak66 Dec 11, 2024
0766313
contract between webserver and api server
matusdrobuliak66 Dec 11, 2024
f09fb33
Merge branch 'master' into introduce-vip-models-pricing-5-part
matusdrobuliak66 Dec 11, 2024
f0614a0
Merge branch 'master' into introduce-vip-models-pricing-5-part
matusdrobuliak66 Dec 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
matusdrobuliak66 marked this conversation as resolved.
Show resolved Hide resolved
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
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",
)
Loading