diff --git a/api/specs/web-server/_licensed_items_checkouts.py b/api/specs/web-server/_licensed_items_checkouts.py new file mode 100644 index 00000000000..cfc51a7c424 --- /dev/null +++ b/api/specs/web-server/_licensed_items_checkouts.py @@ -0,0 +1,57 @@ +""" Helper script to generate OAS automatically +""" + +# pylint: disable=redefined-outer-name +# pylint: disable=unused-argument +# pylint: disable=unused-variable +# pylint: disable=too-many-arguments + +from typing import Annotated + +from _common import as_query +from fastapi import APIRouter, Depends +from models_library.api_schemas_webserver.licensed_items_purchases import ( + LicensedItemPurchaseGet, +) +from models_library.generics import Envelope +from models_library.rest_error import EnvelopedError +from models_library.rest_pagination import Page +from simcore_service_webserver._meta import API_VTAG +from simcore_service_webserver.licenses._exceptions_handlers import _TO_HTTP_ERROR_MAP +from simcore_service_webserver.licenses._licensed_items_checkouts_models import ( + LicensedItemCheckoutPathParams, + LicensedItemsCheckoutsListQueryParams, +) +from simcore_service_webserver.wallets._handlers import WalletsPathParams + +router = APIRouter( + prefix=f"/{API_VTAG}", + tags=[ + "licenses", + ], + responses={ + i.status_code: {"model": EnvelopedError} for i in _TO_HTTP_ERROR_MAP.values() + }, +) + + +@router.get( + "/wallets/{wallet_id}/licensed-items-checkouts", + response_model=Page[LicensedItemPurchaseGet], + tags=["wallets"], +) +async def list_licensed_item_checkouts_for_wallet( + _path: Annotated[WalletsPathParams, Depends()], + _query: Annotated[as_query(LicensedItemsCheckoutsListQueryParams), Depends()], +): + ... + + +@router.get( + "/licensed-items-checkouts/{licensed_item_checkout_id}", + response_model=Envelope[LicensedItemPurchaseGet], +) +async def get_licensed_item_checkout( + _path: Annotated[LicensedItemCheckoutPathParams, Depends()], +): + ... diff --git a/api/specs/web-server/openapi.py b/api/specs/web-server/openapi.py index 6c3bc639fb4..cfcaf183591 100644 --- a/api/specs/web-server/openapi.py +++ b/api/specs/web-server/openapi.py @@ -38,6 +38,7 @@ "_long_running_tasks", "_licensed_items", "_licensed_items_purchases", + "_licensed_items_checkouts", "_metamodeling", "_nih_sparc", "_nih_sparc_redirections", diff --git a/packages/models-library/src/models_library/api_schemas_webserver/licensed_items_checkouts.py b/packages/models-library/src/models_library/api_schemas_webserver/licensed_items_checkouts.py index a3ee122ddee..c8fd22ce581 100644 --- a/packages/models-library/src/models_library/api_schemas_webserver/licensed_items_checkouts.py +++ b/packages/models-library/src/models_library/api_schemas_webserver/licensed_items_checkouts.py @@ -1,7 +1,7 @@ from datetime import datetime from typing import NamedTuple -from pydantic import PositiveInt +from pydantic import BaseModel, PositiveInt from ..licensed_items import LicensedItemID from ..products import ProductName @@ -10,8 +10,29 @@ from ..wallets import WalletID from ._base import OutputSchema +# RPC -class LicensedItemCheckoutGet(OutputSchema): + +class LicensedItemCheckoutRpcGet(BaseModel): + licensed_item_checkout_id: LicensedItemCheckoutID + licensed_item_id: LicensedItemID + wallet_id: WalletID + user_id: UserID + product_name: ProductName + started_at: datetime + stopped_at: datetime | None + num_of_seats: int + + +class LicensedItemCheckoutRpcGetPage(NamedTuple): + items: list[LicensedItemCheckoutRpcGet] + total: PositiveInt + + +# Rest + + +class LicensedItemCheckoutRestGet(OutputSchema): licensed_item_checkout_id: LicensedItemCheckoutID licensed_item_id: LicensedItemID wallet_id: WalletID @@ -22,6 +43,6 @@ class LicensedItemCheckoutGet(OutputSchema): num_of_seats: int -class LicensedItemUsageGetPage(NamedTuple): - items: list[LicensedItemCheckoutGet] +class LicensedItemCheckoutRestGetPage(NamedTuple): + items: list[LicensedItemCheckoutRestGet] total: PositiveInt diff --git a/packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/webserver/licenses/licensed_items.py b/packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/webserver/licenses/licensed_items.py index f767882d247..0f86ab63d79 100644 --- a/packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/webserver/licenses/licensed_items.py +++ b/packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/webserver/licenses/licensed_items.py @@ -6,7 +6,7 @@ LicensedItemGetPage, ) from models_library.api_schemas_webserver.licensed_items_checkouts import ( - LicensedItemCheckoutGet, + LicensedItemCheckoutRpcGet, ) from models_library.licensed_items import LicensedItemID from models_library.products import ProductName @@ -78,7 +78,7 @@ async def checkout_licensed_item_for_wallet( licensed_item_id: LicensedItemID, num_of_seats: int, service_run_id: ServiceRunID, -) -> LicensedItemCheckoutGet: +) -> LicensedItemCheckoutRpcGet: result = await rabbitmq_rpc_client.request( WEBSERVER_RPC_NAMESPACE, TypeAdapter(RPCMethodName).validate_python("checkout_licensed_item_for_wallet"), @@ -89,7 +89,7 @@ async def checkout_licensed_item_for_wallet( num_of_seats=num_of_seats, service_run_id=service_run_id, ) - assert isinstance(result, LicensedItemCheckoutGet) # nosec + assert isinstance(result, LicensedItemCheckoutRpcGet) # nosec return result @@ -100,7 +100,7 @@ async def release_licensed_item_for_wallet( product_name: ProductName, user_id: UserID, licensed_item_checkout_id: LicensedItemCheckoutID, -) -> LicensedItemCheckoutGet: +) -> LicensedItemCheckoutRpcGet: result = await rabbitmq_rpc_client.request( WEBSERVER_RPC_NAMESPACE, TypeAdapter(RPCMethodName).validate_python("release_licensed_item_for_wallet"), @@ -108,5 +108,5 @@ async def release_licensed_item_for_wallet( user_id=user_id, licensed_item_checkout_id=licensed_item_checkout_id, ) - assert isinstance(result, LicensedItemCheckoutGet) # nosec + assert isinstance(result, LicensedItemCheckoutRpcGet) # nosec return result diff --git a/services/catalog/src/simcore_service_catalog/api/rest/_services.py b/services/catalog/src/simcore_service_catalog/api/rest/_services.py index e2abc23d179..78362d63733 100644 --- a/services/catalog/src/simcore_service_catalog/api/rest/_services.py +++ b/services/catalog/src/simcore_service_catalog/api/rest/_services.py @@ -9,6 +9,7 @@ from fastapi import APIRouter, Depends, Header, HTTPException, status from models_library.api_schemas_catalog.services import ServiceGet, ServiceUpdate from models_library.services import ServiceKey, ServiceType, ServiceVersion +from models_library.services_authoring import Author from models_library.services_metadata_published import ServiceMetaDataPublished from pydantic import ValidationError from pydantic.types import PositiveInt @@ -127,7 +128,11 @@ async def list_services( name="nodetails", description="nodetails", type=ServiceType.COMPUTATIONAL, - authors=[{"name": "nodetails", "email": "nodetails@nodetails.com"}], + authors=[ + Author.model_construct( + name="nodetails", email="nodetails@nodetails.com" + ) + ], contact="nodetails@nodetails.com", inputs={}, outputs={}, diff --git a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml index a705e824ab7..2012853da80 100644 --- a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml +++ b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml @@ -3278,6 +3278,121 @@ paths: schema: $ref: '#/components/schemas/EnvelopedError' description: Bad Request + /v0/wallets/{wallet_id}/licensed-items-checkouts: + get: + tags: + - licenses + - wallets + summary: List Licensed Item Checkouts For Wallet + operationId: list_licensed_item_checkouts_for_wallet + parameters: + - name: wallet_id + in: path + required: true + schema: + type: integer + exclusiveMinimum: true + title: Wallet Id + minimum: 0 + - name: order_by + in: query + required: false + schema: + type: string + contentMediaType: application/json + contentSchema: {} + default: '{"field":"started_at","direction":"desc"}' + title: Order By + - name: limit + in: query + required: false + schema: + type: integer + default: 20 + title: Limit + - name: offset + in: query + required: false + schema: + type: integer + default: 0 + title: Offset + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Page_LicensedItemPurchaseGet_' + '404': + content: + application/json: + schema: + $ref: '#/components/schemas/EnvelopedError' + description: Not Found + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/EnvelopedError' + description: Forbidden + '402': + content: + application/json: + schema: + $ref: '#/components/schemas/EnvelopedError' + description: Payment Required + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/EnvelopedError' + description: Bad Request + /v0/licensed-items-checkouts/{licensed_item_checkout_id}: + get: + tags: + - licenses + summary: Get Licensed Item Checkout + operationId: get_licensed_item_checkout + parameters: + - name: licensed_item_checkout_id + in: path + required: true + schema: + type: string + format: uuid + title: Licensed Item Checkout Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/Envelope_LicensedItemPurchaseGet_' + '404': + content: + application/json: + schema: + $ref: '#/components/schemas/EnvelopedError' + description: Not Found + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/EnvelopedError' + description: Forbidden + '402': + content: + application/json: + schema: + $ref: '#/components/schemas/EnvelopedError' + description: Payment Required + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/EnvelopedError' + description: Bad Request /v0/projects/{project_uuid}/checkpoint/{ref_id}/iterations: get: tags: diff --git a/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_checkouts_models.py b/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_checkouts_models.py new file mode 100644 index 00000000000..43d6a290d6f --- /dev/null +++ b/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_checkouts_models.py @@ -0,0 +1,56 @@ +from datetime import datetime +from typing import NamedTuple + +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_licensed_items_checkouts import ( + LicensedItemCheckoutID, +) +from models_library.rest_base import RequestParameters, StrictRequestParameters +from models_library.rest_ordering import ( + OrderBy, + OrderDirection, + create_ordering_query_model_class, +) +from models_library.rest_pagination import PageQueryParameters +from models_library.users import UserID +from models_library.wallets import WalletID +from pydantic import BaseModel, PositiveInt + + +class LicensedItemCheckoutGet(BaseModel): + licensed_item_checkout_id: LicensedItemCheckoutID + licensed_item_id: LicensedItemID + wallet_id: WalletID + user_id: UserID + product_name: ProductName + started_at: datetime + stopped_at: datetime | None + num_of_seats: int + + +class LicensedItemCheckoutGetPage(NamedTuple): + items: list[LicensedItemCheckoutGet] + total: PositiveInt + + +class LicensedItemCheckoutPathParams(StrictRequestParameters): + licensed_item_checkout_id: LicensedItemCheckoutID + + +_LicensedItemsCheckoutsListOrderQueryParams: type[ + RequestParameters +] = create_ordering_query_model_class( + ordering_fields={ + "started_at", + }, + default=OrderBy(field=IDStr("started_at"), direction=OrderDirection.DESC), +) + + +class LicensedItemsCheckoutsListQueryParams( + PageQueryParameters, + _LicensedItemsCheckoutsListOrderQueryParams, # type: ignore[misc, valid-type] +): + ... diff --git a/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_checkouts_rest.py b/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_checkouts_rest.py new file mode 100644 index 00000000000..1a9c7285d0a --- /dev/null +++ b/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_checkouts_rest.py @@ -0,0 +1,129 @@ +import logging + +from aiohttp import web +from models_library.api_schemas_webserver.licensed_items_checkouts import ( + LicensedItemCheckoutRestGet, + LicensedItemCheckoutRestGetPage, +) +from models_library.rest_ordering import OrderBy +from models_library.rest_pagination import Page +from models_library.rest_pagination_utils import paginate_data +from servicelib.aiohttp.requests_validation import ( + parse_request_path_parameters_as, + parse_request_query_parameters_as, +) +from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON +from servicelib.rest_constants import RESPONSE_MODEL_POLICY + +from .._meta import API_VTAG as VTAG +from ..login.decorators import login_required +from ..security.decorators import permission_required +from ..utils_aiohttp import envelope_json_response +from ..wallets._handlers import WalletsPathParams +from . import _licensed_items_checkouts_service +from ._exceptions_handlers import handle_plugin_requests_exceptions +from ._licensed_items_checkouts_models import ( + LicensedItemCheckoutGet, + LicensedItemCheckoutGetPage, + LicensedItemCheckoutPathParams, +) +from ._models import LicensedItemsPurchasesListQueryParams, LicensedItemsRequestContext + +_logger = logging.getLogger(__name__) + + +routes = web.RouteTableDef() + + +@routes.get( + f"/{VTAG}/licensed-items-checkouts/{{licensed_item_checkout_id}}", + name="get_licensed_item_checkout", +) +@login_required +@permission_required("catalog/licensed-items.*") +@handle_plugin_requests_exceptions +async def get_licensed_item_checkout(request: web.Request): + req_ctx = LicensedItemsRequestContext.model_validate(request) + path_params = parse_request_path_parameters_as( + LicensedItemCheckoutPathParams, request + ) + + checkout_item: LicensedItemCheckoutGet = ( + await _licensed_items_checkouts_service.get_licensed_item_checkout( + app=request.app, + product_name=req_ctx.product_name, + user_id=req_ctx.user_id, + licensed_item_checkout_id=path_params.licensed_item_checkout_id, + ) + ) + + output = LicensedItemCheckoutRestGet.model_construct( + licensed_item_checkout_id=checkout_item.licensed_item_checkout_id, + licensed_item_id=checkout_item.licensed_item_id, + wallet_id=checkout_item.wallet_id, + user_id=checkout_item.user_id, + product_name=checkout_item.product_name, + started_at=checkout_item.started_at, + stopped_at=checkout_item.stopped_at, + num_of_seats=checkout_item.num_of_seats, + ) + + return envelope_json_response(output) + + +@routes.get( + f"/{VTAG}/wallets/{{wallet_id}}/licensed-items-checkouts", + name="list_licensed_item_checkouts_for_wallet", +) +@login_required +@permission_required("catalog/licensed-items.*") +@handle_plugin_requests_exceptions +async def list_licensed_item_checkouts_for_wallet(request: web.Request): + req_ctx = LicensedItemsRequestContext.model_validate(request) + path_params = parse_request_path_parameters_as(WalletsPathParams, request) + query_params: LicensedItemsPurchasesListQueryParams = ( + parse_request_query_parameters_as( + LicensedItemsPurchasesListQueryParams, request + ) + ) + + result: LicensedItemCheckoutGetPage = await _licensed_items_checkouts_service.list_licensed_items_checkouts_for_wallet( + app=request.app, + product_name=req_ctx.product_name, + user_id=req_ctx.user_id, + wallet_id=path_params.wallet_id, + offset=query_params.offset, + limit=query_params.limit, + order_by=OrderBy.model_construct(**query_params.order_by.model_dump()), + ) + + get_page = LicensedItemCheckoutRestGetPage( + total=result.total, + items=[ + LicensedItemCheckoutRestGet.model_construct( + licensed_item_checkout_id=checkout_item.licensed_item_checkout_id, + licensed_item_id=checkout_item.licensed_item_id, + wallet_id=checkout_item.wallet_id, + user_id=checkout_item.user_id, + product_name=checkout_item.product_name, + started_at=checkout_item.started_at, + stopped_at=checkout_item.stopped_at, + num_of_seats=checkout_item.num_of_seats, + ) + for checkout_item in result.items + ], + ) + + page = Page[LicensedItemCheckoutRestGet].model_validate( + paginate_data( + chunk=get_page.items, + request_url=request.url, + total=get_page.total, + limit=query_params.limit, + offset=query_params.offset, + ) + ) + return web.Response( + text=page.model_dump_json(**RESPONSE_MODEL_POLICY), + content_type=MIMETYPE_APPLICATION_JSON, + ) diff --git a/services/web/server/src/simcore_service_webserver/licenses/_licensed_checkouts_api.py b/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_checkouts_service.py similarity index 55% rename from services/web/server/src/simcore_service_webserver/licenses/_licensed_checkouts_api.py rename to services/web/server/src/simcore_service_webserver/licenses/_licensed_items_checkouts_service.py index ba140d565d5..87a8aaf14c5 100644 --- a/services/web/server/src/simcore_service_webserver/licenses/_licensed_checkouts_api.py +++ b/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_checkouts_service.py @@ -2,14 +2,12 @@ from models_library.api_schemas_resource_usage_tracker import ( licensed_items_checkouts as rut_licensed_items_checkouts, ) -from models_library.api_schemas_webserver import ( - licensed_items_checkouts as webserver_licensed_items_checkouts, -) from models_library.licensed_items import LicensedItemID from models_library.products import ProductName from models_library.resource_tracker_licensed_items_checkouts import ( LicensedItemCheckoutID, ) +from models_library.rest_ordering import OrderBy from models_library.services_types import ServiceRunID from models_library.users import UserID from models_library.wallets import WalletID @@ -20,6 +18,94 @@ from ..rabbitmq import get_rabbitmq_rpc_client from ..users.api import get_user from ..wallets.api import get_wallet_by_user +from ._licensed_items_checkouts_models import ( + LicensedItemCheckoutGet, + LicensedItemCheckoutGetPage, +) + + +async def list_licensed_items_checkouts_for_wallet( + app: web.Application, + *, + # access context + product_name: ProductName, + user_id: UserID, + wallet_id: WalletID, + offset: int, + limit: int, + order_by: OrderBy, +) -> LicensedItemCheckoutGetPage: + # Check whether user has access to the wallet + await get_wallet_by_user( + app, + user_id=user_id, + wallet_id=wallet_id, + product_name=product_name, + ) + + rpc_client = get_rabbitmq_rpc_client(app) + + result = await licensed_items_checkouts.get_licensed_items_checkouts_page( + rpc_client, + product_name=product_name, + filter_wallet_id=wallet_id, + offset=offset, + limit=limit, + order_by=order_by, + ) + + return LicensedItemCheckoutGetPage( + total=result.total, + items=[ + LicensedItemCheckoutGet.model_construct( + licensed_item_checkout_id=checkout_item.licensed_item_checkout_id, + licensed_item_id=checkout_item.licensed_item_id, + wallet_id=checkout_item.wallet_id, + user_id=checkout_item.user_id, + product_name=checkout_item.product_name, + started_at=checkout_item.started_at, + stopped_at=checkout_item.stopped_at, + num_of_seats=checkout_item.num_of_seats, + ) + for checkout_item in result.items + ], + ) + + +async def get_licensed_item_checkout( + app: web.Application, + *, + # access context + product_name: ProductName, + user_id: UserID, + licensed_item_checkout_id: LicensedItemCheckoutID, +) -> LicensedItemCheckoutGet: + rpc_client = get_rabbitmq_rpc_client(app) + + checkout_item = await licensed_items_checkouts.get_licensed_item_checkout( + rpc_client, + product_name=product_name, + licensed_item_checkout_id=licensed_item_checkout_id, + ) + + # Check whether user has access to the wallet + await get_wallet_by_user( + app, + user_id=user_id, + wallet_id=checkout_item.wallet_id, + product_name=product_name, + ) + + return LicensedItemCheckoutGet.model_construct( + licensed_item_checkout_id=checkout_item.licensed_item_checkout_id, + licensed_item_id=checkout_item.licensed_item_id, + wallet_id=checkout_item.wallet_id, + user_id=checkout_item.user_id, + product_name=checkout_item.product_name, + started_at=checkout_item.started_at, + stopped_at=checkout_item.stopped_at, + num_of_seats=checkout_item.num_of_seats, + ) async def checkout_licensed_item_for_wallet( @@ -33,7 +119,7 @@ async def checkout_licensed_item_for_wallet( licensed_item_id: LicensedItemID, num_of_seats: int, service_run_id: ServiceRunID, -) -> webserver_licensed_items_checkouts.LicensedItemCheckoutGet: +) -> LicensedItemCheckoutGet: # Check whether user has access to the wallet await get_wallet_by_user( app, @@ -58,7 +144,7 @@ async def checkout_licensed_item_for_wallet( ) ) - return webserver_licensed_items_checkouts.LicensedItemCheckoutGet( + return LicensedItemCheckoutGet.model_construct( licensed_item_checkout_id=licensed_item_get.licensed_item_checkout_id, licensed_item_id=licensed_item_get.licensed_item_id, wallet_id=licensed_item_get.wallet_id, @@ -78,7 +164,7 @@ async def release_licensed_item_for_wallet( user_id: UserID, # release args licensed_item_checkout_id: LicensedItemCheckoutID, -) -> webserver_licensed_items_checkouts.LicensedItemCheckoutGet: +) -> LicensedItemCheckoutGet: rpc_client = get_rabbitmq_rpc_client(app) checkout_item = await licensed_items_checkouts.get_licensed_item_checkout( @@ -103,7 +189,7 @@ async def release_licensed_item_for_wallet( ) ) - return webserver_licensed_items_checkouts.LicensedItemCheckoutGet( + return LicensedItemCheckoutGet.model_construct( licensed_item_checkout_id=licensed_item_get.licensed_item_checkout_id, licensed_item_id=licensed_item_get.licensed_item_id, wallet_id=licensed_item_get.wallet_id, diff --git a/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_purchases_handlers.py b/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_purchases_rest.py similarity index 94% rename from services/web/server/src/simcore_service_webserver/licenses/_licensed_items_purchases_handlers.py rename to services/web/server/src/simcore_service_webserver/licenses/_licensed_items_purchases_rest.py index 95f48ebbd0e..5ae0738ebe1 100644 --- a/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_purchases_handlers.py +++ b/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_purchases_rest.py @@ -20,7 +20,7 @@ from ..security.decorators import permission_required from ..utils_aiohttp import envelope_json_response from ..wallets._handlers import WalletsPathParams -from . import _licensed_items_purchases_api +from . import _licensed_items_purchases_service from ._exceptions_handlers import handle_plugin_requests_exceptions from ._models import ( LicensedItemsPurchasesListQueryParams, @@ -47,7 +47,7 @@ async def get_licensed_item_purchase(request: web.Request): ) licensed_item_purchase_get: LicensedItemPurchaseGet = ( - await _licensed_items_purchases_api.get_licensed_item_purchase( + await _licensed_items_purchases_service.get_licensed_item_purchase( app=request.app, product_name=req_ctx.product_name, user_id=req_ctx.user_id, @@ -75,7 +75,7 @@ async def list_wallet_licensed_items_purchases(request: web.Request): ) licensed_item_purchase_get_page: LicensedItemPurchaseGetPage = ( - await _licensed_items_purchases_api.list_licensed_items_purchases( + await _licensed_items_purchases_service.list_licensed_items_purchases( app=request.app, product_name=req_ctx.product_name, user_id=req_ctx.user_id, diff --git a/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_purchases_api.py b/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_purchases_service.py similarity index 99% rename from services/web/server/src/simcore_service_webserver/licenses/_licensed_items_purchases_api.py rename to services/web/server/src/simcore_service_webserver/licenses/_licensed_items_purchases_service.py index b42d593bed1..2cfa6355f83 100644 --- a/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_purchases_api.py +++ b/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_purchases_service.py @@ -75,6 +75,7 @@ async def list_licensed_items_purchases( async def get_licensed_item_purchase( app: web.Application, + *, product_name: ProductName, user_id: UserID, licensed_item_purchase_id: LicensedItemPurchaseID, diff --git a/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_db.py b/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_repository.py similarity index 100% rename from services/web/server/src/simcore_service_webserver/licenses/_licensed_items_db.py rename to services/web/server/src/simcore_service_webserver/licenses/_licensed_items_repository.py diff --git a/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_handlers.py b/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_rest.py similarity index 89% rename from services/web/server/src/simcore_service_webserver/licenses/_licensed_items_handlers.py rename to services/web/server/src/simcore_service_webserver/licenses/_licensed_items_rest.py index 355d9658ebb..4f0a936c041 100644 --- a/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_handlers.py +++ b/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_rest.py @@ -21,7 +21,7 @@ from ..login.decorators import login_required from ..security.decorators import permission_required from ..utils_aiohttp import envelope_json_response -from . import _licensed_items_api +from . import _licensed_items_service from ._exceptions_handlers import handle_plugin_requests_exceptions from ._models import ( LicensedItemsBodyParams, @@ -47,7 +47,7 @@ async def list_licensed_items(request: web.Request): ) licensed_item_get_page: LicensedItemGetPage = ( - await _licensed_items_api.list_licensed_items( + await _licensed_items_service.list_licensed_items( app=request.app, product_name=req_ctx.product_name, offset=query_params.offset, @@ -81,10 +81,12 @@ async def get_licensed_item(request: web.Request): req_ctx = LicensedItemsRequestContext.model_validate(request) path_params = parse_request_path_parameters_as(LicensedItemsPathParams, request) - licensed_item_get: LicensedItemGet = await _licensed_items_api.get_licensed_item( - app=request.app, - licensed_item_id=path_params.licensed_item_id, - product_name=req_ctx.product_name, + licensed_item_get: LicensedItemGet = ( + await _licensed_items_service.get_licensed_item( + app=request.app, + licensed_item_id=path_params.licensed_item_id, + product_name=req_ctx.product_name, + ) ) return envelope_json_response(licensed_item_get) @@ -102,7 +104,7 @@ async def purchase_licensed_item(request: web.Request): path_params = parse_request_path_parameters_as(LicensedItemsPathParams, request) body_params = await parse_request_body_as(LicensedItemsBodyParams, request) - await _licensed_items_api.purchase_licensed_item( + await _licensed_items_service.purchase_licensed_item( app=request.app, user_id=req_ctx.user_id, licensed_item_id=path_params.licensed_item_id, diff --git a/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_api.py b/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_service.py similarity index 96% rename from services/web/server/src/simcore_service_webserver/licenses/_licensed_items_api.py rename to services/web/server/src/simcore_service_webserver/licenses/_licensed_items_service.py index 1f839ae31fa..374da33bbbe 100644 --- a/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_api.py +++ b/services/web/server/src/simcore_service_webserver/licenses/_licensed_items_service.py @@ -25,7 +25,7 @@ from ..users.api import get_user from ..wallets.api import get_wallet_with_available_credits_by_user_and_wallet from ..wallets.errors import WalletNotEnoughCreditsError -from . import _licensed_items_db +from . import _licensed_items_repository from ._models import LicensedItemsBodyParams from .errors import LicensedItemPricingPlanMatchError @@ -39,7 +39,7 @@ async def get_licensed_item( product_name: ProductName, ) -> LicensedItemGet: - licensed_item_db = await _licensed_items_db.get( + licensed_item_db = await _licensed_items_repository.get( app, licensed_item_id=licensed_item_id, product_name=product_name ) return LicensedItemGet( @@ -61,7 +61,7 @@ async def list_licensed_items( limit: int, order_by: OrderBy, ) -> LicensedItemGetPage: - total_count, licensed_item_db_list = await _licensed_items_db.list_( + total_count, licensed_item_db_list = await _licensed_items_repository.list_( app, product_name=product_name, offset=offset, limit=limit, order_by=order_by ) return LicensedItemGetPage( diff --git a/services/web/server/src/simcore_service_webserver/licenses/_rpc.py b/services/web/server/src/simcore_service_webserver/licenses/_rpc.py index 8be794e4016..261eb51c3aa 100644 --- a/services/web/server/src/simcore_service_webserver/licenses/_rpc.py +++ b/services/web/server/src/simcore_service_webserver/licenses/_rpc.py @@ -2,7 +2,7 @@ from models_library.api_schemas_webserver import WEBSERVER_RPC_NAMESPACE from models_library.api_schemas_webserver.licensed_items import LicensedItemGetPage from models_library.api_schemas_webserver.licensed_items_checkouts import ( - LicensedItemCheckoutGet, + LicensedItemCheckoutRpcGet, ) from models_library.basic_types import IDStr from models_library.licensed_items import LicensedItemID @@ -20,7 +20,7 @@ ) from ..rabbitmq import get_rabbitmq_rpc_server -from . import _licensed_checkouts_api, _licensed_items_api +from . import _licensed_items_checkouts_service, _licensed_items_service router = RPCRouter() @@ -34,7 +34,7 @@ async def get_licensed_items( limit: int, ) -> LicensedItemGetPage: licensed_item_get_page: LicensedItemGetPage = ( - await _licensed_items_api.list_licensed_items( + await _licensed_items_service.list_licensed_items( app=app, product_name=product_name, offset=offset, @@ -68,15 +68,27 @@ async def checkout_licensed_item_for_wallet( licensed_item_id: LicensedItemID, num_of_seats: int, service_run_id: ServiceRunID, -) -> LicensedItemCheckoutGet: - return await _licensed_checkouts_api.checkout_licensed_item_for_wallet( - app, - licensed_item_id=licensed_item_id, - wallet_id=wallet_id, - product_name=product_name, - num_of_seats=num_of_seats, - service_run_id=service_run_id, - user_id=user_id, +) -> LicensedItemCheckoutRpcGet: + licensed_item_get = ( + await _licensed_items_checkouts_service.checkout_licensed_item_for_wallet( + app, + licensed_item_id=licensed_item_id, + wallet_id=wallet_id, + product_name=product_name, + num_of_seats=num_of_seats, + service_run_id=service_run_id, + user_id=user_id, + ) + ) + return LicensedItemCheckoutRpcGet.model_construct( + licensed_item_checkout_id=licensed_item_get.licensed_item_checkout_id, + licensed_item_id=licensed_item_get.licensed_item_id, + wallet_id=licensed_item_get.wallet_id, + user_id=licensed_item_get.user_id, + product_name=licensed_item_get.product_name, + started_at=licensed_item_get.started_at, + stopped_at=licensed_item_get.stopped_at, + num_of_seats=licensed_item_get.num_of_seats, ) @@ -87,12 +99,24 @@ async def release_licensed_item_for_wallet( product_name: ProductName, user_id: UserID, licensed_item_checkout_id: LicensedItemCheckoutID, -) -> LicensedItemCheckoutGet: - return await _licensed_checkouts_api.release_licensed_item_for_wallet( - app, - product_name=product_name, - user_id=user_id, - licensed_item_checkout_id=licensed_item_checkout_id, +) -> LicensedItemCheckoutRpcGet: + licensed_item_get = ( + await _licensed_items_checkouts_service.release_licensed_item_for_wallet( + app, + product_name=product_name, + user_id=user_id, + licensed_item_checkout_id=licensed_item_checkout_id, + ) + ) + return LicensedItemCheckoutRpcGet.model_construct( + licensed_item_checkout_id=licensed_item_get.licensed_item_checkout_id, + licensed_item_id=licensed_item_get.licensed_item_id, + wallet_id=licensed_item_get.wallet_id, + user_id=licensed_item_get.user_id, + product_name=licensed_item_get.product_name, + started_at=licensed_item_get.started_at, + stopped_at=licensed_item_get.stopped_at, + num_of_seats=licensed_item_get.num_of_seats, ) diff --git a/services/web/server/src/simcore_service_webserver/licenses/plugin.py b/services/web/server/src/simcore_service_webserver/licenses/plugin.py index 137c7b2d1dc..72af99badeb 100644 --- a/services/web/server/src/simcore_service_webserver/licenses/plugin.py +++ b/services/web/server/src/simcore_service_webserver/licenses/plugin.py @@ -8,7 +8,12 @@ from servicelib.aiohttp.application_setup import ModuleCategory, app_module_setup from ..rabbitmq import setup_rabbitmq -from . import _licensed_items_handlers, _licensed_items_purchases_handlers, _rpc +from . import ( + _licensed_items_checkouts_rest, + _licensed_items_purchases_rest, + _licensed_items_rest, + _rpc, +) _logger = logging.getLogger(__name__) @@ -24,8 +29,9 @@ def setup_licenses(app: web.Application): assert app[APP_SETTINGS_KEY].WEBSERVER_LICENSES # nosec # routes - app.router.add_routes(_licensed_items_handlers.routes) - app.router.add_routes(_licensed_items_purchases_handlers.routes) + app.router.add_routes(_licensed_items_rest.routes) + app.router.add_routes(_licensed_items_purchases_rest.routes) + app.router.add_routes(_licensed_items_checkouts_rest.routes) setup_rabbitmq(app) if app[APP_SETTINGS_KEY].WEBSERVER_RABBITMQ: diff --git a/services/web/server/src/simcore_service_webserver/licenses/api.py b/services/web/server/src/simcore_service_webserver/licenses/service.py similarity index 100% rename from services/web/server/src/simcore_service_webserver/licenses/api.py rename to services/web/server/src/simcore_service_webserver/licenses/service.py diff --git a/services/web/server/tests/unit/with_dbs/04/licenses/test_licensed_items_checkouts_rest.py b/services/web/server/tests/unit/with_dbs/04/licenses/test_licensed_items_checkouts_rest.py new file mode 100644 index 00000000000..1a6a81e76f4 --- /dev/null +++ b/services/web/server/tests/unit/with_dbs/04/licenses/test_licensed_items_checkouts_rest.py @@ -0,0 +1,85 @@ +# pylint: disable=redefined-outer-name +# pylint: disable=unused-argument +# pylint: disable=unused-variable +# pylint: disable=too-many-arguments +# pylint: disable=too-many-statements +from http import HTTPStatus + +import pytest +from aiohttp.test_utils import TestClient +from models_library.api_schemas_resource_usage_tracker.licensed_items_checkouts import ( + LicensedItemCheckoutGet, + LicensedItemsCheckoutsPage, +) +from models_library.api_schemas_webserver.licensed_items_checkouts import ( + LicensedItemCheckoutRestGet, +) +from pytest_mock.plugin import MockerFixture +from pytest_simcore.helpers.assert_checks import assert_status +from pytest_simcore.helpers.webserver_login import UserInfoDict +from servicelib.aiohttp import status +from simcore_service_webserver.db.models import UserRole + +_LICENSED_ITEM_CHECKOUT_GET = LicensedItemCheckoutGet.model_validate( + LicensedItemCheckoutGet.model_config["json_schema_extra"]["examples"][0] +) + +_LICENSED_ITEM_CHECKOUT_PAGE = LicensedItemsCheckoutsPage( + items=[_LICENSED_ITEM_CHECKOUT_GET], + total=1, +) + + +@pytest.fixture +def mock_get_licensed_items_checkouts_page(mocker: MockerFixture) -> tuple: + return mocker.patch( + "simcore_service_webserver.licenses._licensed_items_checkouts_service.licensed_items_checkouts.get_licensed_items_checkouts_page", + spec=True, + return_value=_LICENSED_ITEM_CHECKOUT_PAGE, + ) + + +@pytest.fixture +def mock_get_licensed_item_checkout(mocker: MockerFixture) -> tuple: + return mocker.patch( + "simcore_service_webserver.licenses._licensed_items_checkouts_service.licensed_items_checkouts.get_licensed_item_checkout", + spec=True, + return_value=_LICENSED_ITEM_CHECKOUT_GET, + ) + + +@pytest.fixture +def mock_get_wallet_by_user(mocker: MockerFixture) -> tuple: + return mocker.patch( + "simcore_service_webserver.licenses._licensed_items_checkouts_service.get_wallet_by_user", + spec=True, + ) + + +@pytest.mark.parametrize("user_role,expected", [(UserRole.USER, status.HTTP_200_OK)]) +async def test_licensed_items_checkouts_handlers( + client: TestClient, + logged_user: UserInfoDict, + expected: HTTPStatus, + mock_get_licensed_items_checkouts_page: MockerFixture, + mock_get_licensed_item_checkout: MockerFixture, + mock_get_wallet_by_user: MockerFixture, +): + assert client.app + + # list + url = client.app.router["list_licensed_item_checkouts_for_wallet"].url_for( + wallet_id="1" + ) + resp = await client.get(f"{url}") + data, _ = await assert_status(resp, status.HTTP_200_OK) + assert len(data) == 1 + assert LicensedItemCheckoutRestGet(**data[0]) + + # get + url = client.app.router["get_licensed_item_checkout"].url_for( + licensed_item_checkout_id=f"{_LICENSED_ITEM_CHECKOUT_PAGE.items[0].licensed_item_checkout_id}" + ) + resp = await client.get(f"{url}") + data, _ = await assert_status(resp, status.HTTP_200_OK) + assert LicensedItemCheckoutRestGet(**data) diff --git a/services/web/server/tests/unit/with_dbs/04/licenses/test_licensed_items_purchases_handlers.py b/services/web/server/tests/unit/with_dbs/04/licenses/test_licensed_items_purchases_rest.py similarity index 93% rename from services/web/server/tests/unit/with_dbs/04/licenses/test_licensed_items_purchases_handlers.py rename to services/web/server/tests/unit/with_dbs/04/licenses/test_licensed_items_purchases_rest.py index ce0fddeca19..ee3656d2c1c 100644 --- a/services/web/server/tests/unit/with_dbs/04/licenses/test_licensed_items_purchases_handlers.py +++ b/services/web/server/tests/unit/with_dbs/04/licenses/test_licensed_items_purchases_rest.py @@ -49,7 +49,7 @@ @pytest.fixture def mock_get_licensed_items_purchases_page(mocker: MockerFixture) -> tuple: return mocker.patch( - "simcore_service_webserver.licenses._licensed_items_purchases_api.licensed_items_purchases.get_licensed_items_purchases_page", + "simcore_service_webserver.licenses._licensed_items_purchases_service.licensed_items_purchases.get_licensed_items_purchases_page", spec=True, return_value=_LICENSED_ITEM_PURCHASE_PAGE, ) @@ -58,7 +58,7 @@ def mock_get_licensed_items_purchases_page(mocker: MockerFixture) -> tuple: @pytest.fixture def mock_get_licensed_item_purchase(mocker: MockerFixture) -> tuple: return mocker.patch( - "simcore_service_webserver.licenses._licensed_items_purchases_api.licensed_items_purchases.get_licensed_item_purchase", + "simcore_service_webserver.licenses._licensed_items_purchases_service.licensed_items_purchases.get_licensed_item_purchase", spec=True, return_value=_LICENSED_ITEM_PURCHASE_GET, ) @@ -67,13 +67,13 @@ def mock_get_licensed_item_purchase(mocker: MockerFixture) -> tuple: @pytest.fixture def mock_get_wallet_by_user(mocker: MockerFixture) -> tuple: return mocker.patch( - "simcore_service_webserver.licenses._licensed_items_purchases_api.get_wallet_by_user", + "simcore_service_webserver.licenses._licensed_items_purchases_service.get_wallet_by_user", spec=True, ) @pytest.mark.parametrize("user_role,expected", [(UserRole.USER, status.HTTP_200_OK)]) -async def test_licensed_items_db_crud( +async def test_licensed_items_purchaches_handlers( client: TestClient, logged_user: UserInfoDict, expected: HTTPStatus, diff --git a/services/web/server/tests/unit/with_dbs/04/licenses/test_licensed_items_db.py b/services/web/server/tests/unit/with_dbs/04/licenses/test_licensed_items_repository.py similarity index 84% rename from services/web/server/tests/unit/with_dbs/04/licenses/test_licensed_items_db.py rename to services/web/server/tests/unit/with_dbs/04/licenses/test_licensed_items_repository.py index 910e1bdf3f4..dfe04e2e0d3 100644 --- a/services/web/server/tests/unit/with_dbs/04/licenses/test_licensed_items_db.py +++ b/services/web/server/tests/unit/with_dbs/04/licenses/test_licensed_items_repository.py @@ -16,7 +16,7 @@ from pytest_simcore.helpers.webserver_login import UserInfoDict from servicelib.aiohttp import status from simcore_service_webserver.db.models import UserRole -from simcore_service_webserver.licenses import _licensed_items_db +from simcore_service_webserver.licenses import _licensed_items_repository from simcore_service_webserver.licenses.errors import LicensedItemNotFoundError from simcore_service_webserver.projects.models import ProjectDict @@ -32,7 +32,7 @@ async def test_licensed_items_db_crud( ): assert client.app - output: tuple[int, list[LicensedItemDB]] = await _licensed_items_db.list_( + output: tuple[int, list[LicensedItemDB]] = await _licensed_items_repository.list_( client.app, product_name=osparc_product_name, offset=0, @@ -41,7 +41,7 @@ async def test_licensed_items_db_crud( ) assert output[0] == 0 - licensed_item_db = await _licensed_items_db.create( + licensed_item_db = await _licensed_items_repository.create( client.app, product_name=osparc_product_name, name="Model A", @@ -50,7 +50,7 @@ async def test_licensed_items_db_crud( ) _licensed_item_id = licensed_item_db.licensed_item_id - output: tuple[int, list[LicensedItemDB]] = await _licensed_items_db.list_( + output: tuple[int, list[LicensedItemDB]] = await _licensed_items_repository.list_( client.app, product_name=osparc_product_name, offset=0, @@ -59,35 +59,35 @@ async def test_licensed_items_db_crud( ) assert output[0] == 1 - licensed_item_db = await _licensed_items_db.get( + licensed_item_db = await _licensed_items_repository.get( client.app, licensed_item_id=_licensed_item_id, product_name=osparc_product_name, ) assert licensed_item_db.name == "Model A" - await _licensed_items_db.update( + await _licensed_items_repository.update( client.app, licensed_item_id=_licensed_item_id, product_name=osparc_product_name, updates=LicensedItemUpdateDB(name="Model B"), ) - licensed_item_db = await _licensed_items_db.get( + licensed_item_db = await _licensed_items_repository.get( client.app, licensed_item_id=_licensed_item_id, product_name=osparc_product_name, ) assert licensed_item_db.name == "Model B" - licensed_item_db = await _licensed_items_db.delete( + licensed_item_db = await _licensed_items_repository.delete( client.app, licensed_item_id=_licensed_item_id, product_name=osparc_product_name, ) with pytest.raises(LicensedItemNotFoundError): - await _licensed_items_db.get( + await _licensed_items_repository.get( client.app, licensed_item_id=_licensed_item_id, product_name=osparc_product_name, diff --git a/services/web/server/tests/unit/with_dbs/04/licenses/test_licensed_items_handlers.py b/services/web/server/tests/unit/with_dbs/04/licenses/test_licensed_items_rest.py similarity index 88% rename from services/web/server/tests/unit/with_dbs/04/licenses/test_licensed_items_handlers.py rename to services/web/server/tests/unit/with_dbs/04/licenses/test_licensed_items_rest.py index b1fee67dafa..67c36f2581b 100644 --- a/services/web/server/tests/unit/with_dbs/04/licenses/test_licensed_items_handlers.py +++ b/services/web/server/tests/unit/with_dbs/04/licenses/test_licensed_items_rest.py @@ -18,7 +18,7 @@ from pytest_simcore.helpers.webserver_login import UserInfoDict from servicelib.aiohttp import status from simcore_service_webserver.db.models import UserRole -from simcore_service_webserver.licenses import _licensed_items_db +from simcore_service_webserver.licenses import _licensed_items_repository from simcore_service_webserver.projects.models import ProjectDict @@ -39,7 +39,7 @@ async def test_licensed_items_listing( data, _ = await assert_status(resp, status.HTTP_200_OK) assert data == [] - licensed_item_db = await _licensed_items_db.create( + licensed_item_db = await _licensed_items_repository.create( client.app, product_name=osparc_product_name, name="Model A", @@ -67,7 +67,7 @@ async def test_licensed_items_listing( @pytest.fixture def mock_licensed_items_purchase_functions(mocker: MockerFixture) -> tuple: mock_wallet_credits = mocker.patch( - "simcore_service_webserver.licenses._licensed_items_api.get_wallet_with_available_credits_by_user_and_wallet", + "simcore_service_webserver.licenses._licensed_items_service.get_wallet_with_available_credits_by_user_and_wallet", spec=True, return_value=WalletGetWithAvailableCredits.model_validate( WalletGetWithAvailableCredits.model_config["json_schema_extra"]["examples"][ @@ -76,14 +76,14 @@ def mock_licensed_items_purchase_functions(mocker: MockerFixture) -> tuple: ), ) mock_get_pricing_unit = mocker.patch( - "simcore_service_webserver.licenses._licensed_items_api.get_pricing_plan_unit", + "simcore_service_webserver.licenses._licensed_items_service.get_pricing_plan_unit", spec=True, return_value=PricingUnitGet.model_validate( PricingUnitGet.model_config["json_schema_extra"]["examples"][0] ), ) mock_create_licensed_item_purchase = mocker.patch( - "simcore_service_webserver.licenses._licensed_items_api.licensed_items_purchases.create_licensed_item_purchase", + "simcore_service_webserver.licenses._licensed_items_service.licensed_items_purchases.create_licensed_item_purchase", spec=True, ) @@ -106,7 +106,7 @@ async def test_licensed_items_purchase( ): assert client.app - licensed_item_db = await _licensed_items_db.create( + licensed_item_db = await _licensed_items_repository.create( client.app, product_name=osparc_product_name, name="Model A", diff --git a/services/web/server/tests/unit/with_dbs/04/licenses/test_licenses_rpc.py b/services/web/server/tests/unit/with_dbs/04/licenses/test_licenses_rpc.py index 6888711b2da..836a7fe05e6 100644 --- a/services/web/server/tests/unit/with_dbs/04/licenses/test_licenses_rpc.py +++ b/services/web/server/tests/unit/with_dbs/04/licenses/test_licenses_rpc.py @@ -26,7 +26,7 @@ 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 +from simcore_service_webserver.licenses import _licensed_items_repository pytest_simcore_core_services_selection = [ "rabbit", @@ -73,7 +73,7 @@ async def rpc_client( @pytest.fixture def mock_get_wallet_by_user(mocker: MockerFixture) -> tuple: return mocker.patch( - "simcore_service_webserver.licenses._licensed_checkouts_api.get_wallet_by_user", + "simcore_service_webserver.licenses._licensed_items_checkouts_service.get_wallet_by_user", spec=True, ) @@ -86,7 +86,7 @@ def mock_get_wallet_by_user(mocker: MockerFixture) -> tuple: @pytest.fixture def mock_checkout_licensed_item(mocker: MockerFixture) -> tuple: return mocker.patch( - "simcore_service_webserver.licenses._licensed_checkouts_api.licensed_items_checkouts.checkout_licensed_item", + "simcore_service_webserver.licenses._licensed_items_checkouts_service.licensed_items_checkouts.checkout_licensed_item", spec=True, return_value=_LICENSED_ITEM_CHECKOUT_GET, ) @@ -95,7 +95,7 @@ def mock_checkout_licensed_item(mocker: MockerFixture) -> tuple: @pytest.fixture def mock_get_licensed_item_checkout(mocker: MockerFixture) -> tuple: return mocker.patch( - "simcore_service_webserver.licenses._licensed_checkouts_api.licensed_items_checkouts.get_licensed_item_checkout", + "simcore_service_webserver.licenses._licensed_items_checkouts_service.licensed_items_checkouts.get_licensed_item_checkout", spec=True, return_value=_LICENSED_ITEM_CHECKOUT_GET, ) @@ -104,7 +104,7 @@ def mock_get_licensed_item_checkout(mocker: MockerFixture) -> tuple: @pytest.fixture def mock_release_licensed_item(mocker: MockerFixture) -> tuple: return mocker.patch( - "simcore_service_webserver.licenses._licensed_checkouts_api.licensed_items_checkouts.release_licensed_item", + "simcore_service_webserver.licenses._licensed_items_checkouts_service.licensed_items_checkouts.release_licensed_item", spec=True, return_value=_LICENSED_ITEM_CHECKOUT_GET, ) @@ -132,7 +132,7 @@ async def test_license_checkout_workflow( assert len(result.items) == 0 assert result.total == 0 - license_item_db = await _licensed_items_db.create( + license_item_db = await _licensed_items_repository.create( client.app, product_name=osparc_product_name, name="Model A",