Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 4 additions & 3 deletions api/specs/web-server/_licensed_items.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@

from _common import as_query
from fastapi import APIRouter, Depends, status
from models_library.api_schemas_webserver.licensed_items import LicensedItemGet
from models_library.api_schemas_webserver.licensed_items import LicensedItemRestGet
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._common.exceptions_handlers import (
_TO_HTTP_ERROR_MAP,
Expand All @@ -37,7 +38,7 @@

@router.get(
"/catalog/licensed-items",
response_model=Envelope[list[LicensedItemGet]],
response_model=Page[LicensedItemRestGet],
)
async def list_licensed_items(
_query: Annotated[as_query(LicensedItemsListQueryParams), Depends()],
Expand All @@ -47,7 +48,7 @@ async def list_licensed_items(

@router.get(
"/catalog/licensed-items/{licensed_item_id}",
response_model=Envelope[LicensedItemGet],
response_model=Envelope[LicensedItemRestGet],
)
async def get_licensed_item(
_path: Annotated[LicensedItemsPathParams, Depends()],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,62 @@
from datetime import datetime
from typing import NamedTuple, Self
from typing import Any, NamedTuple, cast

from common_library.dict_tools import remap_keys
from models_library.licensed_items import (
LicensedItemDB,
VIP_DETAILS_EXAMPLE,
LicensedItemID,
LicensedResourceType,
)
from models_library.resource_tracker import PricingPlanId
from pydantic import ConfigDict, PositiveInt
from models_library.utils.common_validators import to_camel_recursive
from pydantic import AfterValidator, BaseModel, ConfigDict, PositiveInt
from pydantic.config import JsonDict
from typing_extensions import Annotated

from ._base import OutputSchema

# RPC

class LicensedItemGet(OutputSchema):
licensed_item_id: LicensedItemID

name: str
license_key: str | None
class LicensedItemRpcGet(BaseModel):
licensed_item_id: LicensedItemID
display_name: str
licensed_resource_type: LicensedResourceType
licensed_resource_data: dict[str, Any]
pricing_plan_id: PricingPlanId
created_at: datetime
modified_at: datetime
model_config = ConfigDict(
json_schema_extra={
"examples": [
{
"licensed_item_id": "0362b88b-91f8-4b41-867c-35544ad1f7a1",
"display_name": "best-model",
"licensed_resource_type": f"{LicensedResourceType.VIP_MODEL}",
"licensed_resource_data": cast(JsonDict, VIP_DETAILS_EXAMPLE),
"pricing_plan_id": "15",
"created_at": "2024-12-12 09:59:26.422140",
"modified_at": "2024-12-12 09:59:26.422140",
}
]
},
)


class LicensedItemRpcGetPage(NamedTuple):
items: list[LicensedItemRpcGet]
total: PositiveInt


# Rest


class LicensedItemRestGet(OutputSchema):
licensed_item_id: LicensedItemID
display_name: str
licensed_resource_type: LicensedResourceType
licensed_resource_data: Annotated[
dict[str, Any], AfterValidator(to_camel_recursive)
]
pricing_plan_id: PricingPlanId

created_at: datetime
Expand All @@ -30,9 +67,9 @@ class LicensedItemGet(OutputSchema):
"examples": [
{
"licensed_item_id": "0362b88b-91f8-4b41-867c-35544ad1f7a1",
"name": "best-model",
"license_key": "license-specific-key",
"display_name": "best-model",
"licensed_resource_type": f"{LicensedResourceType.VIP_MODEL}",
"licensed_resource_data": cast(JsonDict, VIP_DETAILS_EXAMPLE),
"pricing_plan_id": "15",
"created_at": "2024-12-12 09:59:26.422140",
"modified_at": "2024-12-12 09:59:26.422140",
Expand All @@ -41,30 +78,7 @@ class LicensedItemGet(OutputSchema):
}
)

@classmethod
def from_domain_model(cls, licensed_item_db: LicensedItemDB) -> Self:
return cls.model_validate(
remap_keys(
licensed_item_db.model_dump(
include={
"licensed_item_id",
"licensed_resource_name",
"licensed_resource_type",
"license_key",
"pricing_plan_id",
"created",
"modified",
}
),
{
"licensed_resource_name": "name",
"created": "created_at",
"modified": "modified_at",
},
)
)


class LicensedItemGetPage(NamedTuple):
items: list[LicensedItemGet]
class LicensedItemRestGetPage(NamedTuple):
items: list[LicensedItemRestGet]
total: PositiveInt
31 changes: 29 additions & 2 deletions packages/models-library/src/models_library/licensed_items.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,41 @@ class LicensedResourceType(StrAutoEnum):
VIP_MODEL = auto()


VIP_FEAUTES_EXAMPLE = {
"name": "Duke",
"version": "V2.0",
"sex": "Male",
"age": "34 years",
"weight": "70.2 Kg",
"height": "1.77 m",
"data": "2015-03-01",
"ethnicity": "Caucasian",
"functionality": "Static",
"additional_field": "allowed",
}

VIP_DETAILS_EXAMPLE = {
"id": 1,
"description": "custom description",
"thumbnail": "custom description",
"features": VIP_FEAUTES_EXAMPLE,
"doi": "custom value",
"license_key": "custom value",
"license_version": "custom value",
"protection": "custom value",
"available_from_url": "custom value",
"additional_field": "allowed",
}


#
# DB
#


class LicensedItemDB(BaseModel):
licensed_item_id: LicensedItemID
license_key: str | None
display_name: str

licensed_resource_name: str
licensed_resource_type: LicensedResourceType
Expand All @@ -41,7 +68,7 @@ class LicensedItemDB(BaseModel):


class LicensedItemUpdateDB(BaseModel):
display_name: str | None = None
licensed_resource_name: str | None = None
pricing_plan_id: PricingPlanId | None = None

trash: bool | None = None
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class MyModel(BaseModel):
from common_library.json_serialization import json_loads
from orjson import JSONDecodeError
from pydantic import BaseModel
from pydantic.alias_generators import to_camel


def empty_str_to_none_pre_validator(value: Any):
Expand Down Expand Up @@ -120,3 +121,18 @@ def _validator(cls: type[BaseModel], values):
return values

return _validator


def to_camel_recursive(data: dict[str, Any]) -> dict[str, Any]:
"""Recursively convert dictionary keys to camelCase"""
if not isinstance(data, dict):
return data # Return as-is if it's not a dictionary

new_dict = {}
for key, value in data.items():
new_key = to_camel(key) # Convert key to camelCase
if isinstance(value, dict):
new_dict[new_key] = to_camel_recursive(value) # Recursive call for dicts
else:
new_dict[new_key] = value
return new_dict
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""modify licensed items DB

Revision ID: 7d1c6425a51d
Revises: 4f31760a63ba
Create Date: 2025-01-30 17:32:31.969343+00:00

"""
import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision = "7d1c6425a51d"
down_revision = "4f31760a63ba"
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column(
"licensed_items", sa.Column("display_name", sa.String(), nullable=False)
)
op.drop_column("licensed_items", "license_key")
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column(
"licensed_items",
sa.Column("license_key", sa.VARCHAR(), autoincrement=False, nullable=True),
)
op.drop_column("licensed_items", "display_name")
# ### end Alembic commands ###
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ class LicensedResourceType(str, enum.Enum):
primary_key=True,
server_default=sa.text("gen_random_uuid()"),
),
sa.Column(
"display_name",
sa.String,
nullable=False,
doc="Display name for front-end",
),
sa.Column(
"licensed_resource_name",
sa.String,
Expand Down Expand Up @@ -70,13 +76,6 @@ class LicensedResourceType(str, enum.Enum):
nullable=True,
doc="Product name identifier. If None, then the item is not exposed",
),
sa.Column(
"license_key",
sa.String,
nullable=True,
doc="Purpose: Acts as a mapping key to the internal license server."
"Usage: The Sim4Life base applications use this key to check out a seat from the internal license server.",
),
column_created_datetime(timezone=True),
column_modified_datetime(timezone=True),
column_trashed_datetime("licensed_item"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
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.api_schemas_webserver.licensed_items import LicensedItemRpcGetPage
from models_library.api_schemas_webserver.licensed_items_checkouts import (
LicensedItemCheckoutRpcGet,
)
Expand All @@ -31,15 +28,15 @@ async def get_licensed_items(
product_name: str,
offset: int = 0,
limit: int = 20,
) -> LicensedItemGetPage:
result: LicensedItemGetPage = await rabbitmq_rpc_client.request(
) -> LicensedItemRpcGetPage:
result: LicensedItemRpcGetPage = 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) # nosec
assert isinstance(result, LicensedItemRpcGetPage) # nosec
return result


Expand All @@ -52,8 +49,8 @@ async def get_available_licensed_items_for_wallet(
user_id: UserID,
offset: int = 0,
limit: int = 20,
) -> LicensedItemGetPage:
result: LicensedItemGet = await rabbitmq_rpc_client.request(
) -> LicensedItemRpcGetPage:
result: LicensedItemRpcGetPage = await rabbitmq_rpc_client.request(
WEBSERVER_RPC_NAMESPACE,
TypeAdapter(RPCMethodName).validate_python(
"get_available_licensed_items_for_wallet"
Expand All @@ -64,7 +61,7 @@ async def get_available_licensed_items_for_wallet(
offset=offset,
limit=limit,
)
assert isinstance(result, LicensedItemGetPage) # nosec
assert isinstance(result, LicensedItemRpcGetPage) # nosec
return result


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

from datetime import datetime
from decimal import Decimal
from typing import Annotated
from typing import Annotated, Any

from models_library.api_schemas_api_server.pricing_plans import (
ServicePricingPlanGet as _ServicePricingPlanGet,
)
from models_library.api_schemas_webserver.licensed_items import (
LicensedItemGet as _LicensedItemGet,
LicensedItemRpcGet as _LicensedItemGet,
)
from models_library.api_schemas_webserver.licensed_items_checkouts import (
LicensedItemCheckoutRpcGet as _LicensedItemCheckoutRpcGet,
Expand Down Expand Up @@ -137,9 +137,9 @@ class ServicePricingPlanGetLegacy(BaseModel):

class LicensedItemGet(BaseModel):
licensed_item_id: LicensedItemID
name: Annotated[str, Field(alias="display_name")]
license_key: str | None
display_name: str
licensed_resource_type: LicensedResourceType
licensed_resource_data: dict[str, Any]
pricing_plan_id: PricingPlanId
created_at: datetime
modified_at: datetime
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from fastapi import FastAPI
from fastapi_pagination import create_page
from models_library.api_schemas_webserver.licensed_items import LicensedItemGetPage
from models_library.api_schemas_webserver.licensed_items import LicensedItemRpcGetPage
from models_library.licensed_items import LicensedItemID
from models_library.resource_tracker_licensed_items_checkouts import (
LicensedItemCheckoutID,
Expand Down Expand Up @@ -52,15 +52,15 @@


def _create_licensed_items_get_page(
*, licensed_items_page: LicensedItemGetPage, page_params: PaginationParams
*, licensed_items_page: LicensedItemRpcGetPage, page_params: PaginationParams
) -> Page[LicensedItemGet]:
page = create_page(
[
LicensedItemGet(
licensed_item_id=elm.licensed_item_id,
name=elm.name,
license_key=elm.license_key,
display_name=elm.display_name,
licensed_resource_type=elm.licensed_resource_type,
licensed_resource_data=elm.licensed_resource_data,
pricing_plan_id=elm.pricing_plan_id,
created_at=elm.created_at,
modified_at=elm.modified_at,
Expand Down
Loading
Loading