Skip to content

Commit b837ee0

Browse files
authored
✨ Updates licensed_items table 🗃️ (ITISFoundation#7144)
1 parent b50e7e8 commit b837ee0

File tree

14 files changed

+336
-105
lines changed

14 files changed

+336
-105
lines changed

packages/models-library/src/models_library/api_schemas_webserver/licensed_items.py

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
from datetime import datetime
2-
from typing import NamedTuple
2+
from typing import NamedTuple, Self
33

4-
from models_library.licensed_items import LicensedItemID, LicensedResourceType
4+
from common_library.dict_tools import remap_keys
5+
from models_library.licensed_items import (
6+
LicensedItemDB,
7+
LicensedItemID,
8+
LicensedResourceType,
9+
)
510
from models_library.resource_tracker import PricingPlanId
611
from pydantic import ConfigDict, PositiveInt
712

@@ -10,12 +15,16 @@
1015

1116
class LicensedItemGet(OutputSchema):
1217
licensed_item_id: LicensedItemID
18+
1319
name: str
1420
license_key: str | None
1521
licensed_resource_type: LicensedResourceType
22+
1623
pricing_plan_id: PricingPlanId
24+
1725
created_at: datetime
1826
modified_at: datetime
27+
1928
model_config = ConfigDict(
2029
json_schema_extra={
2130
"examples": [
@@ -32,6 +41,29 @@ class LicensedItemGet(OutputSchema):
3241
}
3342
)
3443

44+
@classmethod
45+
def from_domain_model(cls, licensed_item_db: LicensedItemDB) -> Self:
46+
return cls.model_validate(
47+
remap_keys(
48+
licensed_item_db.model_dump(
49+
include={
50+
"licensed_item_id",
51+
"licensed_resource_name",
52+
"licensed_resource_type",
53+
"license_key",
54+
"pricing_plan_id",
55+
"created",
56+
"modified",
57+
}
58+
),
59+
{
60+
"licensed_resource_name": "name",
61+
"created": "created_at",
62+
"modified": "modified_at",
63+
},
64+
)
65+
)
66+
3567

3668
class LicensedItemGetPage(NamedTuple):
3769
items: list[LicensedItemGet]
Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
from datetime import datetime
22
from enum import auto
3-
from typing import TypeAlias
3+
from typing import Any, TypeAlias
44
from uuid import UUID
55

6-
from pydantic import BaseModel, ConfigDict, Field
6+
from pydantic import BaseModel, ConfigDict
77

88
from .products import ProductName
99
from .resource_tracker import PricingPlanId
@@ -23,23 +23,25 @@ class LicensedResourceType(StrAutoEnum):
2323

2424
class LicensedItemDB(BaseModel):
2525
licensed_item_id: LicensedItemID
26-
name: str
2726
license_key: str | None
27+
28+
licensed_resource_name: str
2829
licensed_resource_type: LicensedResourceType
29-
pricing_plan_id: PricingPlanId
30-
product_name: ProductName
31-
created: datetime = Field(
32-
...,
33-
description="Timestamp on creation",
34-
)
35-
modified: datetime = Field(
36-
...,
37-
description="Timestamp of last modification",
38-
)
39-
# ----
30+
licensed_resource_data: dict[str, Any] | None
31+
32+
pricing_plan_id: PricingPlanId | None
33+
product_name: ProductName | None
34+
35+
# states
36+
created: datetime
37+
modified: datetime
38+
trashed: datetime | None
39+
4040
model_config = ConfigDict(from_attributes=True)
4141

4242

4343
class LicensedItemUpdateDB(BaseModel):
44-
name: str | None = None
44+
licensed_resource_name: str | None = None
4545
pricing_plan_id: PricingPlanId | None = None
46+
47+
trash: bool | None = None
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
"""add data to licensed_items
2+
3+
Revision ID: 4f31760a63ba
4+
Revises: 1bc517536e0a
5+
Create Date: 2025-01-29 16:51:16.453069+00:00
6+
7+
"""
8+
import sqlalchemy as sa
9+
from alembic import op
10+
from sqlalchemy.dialects import postgresql
11+
12+
# revision identifiers, used by Alembic.
13+
revision = "4f31760a63ba"
14+
down_revision = "1bc517536e0a"
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade():
20+
21+
with op.batch_alter_table("licensed_items") as batch_op:
22+
batch_op.alter_column(
23+
"name",
24+
new_column_name="licensed_resource_name",
25+
existing_type=sa.String(),
26+
nullable=False,
27+
)
28+
batch_op.alter_column(
29+
"pricing_plan_id",
30+
existing_type=sa.Integer(),
31+
nullable=True,
32+
)
33+
batch_op.alter_column(
34+
"product_name",
35+
existing_type=sa.String(),
36+
nullable=True,
37+
)
38+
39+
# ### commands auto generated by Alembic - please adjust! ###
40+
op.add_column(
41+
"licensed_items",
42+
sa.Column(
43+
"licensed_resource_data",
44+
postgresql.JSONB(astext_type=sa.Text()),
45+
nullable=True,
46+
),
47+
)
48+
op.add_column(
49+
"licensed_items",
50+
sa.Column(
51+
"trashed",
52+
sa.DateTime(timezone=True),
53+
nullable=True,
54+
comment="The date and time when the licensed_item was marked as trashed. Null if the licensed_item has not been trashed [default].",
55+
),
56+
)
57+
# ### end Alembic commands ###
58+
59+
60+
def downgrade():
61+
# ### commands auto generated by Alembic - please adjust! ###
62+
op.drop_column("licensed_items", "trashed")
63+
op.drop_column("licensed_items", "licensed_resource_data")
64+
# ### end Alembic commands ###
65+
66+
# Delete rows with null values in pricing_plan_id and product_name
67+
op.execute(
68+
sa.DDL(
69+
"""
70+
DELETE FROM licensed_items
71+
WHERE pricing_plan_id IS NULL OR product_name IS NULL;
72+
"""
73+
)
74+
)
75+
print(
76+
"Warning: Rows with null values in pricing_plan_id or product_name have been deleted."
77+
)
78+
79+
with op.batch_alter_table("licensed_items") as batch_op:
80+
81+
batch_op.alter_column(
82+
"product_name",
83+
existing_type=sa.String(),
84+
nullable=False,
85+
)
86+
batch_op.alter_column(
87+
"pricing_plan_id",
88+
existing_type=sa.Integer(),
89+
nullable=False,
90+
)
91+
batch_op.alter_column(
92+
"licensed_resource_name",
93+
new_column_name="name",
94+
existing_type=sa.String(),
95+
nullable=False,
96+
)

packages/postgres-database/src/simcore_postgres_database/models/licensed_items.py

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@
44
import enum
55

66
import sqlalchemy as sa
7-
from sqlalchemy.dialects.postgresql import UUID
7+
from sqlalchemy.dialects import postgresql
88

9-
from ._common import RefActions, column_created_datetime, column_modified_datetime
9+
from ._common import (
10+
RefActions,
11+
column_created_datetime,
12+
column_modified_datetime,
13+
column_trashed_datetime,
14+
)
1015
from .base import metadata
1116

1217

@@ -19,21 +24,28 @@ class LicensedResourceType(str, enum.Enum):
1924
metadata,
2025
sa.Column(
2126
"licensed_item_id",
22-
UUID(as_uuid=True),
27+
postgresql.UUID(as_uuid=True),
2328
nullable=False,
2429
primary_key=True,
2530
server_default=sa.text("gen_random_uuid()"),
2631
),
2732
sa.Column(
28-
"name",
33+
"licensed_resource_name",
2934
sa.String,
3035
nullable=False,
36+
doc="Resource name identifier",
3137
),
3238
sa.Column(
3339
"licensed_resource_type",
3440
sa.Enum(LicensedResourceType),
3541
nullable=False,
36-
doc="Item type, ex. VIP_MODEL",
42+
doc="Resource type, ex. VIP_MODEL",
43+
),
44+
sa.Column(
45+
"licensed_resource_data",
46+
postgresql.JSONB,
47+
nullable=True,
48+
doc="Resource metadata. Used for read-only purposes",
3749
),
3850
sa.Column(
3951
"pricing_plan_id",
@@ -44,7 +56,7 @@ class LicensedResourceType(str, enum.Enum):
4456
onupdate=RefActions.CASCADE,
4557
ondelete=RefActions.RESTRICT,
4658
),
47-
nullable=False,
59+
nullable=True,
4860
),
4961
sa.Column(
5062
"product_name",
@@ -55,15 +67,17 @@ class LicensedResourceType(str, enum.Enum):
5567
ondelete=RefActions.CASCADE,
5668
name="fk_resource_tracker_license_packages_product_name",
5769
),
58-
nullable=False,
59-
doc="Product name",
70+
nullable=True,
71+
doc="Product name identifier. If None, then the item is not exposed",
6072
),
6173
sa.Column(
6274
"license_key",
6375
sa.String,
6476
nullable=True,
65-
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.",
77+
doc="Purpose: Acts as a mapping key to the internal license server."
78+
"Usage: The Sim4Life base applications use this key to check out a seat from the internal license server.",
6679
),
6780
column_created_datetime(timezone=True),
6881
column_modified_datetime(timezone=True),
82+
column_trashed_datetime("licensed_item"),
6983
)

services/web/server/src/simcore_service_webserver/application_settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ def _enable_only_if_dev_features_allowed(cls, v, info: ValidationInfo):
411411

412412
return (
413413
None
414-
if info.field_name and is_nullable(cls.model_fields[info.field_name])
414+
if info.field_name and is_nullable(dict(cls.model_fields)[info.field_name])
415415
else False
416416
)
417417

services/web/server/src/simcore_service_webserver/application_settings_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ def convert_to_environ_vars( # noqa: C901, PLR0915, PLR0912
202202
def _set_if_disabled(field_name, section):
203203
# Assumes that by default is enabled
204204
enabled = section.get("enabled", True)
205-
field = ApplicationSettings.model_fields[field_name]
205+
field = dict(ApplicationSettings.model_fields)[field_name]
206206
if not enabled:
207207
envs[field_name] = "null" if is_nullable(field) else "0"
208208
elif get_type(field) == bool:

0 commit comments

Comments
 (0)