Skip to content

Commit 7dd1e0a

Browse files
πŸ› add product_name to licensed item/resource mapping table (πŸ—ƒοΈ) (#7272)
1 parent 731dd9a commit 7dd1e0a

File tree

6 files changed

+137
-25
lines changed

6 files changed

+137
-25
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
"""add product name to licensed item to resource table
2+
3+
Revision ID: 381336fa8001
4+
Revises: d84edab53761
5+
Create Date: 2025-02-25 13:37:19.861701+00:00
6+
7+
"""
8+
9+
import sqlalchemy as sa
10+
from alembic import op
11+
12+
# revision identifiers, used by Alembic.
13+
revision = "381336fa8001"
14+
down_revision = "d84edab53761"
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade():
20+
op.add_column(
21+
"licensed_item_to_resource",
22+
sa.Column("product_name", sa.String(), nullable=True),
23+
)
24+
25+
### Added Manually -->
26+
op.execute(
27+
"""
28+
UPDATE licensed_item_to_resource
29+
SET product_name = 's4l'
30+
WHERE product_name IS NULL
31+
"""
32+
)
33+
op.alter_column("licensed_item_to_resource", "product_name", nullable=False)
34+
### <-- Added Manually
35+
36+
op.drop_constraint(
37+
"uq_licensed_item_to_resource_resource_id",
38+
"licensed_item_to_resource",
39+
type_="unique",
40+
)
41+
op.create_unique_constraint(
42+
"uq_licensed_item_to_resource_resource_id",
43+
"licensed_item_to_resource",
44+
["product_name", "licensed_resource_id"],
45+
)
46+
op.create_foreign_key(
47+
"fk_licensed_item_to_resource_product_name",
48+
"licensed_item_to_resource",
49+
"products",
50+
["product_name"],
51+
["name"],
52+
onupdate="CASCADE",
53+
ondelete="CASCADE",
54+
)
55+
56+
57+
def downgrade():
58+
# ### commands auto generated by Alembic - please adjust! ###
59+
op.drop_constraint(
60+
"fk_licensed_item_to_resource_product_name",
61+
"licensed_item_to_resource",
62+
type_="foreignkey",
63+
)
64+
op.drop_constraint(
65+
"uq_licensed_item_to_resource_resource_id",
66+
"licensed_item_to_resource",
67+
type_="unique",
68+
)
69+
op.create_unique_constraint(
70+
"uq_licensed_item_to_resource_resource_id",
71+
"licensed_item_to_resource",
72+
["licensed_resource_id"],
73+
)
74+
op.drop_column("licensed_item_to_resource", "product_name")
75+
# ### end Alembic commands ###

β€Žpackages/postgres-database/src/simcore_postgres_database/models/licensed_item_to_resource.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,31 @@
2929
),
3030
nullable=False,
3131
),
32+
sa.Column(
33+
"product_name",
34+
sa.String,
35+
sa.ForeignKey(
36+
"products.name",
37+
onupdate=RefActions.CASCADE,
38+
ondelete=RefActions.CASCADE,
39+
name="fk_licensed_item_to_resource_product_name",
40+
),
41+
nullable=False,
42+
),
3243
column_created_datetime(timezone=True),
3344
column_modified_datetime(timezone=True),
3445
#########
46+
sa.PrimaryKeyConstraint(
47+
"licensed_item_id",
48+
"licensed_resource_id",
49+
name="pk_licensed_item_to_resource_item_and_resource_id",
50+
),
3551
# NOTE: Currently, there is a constraint that a resource item ID cannot be in multiple licensed items.
3652
# The reason is that the license key and license version coming from the internal license server are part of the licensed resource domain.
3753
# Sim4Life performs a mapping on their side, where the license key and version are mapped to a licensed item.
3854
# If this constraint is broken, the mapping logic in Sim4Life might break.
3955
sa.UniqueConstraint(
56+
"product_name",
4057
"licensed_resource_id",
4158
name="uq_licensed_item_to_resource_resource_id",
4259
),

β€Žservices/web/server/src/simcore_service_webserver/licenses/_licensed_items_repository.py

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -212,33 +212,39 @@ async def delete(
212212
### LICENSED ITEMS DOMAIN
213213

214214

215-
# Step 1: Create an ordered subquery
216-
_ordered_subquery = (
217-
select(
218-
licensed_item_to_resource.c.licensed_item_id,
219-
licensed_resources.c.licensed_resource_data,
220-
licensed_resources.c.priority,
221-
licensed_resources.c.licensed_resource_id,
222-
)
223-
.select_from(
224-
licensed_item_to_resource.join(
225-
licensed_resources,
226-
licensed_resources.c.licensed_resource_id
227-
== licensed_item_to_resource.c.licensed_resource_id,
215+
def _create_licensed_resource_subquery(product_name: ProductName):
216+
# Step 1: Create an ordered subquery
217+
_ordered_subquery = (
218+
select(
219+
licensed_item_to_resource.c.licensed_item_id,
220+
licensed_resources.c.licensed_resource_data,
221+
licensed_resources.c.priority,
222+
licensed_resources.c.licensed_resource_id,
228223
)
229-
)
230-
.order_by(licensed_resources.c.priority, licensed_resources.c.licensed_resource_id)
231-
).subquery("ordered_subquery")
224+
.select_from(
225+
licensed_item_to_resource.join(
226+
licensed_resources,
227+
licensed_resources.c.licensed_resource_id
228+
== licensed_item_to_resource.c.licensed_resource_id,
229+
)
230+
)
231+
.where(licensed_item_to_resource.c.product_name == product_name)
232+
.order_by(
233+
licensed_resources.c.priority, licensed_resources.c.licensed_resource_id
234+
)
235+
).subquery("ordered_subquery")
232236

233-
# Step 2: Aggregate the ordered subquery results
234-
_licensed_resource_subquery = (
235-
select(
236-
_ordered_subquery.c.licensed_item_id,
237-
func.array_agg(_ordered_subquery.c.licensed_resource_data).label(
238-
"licensed_resources"
239-
),
240-
).group_by(_ordered_subquery.c.licensed_item_id)
241-
).subquery("licensed_resource_subquery")
237+
# Step 2: Aggregate the ordered subquery results
238+
_licensed_resource_subquery = (
239+
select(
240+
_ordered_subquery.c.licensed_item_id,
241+
func.array_agg(_ordered_subquery.c.licensed_resource_data).label(
242+
"licensed_resources"
243+
),
244+
).group_by(_ordered_subquery.c.licensed_item_id)
245+
).subquery("licensed_resource_subquery")
246+
247+
return _licensed_resource_subquery # noqa: RET504
242248

243249

244250
async def get_licensed_item_by_key_version(
@@ -250,6 +256,10 @@ async def get_licensed_item_by_key_version(
250256
product_name: ProductName,
251257
) -> LicensedItem:
252258

259+
_licensed_resource_subquery = _create_licensed_resource_subquery(
260+
product_name=product_name
261+
)
262+
253263
select_query = (
254264
select(
255265
licensed_items.c.licensed_item_id,
@@ -298,6 +308,10 @@ async def list_licensed_items(
298308
include_hidden_items_on_market: bool = False,
299309
) -> tuple[int, list[LicensedItem]]:
300310

311+
_licensed_resource_subquery = _create_licensed_resource_subquery(
312+
product_name=product_name
313+
)
314+
301315
base_query = (
302316
select(
303317
licensed_items.c.licensed_item_id,

β€Žservices/web/server/tests/unit/with_dbs/04/licenses/test_licensed_items_repository.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,14 +205,17 @@ async def test_licensed_items_domain_listing(
205205
{
206206
"licensed_item_id": got_duke1.licensed_item_id,
207207
"licensed_resource_id": got_licensed_resource_duke1.licensed_resource_id,
208+
"product_name": osparc_product_name,
208209
},
209210
{
210211
"licensed_item_id": got_duke1.licensed_item_id,
211212
"licensed_resource_id": got_licensed_resource_duke1_different_id.licensed_resource_id,
213+
"product_name": osparc_product_name,
212214
},
213215
{
214216
"licensed_item_id": got_duke2.licensed_item_id,
215217
"licensed_resource_id": got_licensed_resource_duke2.licensed_resource_id,
218+
"product_name": osparc_product_name,
216219
},
217220
],
218221
)

β€Žservices/web/server/tests/unit/with_dbs/04/licenses/test_licensed_items_rest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ async def test_licensed_items_listing(
8686
licensed_item_to_resource.insert().values(
8787
licensed_item_id=_licensed_item_id,
8888
licensed_resource_id=got_licensed_resource_duke.licensed_resource_id,
89+
product_name=osparc_product_name,
8990
)
9091
)
9192

@@ -215,6 +216,7 @@ async def test_licensed_items_purchase(
215216
licensed_item_to_resource.insert().values(
216217
licensed_item_id=_licensed_item_id,
217218
licensed_resource_id=got_licensed_resource_duke.licensed_resource_id,
219+
product_name=osparc_product_name,
218220
)
219221
)
220222

β€Žservices/web/server/tests/unit/with_dbs/04/licenses/test_licenses_rpc.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ async def test_license_checkout_workflow(
172172
licensed_item_to_resource.insert().values(
173173
licensed_item_id=_licensed_item_id,
174174
licensed_resource_id=got_licensed_resource_duke.licensed_resource_id,
175+
product_name=osparc_product_name,
175176
)
176177
)
177178

0 commit comments

Comments
Β (0)