22
33import logging
44from datetime import UTC , datetime , timedelta
5- from enum import Enum , auto
6- from pprint import pformat
7- from typing import NamedTuple
85
96from aiohttp import web
10- from deepdiff import DeepDiff # type: ignore[attr-defined]
11- from models_library .licenses import (
12- LicensedItem ,
13- LicensedItemID ,
14- LicensedItemPage ,
15- LicensedItemPatchDB ,
16- LicensedResourceDB ,
17- LicensedResourceType ,
18- )
7+ from models_library .licenses import LicensedItem , LicensedItemID , LicensedItemPage
198from models_library .products import ProductName
209from models_library .resource_tracker_licensed_items_purchases import (
2110 LicensedItemsPurchasesCreate ,
2211)
2312from models_library .rest_ordering import OrderBy
2413from models_library .users import UserID
25- from pydantic import BaseModel , NonNegativeInt
14+ from pydantic import NonNegativeInt
2615from servicelib .rabbitmq .rpc_interfaces .resource_usage_tracker import (
2716 licensed_items_purchases ,
2817)
3221from ..users .api import get_user
3322from ..wallets .api import get_wallet_with_available_credits_by_user_and_wallet
3423from ..wallets .errors import WalletNotEnoughCreditsError
35- from . import _licensed_items_repository , _licensed_resources_repository
24+ from . import _licensed_items_repository
3625from ._common .models import LicensedItemsBodyParams
37- from .errors import LicensedItemNotFoundError , LicensedItemPricingPlanMatchError
26+ from .errors import LicensedItemPricingPlanMatchError
3827
3928_logger = logging .getLogger (__name__ )
4029
4130
42- class RegistrationState (Enum ):
43- ALREADY_REGISTERED = auto ()
44- DIFFERENT_RESOURCE = auto ()
45- NEWLY_REGISTERED = auto ()
46-
47-
48- class RegistrationResult (NamedTuple ):
49- registered : LicensedResourceDB
50- state : RegistrationState
51- message : str | None
52-
53-
54- async def register_licensed_resource (
55- app : web .Application ,
56- * ,
57- licensed_resource_name : str ,
58- licensed_resource_type : LicensedResourceType ,
59- licensed_resource_data : BaseModel ,
60- licensed_item_display_name : str ,
61- ) -> RegistrationResult :
62- # NOTE about the implementation choice:
63- # Using `create_if_not_exists` (INSERT with IGNORE_ON_CONFLICT) would have been an option,
64- # but it generates excessive error logs due to conflicts.
65- #
66- # To avoid this, we first attempt to retrieve the resource using `get_by_resource_identifier` (GET).
67- # If the resource does not exist, we proceed with `create_if_not_exists` (INSERT with IGNORE_ON_CONFLICT).
68- #
69- # This approach not only reduces unnecessary error logs but also helps prevent race conditions
70- # when multiple concurrent calls attempt to register the same resource.
71-
72- resource_key = f"{ licensed_resource_type } , { licensed_resource_name } "
73- new_licensed_resource_data = licensed_resource_data .model_dump (
74- mode = "json" ,
75- exclude_unset = True ,
76- )
77-
78- try :
79- licensed_resource = (
80- await _licensed_resources_repository .get_by_resource_identifier (
81- app ,
82- licensed_resource_name = licensed_resource_name ,
83- licensed_resource_type = licensed_resource_type ,
84- )
85- )
86-
87- if licensed_resource .licensed_resource_data != new_licensed_resource_data :
88- ddiff = DeepDiff (
89- licensed_resource .licensed_resource_data , new_licensed_resource_data
90- )
91- msg = (
92- f"DIFFERENT_RESOURCE: { resource_key = } found in licensed_resource_id={ licensed_resource .licensed_resource_id } with different data. "
93- f"Diff:\n \t { pformat (ddiff , indent = 2 , width = 200 )} "
94- )
95- return RegistrationResult (
96- licensed_resource , RegistrationState .DIFFERENT_RESOURCE , msg
97- )
98-
99- return RegistrationResult (
100- licensed_resource ,
101- RegistrationState .ALREADY_REGISTERED ,
102- f"ALREADY_REGISTERED: { resource_key = } found in licensed_resource_id={ licensed_resource .licensed_resource_id } " ,
103- )
104-
105- except LicensedItemNotFoundError :
106- licensed_resource = await _licensed_resources_repository .create_if_not_exists (
107- app ,
108- display_name = licensed_item_display_name ,
109- licensed_resource_name = licensed_resource_name ,
110- licensed_resource_type = licensed_resource_type ,
111- licensed_resource_data = new_licensed_resource_data ,
112- )
113-
114- return RegistrationResult (
115- licensed_resource ,
116- RegistrationState .NEWLY_REGISTERED ,
117- f"NEWLY_REGISTERED: { resource_key = } registered with licensed_resource_id={ licensed_resource .licensed_resource_id } " ,
118- )
119-
120-
12131async def get_licensed_item (
12232 app : web .Application ,
12333 * ,
@@ -176,32 +86,32 @@ async def list_licensed_items(
17686 )
17787
17888
179- async def trash_licensed_item (
180- app : web .Application ,
181- * ,
182- product_name : ProductName ,
183- licensed_item_id : LicensedItemID ,
184- ):
185- await _licensed_items_repository .update (
186- app ,
187- product_name = product_name ,
188- licensed_item_id = licensed_item_id ,
189- updates = LicensedItemPatchDB (trash = True ),
190- )
191-
192-
193- async def untrash_licensed_item (
194- app : web .Application ,
195- * ,
196- product_name : ProductName ,
197- licensed_item_id : LicensedItemID ,
198- ):
199- await _licensed_items_repository .update (
200- app ,
201- product_name = product_name ,
202- licensed_item_id = licensed_item_id ,
203- updates = LicensedItemPatchDB (trash = True ),
204- )
89+ # async def trash_licensed_item(
90+ # app: web.Application,
91+ # *,
92+ # product_name: ProductName,
93+ # licensed_item_id: LicensedItemID,
94+ # ):
95+ # await _licensed_items_repository.update(
96+ # app,
97+ # product_name=product_name,
98+ # licensed_item_id=licensed_item_id,
99+ # updates=LicensedItemPatchDB(trash=True),
100+ # )
101+
102+
103+ # async def untrash_licensed_item(
104+ # app: web.Application,
105+ # *,
106+ # product_name: ProductName,
107+ # licensed_item_id: LicensedItemID,
108+ # ):
109+ # await _licensed_items_repository.update(
110+ # app,
111+ # product_name=product_name,
112+ # licensed_item_id=licensed_item_id,
113+ # updates=LicensedItemPatchDB(trash=True),
114+ # )
205115
206116
207117async def purchase_licensed_item (
@@ -210,6 +120,8 @@ async def purchase_licensed_item(
210120 product_name : ProductName ,
211121 user_id : UserID ,
212122 licensed_item_id : LicensedItemID ,
123+ key : str ,
124+ version : str ,
213125 body_params : LicensedItemsBodyParams ,
214126) -> None :
215127 # Check user wallet permissions
@@ -245,6 +157,8 @@ async def purchase_licensed_item(
245157 _data = LicensedItemsPurchasesCreate (
246158 product_name = product_name ,
247159 licensed_item_id = licensed_item_id ,
160+ key = key ,
161+ version = version ,
248162 wallet_id = wallet .wallet_id ,
249163 wallet_name = wallet .name ,
250164 pricing_plan_id = body_params .pricing_plan_id ,
0 commit comments