Skip to content

Commit 01cf113

Browse files
authored
Merge branch 'master' into 2025/change/makePOSTGRESSIZEconfigurable
2 parents a284a75 + 15204d5 commit 01cf113

File tree

5 files changed

+307
-45
lines changed

5 files changed

+307
-45
lines changed

packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/webserver/functions/functions_rpc_interface.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@
1717
RegisteredFunctionJobCollection,
1818
)
1919
from models_library.functions import (
20+
FunctionClass,
2021
FunctionJobStatus,
2122
FunctionOutputs,
2223
FunctionUserAccessRights,
2324
FunctionUserApiAccessRights,
2425
)
2526
from models_library.products import ProductName
2627
from models_library.rabbitmq_basic_types import RPCMethodName
28+
from models_library.rest_ordering import OrderBy
2729
from models_library.rest_pagination import PageMetaInfoLimitOffset
2830
from models_library.users import UserID
2931
from pydantic import TypeAdapter
@@ -135,6 +137,10 @@ async def list_functions(
135137
product_name: ProductName,
136138
pagination_offset: int,
137139
pagination_limit: int,
140+
order_by: OrderBy | None = None,
141+
filter_by_function_class: FunctionClass | None = None,
142+
search_by_function_title: str | None = None,
143+
search_by_multi_columns: str | None = None,
138144
) -> tuple[list[RegisteredFunction], PageMetaInfoLimitOffset]:
139145
result: tuple[list[RegisteredFunction], PageMetaInfoLimitOffset] = (
140146
await rabbitmq_rpc_client.request(
@@ -144,6 +150,10 @@ async def list_functions(
144150
pagination_limit=pagination_limit,
145151
user_id=user_id,
146152
product_name=product_name,
153+
order_by=order_by,
154+
filter_by_function_class=filter_by_function_class,
155+
search_by_function_title=search_by_function_title,
156+
search_by_multi_columns=search_by_multi_columns,
147157
)
148158
)
149159
return TypeAdapter(

services/web/server/src/simcore_service_webserver/functions/_controller/_functions_rpc.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from models_library.functions import (
44
Function,
55
FunctionAccessRights,
6+
FunctionClass,
67
FunctionID,
78
FunctionInputs,
89
FunctionInputSchema,
@@ -40,6 +41,7 @@
4041
UnsupportedFunctionJobClassError,
4142
)
4243
from models_library.products import ProductName
44+
from models_library.rest_ordering import OrderBy
4345
from models_library.rest_pagination import PageMetaInfoLimitOffset
4446
from models_library.users import UserID
4547
from servicelib.rabbitmq import RPCRouter
@@ -176,13 +178,21 @@ async def list_functions(
176178
product_name: ProductName,
177179
pagination_limit: int,
178180
pagination_offset: int,
181+
order_by: OrderBy | None = None,
182+
filter_by_function_class: FunctionClass | None = None,
183+
search_by_function_title: str | None = None,
184+
search_by_multi_columns: str | None = None,
179185
) -> tuple[list[RegisteredFunction], PageMetaInfoLimitOffset]:
180186
return await _functions_service.list_functions(
181187
app=app,
182188
user_id=user_id,
183189
product_name=product_name,
184190
pagination_limit=pagination_limit,
185191
pagination_offset=pagination_offset,
192+
order_by=order_by,
193+
filter_by_function_class=filter_by_function_class,
194+
search_by_function_title=search_by_function_title,
195+
search_by_multi_columns=search_by_multi_columns,
186196
)
187197

188198

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

Lines changed: 77 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import sqlalchemy
88
from aiohttp import web
9+
from models_library.basic_types import IDStr
910
from models_library.functions import (
1011
FunctionAccessRightsDB,
1112
FunctionClass,
@@ -54,6 +55,7 @@
5455
)
5556
from models_library.groups import GroupID
5657
from models_library.products import ProductName
58+
from models_library.rest_ordering import OrderBy, OrderDirection
5759
from models_library.rest_pagination import PageMetaInfoLimitOffset
5860
from models_library.users import UserID
5961
from pydantic import TypeAdapter
@@ -84,10 +86,10 @@
8486
pass_or_acquire_connection,
8587
transaction_context,
8688
)
87-
from sqlalchemy import Text, cast
89+
from sqlalchemy import String, Text, cast
8890
from sqlalchemy.engine.row import Row
8991
from sqlalchemy.ext.asyncio import AsyncConnection
90-
from sqlalchemy.sql import func
92+
from sqlalchemy.sql import ColumnElement, func
9193

9294
from ..db.plugin import get_asyncpg_engine
9395
from ..groups.api import list_all_user_groups_ids
@@ -110,6 +112,8 @@
110112
function_job_collections_access_rights_table, FunctionJobCollectionAccessRightsDB
111113
)
112114

115+
DEFAULT_ORDER_BY = OrderBy(field=IDStr("modified"), direction=OrderDirection.DESC)
116+
113117

114118
async def create_function( # noqa: PLR0913
115119
app: web.Application,
@@ -347,6 +351,38 @@ async def get_function(
347351
return RegisteredFunctionDB.model_validate(row)
348352

349353

354+
def _create_list_functions_attributes_filters(
355+
*,
356+
filter_by_function_class: FunctionClass | None,
357+
search_by_multi_columns: str | None,
358+
search_by_function_title: str | None,
359+
) -> list[ColumnElement]:
360+
attributes_filters: list[ColumnElement] = []
361+
362+
if filter_by_function_class is not None:
363+
attributes_filters.append(
364+
functions_table.c.function_class == filter_by_function_class.value
365+
)
366+
367+
if search_by_multi_columns is not None:
368+
attributes_filters.append(
369+
(functions_table.c.title.ilike(f"%{search_by_multi_columns}%"))
370+
| (functions_table.c.description.ilike(f"%{search_by_multi_columns}%"))
371+
| (
372+
cast(functions_table.c.uuid, String).ilike(
373+
f"%{search_by_multi_columns}%"
374+
)
375+
)
376+
)
377+
378+
if search_by_function_title is not None:
379+
attributes_filters.append(
380+
functions_table.c.title.ilike(f"%{search_by_function_title}%")
381+
)
382+
383+
return attributes_filters
384+
385+
350386
async def list_functions(
351387
app: web.Application,
352388
connection: AsyncConnection | None = None,
@@ -355,7 +391,12 @@ async def list_functions(
355391
product_name: ProductName,
356392
pagination_limit: int,
357393
pagination_offset: int,
394+
order_by: OrderBy | None = None,
395+
filter_by_function_class: FunctionClass | None = None,
396+
search_by_multi_columns: str | None = None,
397+
search_by_function_title: str | None = None,
358398
) -> tuple[list[RegisteredFunctionDB], PageMetaInfoLimitOffset]:
399+
359400
async with pass_or_acquire_connection(get_asyncpg_engine(app), connection) as conn:
360401
await check_user_api_access_rights(
361402
app,
@@ -365,39 +406,59 @@ async def list_functions(
365406
api_access_rights=[FunctionsApiAccessRights.READ_FUNCTIONS],
366407
)
367408
user_groups = await list_all_user_groups_ids(app, user_id=user_id)
409+
attributes_filters = _create_list_functions_attributes_filters(
410+
filter_by_function_class=filter_by_function_class,
411+
search_by_multi_columns=search_by_multi_columns,
412+
search_by_function_title=search_by_function_title,
413+
)
368414

369-
subquery = (
370-
functions_access_rights_table.select()
371-
.with_only_columns(functions_access_rights_table.c.function_uuid)
415+
# Build the base query with join to access rights table
416+
base_query = (
417+
functions_table.select()
418+
.join(
419+
functions_access_rights_table,
420+
functions_table.c.uuid == functions_access_rights_table.c.function_uuid,
421+
)
372422
.where(
373423
functions_access_rights_table.c.group_id.in_(user_groups),
374424
functions_access_rights_table.c.product_name == product_name,
375425
functions_access_rights_table.c.read,
426+
*attributes_filters,
376427
)
377428
)
378429

379-
total_count_result = await conn.scalar(
380-
func.count()
381-
.select()
382-
.select_from(functions_table)
383-
.where(functions_table.c.uuid.in_(subquery))
430+
# Get total count
431+
total_count = await conn.scalar(
432+
func.count().select().select_from(base_query.subquery())
384433
)
385-
if total_count_result == 0:
434+
if total_count == 0:
386435
return [], PageMetaInfoLimitOffset(
387436
total=0, offset=pagination_offset, limit=pagination_limit, count=0
388437
)
438+
439+
if order_by is None:
440+
order_by = DEFAULT_ORDER_BY
441+
# Apply ordering and pagination
442+
if order_by.direction == OrderDirection.ASC:
443+
base_query = base_query.order_by(
444+
sqlalchemy.asc(getattr(functions_table.c, order_by.field)),
445+
functions_table.c.uuid,
446+
)
447+
else:
448+
base_query = base_query.order_by(
449+
sqlalchemy.desc(getattr(functions_table.c, order_by.field)),
450+
functions_table.c.uuid,
451+
)
452+
389453
function_rows = [
390454
RegisteredFunctionDB.model_validate(row)
391455
async for row in await conn.stream(
392-
functions_table.select()
393-
.where(functions_table.c.uuid.in_(subquery))
394-
.offset(pagination_offset)
395-
.limit(pagination_limit)
456+
base_query.offset(pagination_offset).limit(pagination_limit)
396457
)
397458
]
398459

399460
return function_rows, PageMetaInfoLimitOffset(
400-
total=total_count_result,
461+
total=total_count,
401462
offset=pagination_offset,
402463
limit=pagination_limit,
403464
count=len(function_rows),

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from aiohttp import web
2+
from models_library.basic_types import IDStr
23
from models_library.functions import (
34
Function,
45
FunctionClass,
@@ -37,6 +38,7 @@
3738
)
3839
from models_library.groups import GroupID
3940
from models_library.products import ProductName
41+
from models_library.rest_ordering import OrderBy
4042
from models_library.rest_pagination import PageMetaInfoLimitOffset
4143
from models_library.users import UserID
4244
from servicelib.rabbitmq import RPCRouter
@@ -185,13 +187,28 @@ async def list_functions(
185187
product_name: ProductName,
186188
pagination_limit: int,
187189
pagination_offset: int,
190+
order_by: OrderBy | None = None,
191+
filter_by_function_class: FunctionClass | None = None,
192+
search_by_function_title: str | None = None,
193+
search_by_multi_columns: str | None = None,
188194
) -> tuple[list[RegisteredFunction], PageMetaInfoLimitOffset]:
189195
returned_functions, page = await _functions_repository.list_functions(
190196
app=app,
191197
user_id=user_id,
192198
product_name=product_name,
193199
pagination_limit=pagination_limit,
194200
pagination_offset=pagination_offset,
201+
order_by=(
202+
OrderBy(
203+
field=IDStr("uuid") if order_by.field == "uid" else order_by.field,
204+
direction=order_by.direction,
205+
)
206+
if order_by
207+
else None
208+
),
209+
filter_by_function_class=filter_by_function_class,
210+
search_by_function_title=search_by_function_title,
211+
search_by_multi_columns=search_by_multi_columns,
195212
)
196213
return [
197214
_decode_function(returned_function) for returned_function in returned_functions

0 commit comments

Comments
 (0)