Skip to content

Commit 8fac934

Browse files
committed
adding trashing
1 parent df3a3db commit 8fac934

File tree

3 files changed

+109
-12
lines changed

3 files changed

+109
-12
lines changed

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from typing import Any, TypeAlias
44
from uuid import UUID
55

6-
from common_library.exclude import UnSet
76
from pydantic import BaseModel, ConfigDict
87

98
from .products import ProductName
@@ -43,4 +42,4 @@ class LicensedItemDB(BaseModel):
4342
class LicensedItemUpdateDB(BaseModel):
4443
name: str | None = None
4544
pricing_plan_id: PricingPlanId | None = None
46-
trashed: datetime | None | UnSet = UnSet.VALUE
45+
trash: bool | None = None

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

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"""
66

77
import logging
8-
from typing import cast
8+
from typing import Literal, cast
99

1010
from aiohttp import web
1111
from models_library.licensed_items import (
@@ -47,7 +47,7 @@ async def create(
4747
pricing_plan_id: PricingPlanId,
4848
) -> LicensedItemDB:
4949
async with transaction_context(get_asyncpg_engine(app), connection) as conn:
50-
result = await conn.stream(
50+
result = await conn.execute(
5151
licensed_items.insert()
5252
.values(
5353
name=name,
@@ -59,7 +59,7 @@ async def create(
5959
)
6060
.returning(*_SELECTION_ARGS)
6161
)
62-
row = await result.first()
62+
row = result.one()
6363
return LicensedItemDB.model_validate(row)
6464

6565

@@ -71,13 +71,20 @@ async def list_(
7171
offset: NonNegativeInt,
7272
limit: NonNegativeInt,
7373
order_by: OrderBy,
74+
filter_trashed: Literal["exclude", "only", "include"] = "exclude",
7475
) -> tuple[int, list[LicensedItemDB]]:
7576
base_query = (
7677
select(*_SELECTION_ARGS)
7778
.select_from(licensed_items)
7879
.where(licensed_items.c.product_name == product_name)
7980
)
8081

82+
# Apply trashed filter
83+
if filter_trashed == "exclude":
84+
base_query = base_query.where(licensed_items.c.trashed.is_(None))
85+
elif filter_trashed == "only":
86+
base_query = base_query.where(licensed_items.c.trashed.is_not(None))
87+
8188
# Select total count from base_query
8289
subquery = base_query.subquery()
8390
count_query = select(func.count()).select_from(subquery)
@@ -137,11 +144,16 @@ async def update(
137144
# NOTE: at least 'touch' if updated_values is empty
138145
_updates = {
139146
**updates.model_dump(exclude_unset=True),
140-
"modified": func.now(),
147+
licensed_items.c.modified.name: func.now(),
141148
}
142149

150+
# trashing
151+
assert "trash" in dict(LicensedItemUpdateDB.model_fields) # nosec
152+
if trash := _updates.pop("trash", None):
153+
_updates[licensed_items.c.trashed.name] = func.now() if trash else None
154+
143155
async with transaction_context(get_asyncpg_engine(app), connection) as conn:
144-
result = await conn.stream(
156+
result = await conn.execute(
145157
licensed_items.update()
146158
.values(**_updates)
147159
.where(
@@ -150,7 +162,7 @@ async def update(
150162
)
151163
.returning(*_SELECTION_ARGS)
152164
)
153-
row = await result.first()
165+
row = result.one_or_none()
154166
if row is None:
155167
raise LicensedItemNotFoundError(licensed_item_id=licensed_item_id)
156168
return LicensedItemDB.model_validate(row)

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

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
# pylint: disable=unused-variable
44
# pylint: disable=too-many-arguments
55
# pylint: disable=too-many-statements
6-
from http import HTTPStatus
76

87
import pytest
98
from aiohttp.test_utils import TestClient
@@ -14,20 +13,22 @@
1413
)
1514
from models_library.rest_ordering import OrderBy
1615
from pytest_simcore.helpers.webserver_login import UserInfoDict
17-
from servicelib.aiohttp import status
1816
from simcore_service_webserver.db.models import UserRole
1917
from simcore_service_webserver.licenses import _licensed_items_repository
2018
from simcore_service_webserver.licenses.errors import LicensedItemNotFoundError
2119
from simcore_service_webserver.projects.models import ProjectDict
2220

2321

24-
@pytest.mark.parametrize("user_role,expected", [(UserRole.USER, status.HTTP_200_OK)])
22+
@pytest.fixture
23+
def user_role() -> UserRole:
24+
return UserRole.USER
25+
26+
2527
async def test_licensed_items_db_crud(
2628
client: TestClient,
2729
logged_user: UserInfoDict,
2830
user_project: ProjectDict,
2931
osparc_product_name: str,
30-
expected: HTTPStatus,
3132
pricing_plan_id: int,
3233
):
3334
assert client.app
@@ -92,3 +93,88 @@ async def test_licensed_items_db_crud(
9293
licensed_item_id=_licensed_item_id,
9394
product_name=osparc_product_name,
9495
)
96+
97+
98+
async def test_licensed_items_db_trash(
99+
client: TestClient,
100+
logged_user: UserInfoDict,
101+
user_project: ProjectDict,
102+
osparc_product_name: str,
103+
pricing_plan_id: int,
104+
):
105+
assert client.app
106+
107+
# Create two licensed items
108+
licensed_item_db1 = await _licensed_items_repository.create(
109+
client.app,
110+
product_name=osparc_product_name,
111+
name="Model A",
112+
licensed_resource_type=LicensedResourceType.VIP_MODEL,
113+
pricing_plan_id=pricing_plan_id,
114+
)
115+
licensed_item_id1 = licensed_item_db1.licensed_item_id
116+
117+
licensed_item_db2 = await _licensed_items_repository.create(
118+
client.app,
119+
product_name=osparc_product_name,
120+
name="Model B",
121+
licensed_resource_type=LicensedResourceType.VIP_MODEL,
122+
pricing_plan_id=pricing_plan_id,
123+
)
124+
licensed_item_id2 = licensed_item_db2.licensed_item_id
125+
126+
# Trash one licensed item
127+
await _licensed_items_repository.update(
128+
client.app,
129+
licensed_item_id=licensed_item_id1,
130+
product_name=osparc_product_name,
131+
updates=LicensedItemUpdateDB(trash=True),
132+
)
133+
134+
# List with filter_trashed include
135+
output_include: tuple[
136+
int, list[LicensedItemDB]
137+
] = await _licensed_items_repository.list_(
138+
client.app,
139+
product_name=osparc_product_name,
140+
offset=0,
141+
limit=10,
142+
order_by=OrderBy(field="modified"),
143+
filter_trashed="include",
144+
)
145+
assert len(output_include[1]) == 2
146+
147+
# List with filter_trashed exclude
148+
output_exclude: tuple[
149+
int, list[LicensedItemDB]
150+
] = await _licensed_items_repository.list_(
151+
client.app,
152+
product_name=osparc_product_name,
153+
offset=0,
154+
limit=10,
155+
order_by=OrderBy(field="modified"),
156+
filter_trashed="exclude",
157+
)
158+
assert len(output_exclude[1]) == 1
159+
assert output_exclude[1][0].licensed_item_id == licensed_item_id2
160+
161+
# List with filter_trashed all
162+
output_all: tuple[
163+
int, list[LicensedItemDB]
164+
] = await _licensed_items_repository.list_(
165+
client.app,
166+
product_name=osparc_product_name,
167+
offset=0,
168+
limit=10,
169+
order_by=OrderBy(field="modified"),
170+
filter_trashed="all",
171+
)
172+
assert len(output_all[1]) == 2
173+
174+
# Get the trashed licensed item
175+
trashed_item = await _licensed_items_repository.get(
176+
client.app,
177+
licensed_item_id=licensed_item_id1,
178+
product_name=osparc_product_name,
179+
)
180+
assert trashed_item.trashed is True

0 commit comments

Comments
 (0)