Skip to content

Commit 156dc6d

Browse files
authored
Merge branch 'master' into mai/payments-to-asyncpg
2 parents 0208ce7 + 9d5b558 commit 156dc6d

File tree

3 files changed

+91
-32
lines changed

3 files changed

+91
-32
lines changed

services/web/server/src/simcore_service_webserver/functions/_functions_repository.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ async def list_functions(
453453
search_by_function_title=search_by_function_title,
454454
)
455455

456-
# Build the base query with join to access rights table
456+
# Use GROUP BY on the primary key to ensure unique functions
457457
base_query = (
458458
functions_table.select()
459459
.join(
@@ -466,6 +466,7 @@ async def list_functions(
466466
functions_access_rights_table.c.read,
467467
*attributes_filters,
468468
)
469+
.group_by(functions_table.c.uuid)
469470
)
470471

471472
# Get total count

services/web/server/tests/unit/with_dbs/04/functions/conftest.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55

66
from collections.abc import AsyncIterator, Awaitable, Callable
7-
from contextlib import AsyncExitStack
7+
from contextlib import AsyncExitStack, suppress
88
from typing import Any
99
from uuid import UUID, uuid4
1010

@@ -18,6 +18,7 @@
1818
ProjectFunction,
1919
)
2020
from models_library.functions import FunctionClass, SolverFunction
21+
from models_library.functions_errors import FunctionWriteAccessDeniedError
2122
from models_library.products import ProductName
2223
from models_library.rabbitmq_basic_types import RPCNamespace
2324
from pydantic import TypeAdapter
@@ -152,23 +153,26 @@ async def clean_functions(
152153
client: TestClient,
153154
webserver_rpc_client: WebServerRpcClient,
154155
logged_user: UserInfoDict,
156+
other_logged_user: UserInfoDict,
155157
osparc_product_name: ProductName,
156158
) -> None:
157159
assert client.app
158160

159-
functions, _ = await webserver_rpc_client.functions.list_functions(
160-
pagination_limit=100,
161-
pagination_offset=0,
162-
user_id=logged_user["id"],
163-
product_name=osparc_product_name,
164-
)
165-
for function in functions:
166-
assert function.uid is not None
167-
await webserver_rpc_client.functions.delete_function(
168-
function_id=function.uid,
169-
user_id=logged_user["id"],
161+
for user_id in (logged_user["id"], other_logged_user["id"]):
162+
functions, _ = await webserver_rpc_client.functions.list_functions(
163+
pagination_limit=100,
164+
pagination_offset=0,
165+
user_id=user_id,
170166
product_name=osparc_product_name,
171167
)
168+
for function in functions:
169+
assert function.uid is not None
170+
with suppress(FunctionWriteAccessDeniedError):
171+
await webserver_rpc_client.functions.delete_function(
172+
function_id=function.uid,
173+
user_id=user_id,
174+
product_name=osparc_product_name,
175+
)
172176

173177

174178
@pytest.fixture

services/web/server/tests/unit/with_dbs/04/functions/wb-api-server/test_functions_controller_rpc.py

Lines changed: 73 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# pylint: disable=redefined-outer-name
22
# pylint: disable=unused-argument
3+
# pylint: disable=too-many-arguments
34

45
import datetime
56
from collections.abc import Callable
@@ -27,8 +28,10 @@
2728
FunctionsWriteApiAccessDeniedError,
2829
FunctionWriteAccessDeniedError,
2930
)
31+
from models_library.groups import EVERYONE_GROUP_ID
3032
from models_library.products import ProductName
3133
from models_library.rest_ordering import OrderBy, OrderDirection
34+
from models_library.rest_pagination import PageMetaInfoLimitOffset
3235
from pytest_simcore.helpers.webserver_users import UserInfoDict
3336
from servicelib.rabbitmq.rpc_interfaces.webserver.v1 import WebServerRpcClient
3437

@@ -194,7 +197,7 @@ async def test_list_functions(
194197
)
195198

196199
# Assert the list contains the registered function
197-
assert len(functions) > 0
200+
assert len(functions) == 1
198201
assert any(f.uid == registered_function.uid for f in functions)
199202

200203

@@ -221,7 +224,7 @@ async def test_list_functions_mixed_user(
221224
)
222225
for _ in range(2)
223226
]
224-
227+
assert int(logged_user["primary_gid"]) != 1
225228
# List functions for the other logged user
226229
other_functions, _ = await webserver_rpc_client.functions.list_functions(
227230
pagination_limit=10,
@@ -250,7 +253,7 @@ async def test_list_functions_mixed_user(
250253
product_name=osparc_product_name,
251254
)
252255
# Assert the list contains only the logged user's function
253-
assert len(functions) == 2
256+
assert len(functions) == len(registered_functions)
254257
assert all(f.uid in [rf.uid for rf in registered_functions] for f in functions)
255258

256259
other_functions, _ = await webserver_rpc_client.functions.list_functions(
@@ -260,11 +263,49 @@ async def test_list_functions_mixed_user(
260263
product_name=osparc_product_name,
261264
)
262265
# Assert the list contains only the other user's functions
263-
assert len(other_functions) == 3
266+
assert len(other_functions) == len(other_registered_function)
264267
assert all(
265268
f.uid in [orf.uid for orf in other_registered_function] for f in other_functions
266269
)
267270

271+
# Add other-user permissions to a logged user function
272+
await webserver_rpc_client.functions.set_group_permissions(
273+
object_type="function",
274+
permission_group_id=int(other_logged_user["primary_gid"]),
275+
object_ids=[registered_functions[0].uid],
276+
user_id=logged_user["id"],
277+
product_name=osparc_product_name,
278+
read=True,
279+
)
280+
281+
other_functions, _ = await webserver_rpc_client.functions.list_functions(
282+
pagination_limit=10,
283+
pagination_offset=0,
284+
user_id=other_logged_user["id"],
285+
product_name=osparc_product_name,
286+
)
287+
288+
assert len(other_functions) == len(other_registered_function) + 1
289+
assert any(f.uid == registered_functions[0].uid for f in other_functions)
290+
291+
# Add all-user permissions to a logged user function
292+
await webserver_rpc_client.functions.set_group_permissions(
293+
object_type="function",
294+
permission_group_id=EVERYONE_GROUP_ID,
295+
object_ids=[registered_functions[0].uid],
296+
user_id=logged_user["id"],
297+
product_name=osparc_product_name,
298+
read=True,
299+
write=True,
300+
)
301+
other_functions, _ = await webserver_rpc_client.functions.list_functions(
302+
pagination_limit=10,
303+
pagination_offset=0,
304+
user_id=other_logged_user["id"],
305+
product_name=osparc_product_name,
306+
)
307+
assert len(other_functions) == len(other_registered_function) + 1
308+
268309

269310
@pytest.mark.parametrize("user_role", [UserRole.USER])
270311
@pytest.mark.parametrize(
@@ -276,38 +317,48 @@ async def test_list_functions_mixed_user(
276317
],
277318
)
278319
@pytest.mark.parametrize(
279-
"test_pagination_limit, test_pagination_offset",
320+
"test_pagination_limit, test_pagination_offset, total_number_functions",
280321
[
281-
(5, 0),
282-
(2, 2),
283-
(12, 4),
322+
(5, 0, 10),
323+
(2, 2, 10),
324+
(12, 4, 10),
284325
],
285326
)
286327
async def test_list_functions_with_pagination_ordering(
287328
client: TestClient,
288329
add_user_function_api_access_rights: None,
289330
webserver_rpc_client: WebServerRpcClient,
290331
create_fake_function_obj: Callable[[FunctionClass], ProjectFunction],
291-
clean_functions: None,
292332
osparc_product_name: ProductName,
293333
logged_user: UserInfoDict,
294334
order_by: OrderBy | None,
295335
test_pagination_limit: int,
296336
test_pagination_offset: int,
337+
total_number_functions: int,
338+
clean_functions: None,
297339
):
340+
# Making sure functions are empty before we start
341+
assert await webserver_rpc_client.functions.list_functions(
342+
pagination_limit=1,
343+
pagination_offset=0,
344+
user_id=logged_user["id"],
345+
product_name=osparc_product_name,
346+
) == (
347+
[],
348+
PageMetaInfoLimitOffset(limit=1, total=0, offset=0, count=0),
349+
)
298350
# Register multiple functions
299-
TOTAL_FUNCTIONS = 10
300351
registered_functions = [
301352
await webserver_rpc_client.functions.register_function(
302353
function=create_fake_function_obj(FunctionClass.PROJECT),
303354
user_id=logged_user["id"],
304355
product_name=osparc_product_name,
305356
)
306-
for _ in range(TOTAL_FUNCTIONS)
357+
for _ in range(total_number_functions)
307358
]
308359

309360
# List functions with pagination
310-
functions, page_info = await webserver_rpc_client.functions.list_functions(
361+
listed_functions, page_info = await webserver_rpc_client.functions.list_functions(
311362
pagination_limit=test_pagination_limit,
312363
pagination_offset=test_pagination_offset,
313364
user_id=logged_user["id"],
@@ -316,23 +367,26 @@ async def test_list_functions_with_pagination_ordering(
316367
)
317368

318369
# Assert the list contains the correct number of functions
319-
assert len(functions) == min(
320-
test_pagination_limit, max(0, TOTAL_FUNCTIONS - test_pagination_offset)
370+
assert len(listed_functions) == min(
371+
test_pagination_limit, max(0, total_number_functions - test_pagination_offset)
321372
)
322-
assert all(f.uid in [rf.uid for rf in registered_functions] for f in functions)
323-
assert page_info.count == len(functions)
324-
assert page_info.total == TOTAL_FUNCTIONS
373+
374+
assert all(
375+
f.uid in [rf.uid for rf in registered_functions] for f in listed_functions
376+
)
377+
assert page_info.count == len(listed_functions)
378+
assert page_info.total == total_number_functions
325379

326380
# Verify the functions are sorted correctly based on the order_by parameter
327381
if order_by:
328382
field = order_by.field
329383
direction = order_by.direction
330384
sorted_functions = sorted(
331-
functions,
385+
listed_functions,
332386
key=lambda f: getattr(f, field),
333387
reverse=(direction == OrderDirection.DESC),
334388
)
335-
assert functions == sorted_functions
389+
assert listed_functions == sorted_functions
336390

337391

338392
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)