Skip to content

Commit 684e28f

Browse files
committed
registration
1 parent 326de71 commit 684e28f

File tree

3 files changed

+143
-60
lines changed

3 files changed

+143
-60
lines changed

services/web/server/src/simcore_service_webserver/licenses/_licensed_items_repository.py

Lines changed: 61 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,18 @@
3838
_SELECTION_ARGS = get_columns_from_db_model(licensed_items, LicensedItemDB)
3939

4040

41-
def _build_insert_query(
41+
async def create(
42+
app: web.Application,
43+
connection: AsyncConnection | None = None,
44+
*,
4245
licensed_resource_name: str,
4346
licensed_resource_type: LicensedResourceType,
44-
licensed_resource_data: dict[str, Any] | None,
45-
license_key: str | None,
46-
product_name: ProductName | None,
47-
pricing_plan_id: PricingPlanId | None,
48-
*,
49-
on_conflict_do_nothing: bool = False,
50-
) -> postgresql.Insert:
47+
licensed_resource_data: dict[str, Any] | None = None,
48+
license_key: str | None = None,
49+
product_name: ProductName | None = None,
50+
pricing_plan_id: PricingPlanId | None = None,
51+
) -> LicensedItemDB:
52+
5153
query = (
5254
postgresql.insert(licensed_items)
5355
.values(
@@ -62,30 +64,7 @@ def _build_insert_query(
6264
)
6365
.returning(*_SELECTION_ARGS)
6466
)
65-
if on_conflict_do_nothing:
66-
query = query.on_conflict_do_nothing()
67-
return query
68-
6967

70-
async def create(
71-
app: web.Application,
72-
connection: AsyncConnection | None = None,
73-
*,
74-
licensed_resource_name: str,
75-
licensed_resource_type: LicensedResourceType,
76-
licensed_resource_data: dict[str, Any] | None = None,
77-
license_key: str | None = None,
78-
product_name: ProductName | None = None,
79-
pricing_plan_id: PricingPlanId | None = None,
80-
) -> LicensedItemDB:
81-
query = _build_insert_query(
82-
licensed_resource_name,
83-
licensed_resource_type,
84-
licensed_resource_data,
85-
license_key,
86-
product_name,
87-
pricing_plan_id,
88-
)
8968
async with transaction_context(get_asyncpg_engine(app), connection) as conn:
9069
result = await conn.execute(query)
9170
row = result.one()
@@ -103,18 +82,34 @@ async def create_if_not_exists(
10382
product_name: ProductName | None = None,
10483
pricing_plan_id: PricingPlanId | None = None,
10584
) -> LicensedItemDB:
106-
query = _build_insert_query(
107-
licensed_resource_name,
108-
licensed_resource_type,
109-
licensed_resource_data,
110-
license_key,
111-
product_name,
112-
pricing_plan_id,
113-
on_conflict_do_nothing=True,
85+
insert_query_or_none_query = (
86+
postgresql.insert(licensed_items)
87+
.values(
88+
licensed_resource_name=licensed_resource_name,
89+
licensed_resource_type=licensed_resource_type,
90+
licensed_resource_data=licensed_resource_data,
91+
license_key=license_key,
92+
pricing_plan_id=pricing_plan_id,
93+
product_name=product_name,
94+
created=func.now(),
95+
modified=func.now(),
96+
)
97+
.on_conflict_do_nothing()
98+
.returning(*_SELECTION_ARGS)
11499
)
100+
115101
async with transaction_context(get_asyncpg_engine(app), connection) as conn:
116-
result = await conn.execute(query)
102+
result = await conn.execute(insert_query_or_none_query)
117103
row = result.one_or_none()
104+
if row is None:
105+
select_query = select(*_SELECTION_ARGS).where(
106+
(licensed_items.c.licensed_resource_name == licensed_resource_name)
107+
& (licensed_items.c.licensed_resource_type == licensed_resource_type)
108+
)
109+
result = await conn.execute(select_query)
110+
row = result.one()
111+
112+
assert row is not None # nosec
118113
return LicensedItemDB.model_validate(row)
119114

120115

@@ -185,7 +180,7 @@ async def get(
185180
licensed_item_id: LicensedItemID,
186181
product_name: ProductName,
187182
) -> LicensedItemDB:
188-
base_query = (
183+
select_query = (
189184
select(*_SELECTION_ARGS)
190185
.select_from(licensed_items)
191186
.where(
@@ -195,13 +190,37 @@ async def get(
195190
)
196191

197192
async with pass_or_acquire_connection(get_asyncpg_engine(app), connection) as conn:
198-
result = await conn.execute(base_query)
193+
result = await conn.execute(select_query)
199194
row = result.one_or_none()
200195
if row is None:
201196
raise LicensedItemNotFoundError(licensed_item_id=licensed_item_id)
202197
return LicensedItemDB.model_validate(row)
203198

204199

200+
async def get_by_resource_identifier(
201+
app: web.Application,
202+
connection: AsyncConnection | None = None,
203+
*,
204+
licensed_resource_name: str,
205+
licensed_resource_type: LicensedResourceType,
206+
) -> LicensedItemDB:
207+
select_query = select(*_SELECTION_ARGS).where(
208+
(licensed_items.c.licensed_resource_name == licensed_resource_name)
209+
& (licensed_items.c.licensed_resource_type == licensed_resource_type)
210+
)
211+
212+
async with pass_or_acquire_connection(get_asyncpg_engine(app), connection) as conn:
213+
result = await conn.execute(select_query)
214+
row = result.one_or_none()
215+
if row is None:
216+
raise LicensedItemNotFoundError(
217+
licensed_item_id="Unkown",
218+
licensed_resource_name=licensed_resource_name,
219+
licensed_resource_type=licensed_resource_type,
220+
)
221+
return LicensedItemDB.model_validate(row)
222+
223+
205224
async def update(
206225
app: web.Application,
207226
connection: AsyncConnection | None = None,

services/web/server/src/simcore_service_webserver/licenses/_licensed_items_service.py

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,30 +32,79 @@
3232
from ..wallets.errors import WalletNotEnoughCreditsError
3333
from . import _licensed_items_repository
3434
from ._common.models import LicensedItemsBodyParams
35-
from .errors import LicensedItemPricingPlanMatchError
35+
from .errors import LicensedItemNotFoundError, LicensedItemPricingPlanMatchError
3636

3737
_logger = logging.getLogger(__name__)
3838

3939

40-
async def create_licensed_item_from_resource(
40+
def _compute_difference(old_data: dict, new_data: dict):
41+
differences = {
42+
k: {"old": old_data[k], "new": new_data[k]}
43+
for k in old_data
44+
if old_data[k] != new_data.get(k)
45+
}
46+
differences.update(
47+
{k: {"old": None, "new": new_data[k]} for k in new_data if k not in old_data}
48+
)
49+
return differences
50+
51+
52+
async def register_licensed_item_from_resource(
4153
app: web.Application,
4254
*,
4355
licensed_resource_name: str,
4456
licensed_resource_type: LicensedResourceType,
4557
licensed_resource_data: BaseModel,
4658
license_key: str | None,
4759
) -> LicensedItemDB:
48-
return await _licensed_items_repository.create_if_not_exists(
49-
app,
50-
licensed_resource_name=licensed_resource_name,
51-
licensed_resource_type=licensed_resource_type,
52-
licensed_resource_data=licensed_resource_data.model_dump(
60+
61+
try:
62+
license_item = await _licensed_items_repository.get_by_resource_identifier(
63+
app,
64+
licensed_resource_name=licensed_resource_name,
65+
licensed_resource_type=licensed_resource_type,
66+
)
67+
68+
if license_item.licensed_resource_data != licensed_resource_data.model_dump(
5369
mode="json", exclude_unset=True
54-
),
55-
license_key=license_key,
56-
product_name=None,
57-
pricing_plan_id=None,
58-
)
70+
):
71+
differences = _compute_difference(
72+
license_item.licensed_resource_data or {},
73+
licensed_resource_data.model_dump(mode="json", exclude_unset=True),
74+
)
75+
_logger.warning(
76+
"CHANGES: NEEDED for %s, %s: Resource differs from the one registered: %s",
77+
licensed_resource_name,
78+
licensed_resource_type,
79+
differences,
80+
)
81+
else:
82+
_logger.info(
83+
"Resource %s, %s already registered",
84+
licensed_resource_name,
85+
licensed_resource_type,
86+
)
87+
88+
except LicensedItemNotFoundError:
89+
license_item = await _licensed_items_repository.create_if_not_exists(
90+
app,
91+
licensed_resource_name=licensed_resource_name,
92+
licensed_resource_type=licensed_resource_type,
93+
licensed_resource_data=licensed_resource_data.model_dump(
94+
mode="json", exclude_unset=True
95+
),
96+
license_key=license_key,
97+
product_name=None,
98+
pricing_plan_id=None,
99+
)
100+
101+
_logger.info(
102+
"NEW license with resource %s, %s already registered",
103+
licensed_resource_name,
104+
licensed_resource_type,
105+
)
106+
107+
return license_item
59108

60109

61110
async def get_licensed_item(

services/web/server/tests/unit/with_dbs/04/licenses/test_itis_vip_service.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,25 @@ async def test_sync_itis_vip_as_licensed_items(
149149

150150
for vip_item in items:
151151
# TODO: how to update to minimize collisions? one by one?
152-
await _licensed_items_service.create_licensed_item_from_resource(
153-
client.app,
154-
licensed_resource_name=f"{category}/{vip_item.id}",
155-
licensed_resource_type=LicensedResourceType.VIP_MODEL,
156-
licensed_resource_data=vip_item,
157-
license_key=vip_item.license_key,
152+
153+
got1 = (
154+
await _licensed_items_service.register_licensed_item_from_resource(
155+
client.app,
156+
licensed_resource_name=f"{category}/{vip_item.id}",
157+
licensed_resource_type=LicensedResourceType.VIP_MODEL,
158+
licensed_resource_data=vip_item,
159+
license_key=vip_item.license_key,
160+
)
161+
)
162+
163+
got2 = (
164+
await _licensed_items_service.register_licensed_item_from_resource(
165+
client.app,
166+
licensed_resource_name=f"{category}/{vip_item.id}",
167+
licensed_resource_type=LicensedResourceType.VIP_MODEL,
168+
licensed_resource_data=vip_item,
169+
license_key=vip_item.license_key,
170+
)
158171
)
172+
173+
assert got1 == got2

0 commit comments

Comments
 (0)