Skip to content

Commit 3e21fc7

Browse files
committed
cleanup
1 parent 2050ef0 commit 3e21fc7

File tree

3 files changed

+133
-128
lines changed

3 files changed

+133
-128
lines changed

services/web/server/src/simcore_service_webserver/users/_users_rest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ async def list_users_accounts(request: web.Request) -> web.Response:
196196
# ALL
197197
filter_any_account_request_status = None
198198

199-
users, total_count = await _users_service.list_all_users_as_admin(
199+
users, total_count = await _users_service.list_user_accounts(
200200
request.app,
201201
product_name=req_ctx.product_name,
202202
filter_any_account_request_status=filter_any_account_request_status,
@@ -234,7 +234,7 @@ async def search_user_accounts(request: web.Request) -> web.Response:
234234
UserAccountSearchQueryParams, request
235235
)
236236

237-
found = await _users_service.search_users_as_admin(
237+
found = await _users_service.search_users_accounts(
238238
request.app, email_glob=query_params.email, include_products=True
239239
)
240240

services/web/server/src/simcore_service_webserver/users/_users_service.py

Lines changed: 128 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ async def pre_register_user(
4949
product_name: ProductName,
5050
) -> UserAccountGet:
5151

52-
found = await search_users_as_admin(
52+
found = await search_users_accounts(
5353
app, email_glob=profile.email, product_name=product_name, include_products=False
5454
)
5555
if found:
@@ -83,7 +83,7 @@ async def pre_register_user(
8383
**details,
8484
)
8585

86-
found = await search_users_as_admin(
86+
found = await search_users_accounts(
8787
app, email_glob=profile.email, product_name=product_name, include_products=False
8888
)
8989

@@ -134,69 +134,6 @@ async def get_user_id_from_gid(app: web.Application, primary_gid: GroupID) -> Us
134134
return await _users_repository.get_user_id_from_pgid(app, primary_gid=primary_gid)
135135

136136

137-
async def search_users_as_admin(
138-
app: web.Application,
139-
*,
140-
email_glob: str,
141-
product_name: ProductName | None = None,
142-
include_products: bool = False,
143-
) -> list[UserAccountGet]:
144-
"""
145-
WARNING: this information is reserved for admin users. Note that the returned model include UserForAdminGet
146-
147-
NOTE: Functions in the service layer typically validate the caller's access rights
148-
using parameters like product_name and user_id. However, this function skips
149-
such checks as it is designed for scenarios (e.g., background tasks) where
150-
no caller or context is available.
151-
"""
152-
153-
def _glob_to_sql_like(glob_pattern: str) -> str:
154-
# Escape SQL LIKE special characters in the glob pattern
155-
sql_like_pattern = glob_pattern.replace("%", r"\%").replace("_", r"\_")
156-
# Convert glob wildcards to SQL LIKE wildcards
157-
return sql_like_pattern.replace("*", "%").replace("?", "_")
158-
159-
rows = await _users_repository.search_merged_pre_and_registered_users(
160-
get_asyncpg_engine(app),
161-
email_like=_glob_to_sql_like(email_glob),
162-
product_name=product_name,
163-
)
164-
165-
async def _list_products_or_none(user_id):
166-
if user_id is not None and include_products:
167-
products = await _users_repository.get_user_products(
168-
get_asyncpg_engine(app), user_id=user_id
169-
)
170-
return [_.product_name for _ in products]
171-
return None
172-
173-
return [
174-
UserAccountGet(
175-
first_name=r.first_name or r.pre_first_name,
176-
last_name=r.last_name or r.pre_last_name,
177-
email=r.email or r.pre_email,
178-
institution=r.institution,
179-
phone=r.phone or r.pre_phone,
180-
address=r.address,
181-
city=r.city,
182-
state=r.state,
183-
postal_code=r.postal_code,
184-
country=r.country,
185-
extras=r.extras or {},
186-
invited_by=r.invited_by,
187-
pre_registration_id=r.id,
188-
account_request_status=r.account_request_status,
189-
account_request_reviewed_by=r.account_request_reviewed_by,
190-
account_request_reviewed_at=r.account_request_reviewed_at,
191-
products=await _list_products_or_none(r.user_id),
192-
# NOTE: old users will not have extra details
193-
registered=r.user_id is not None if r.pre_email else r.status is not None,
194-
status=r.status,
195-
)
196-
for r in rows
197-
]
198-
199-
200137
async def get_users_in_group(app: web.Application, *, gid: GroupID) -> set[UserID]:
201138
return await _users_repository.get_users_ids_in_group(
202139
get_asyncpg_engine(app), group_id=gid
@@ -214,64 +151,6 @@ async def is_user_in_product(
214151
)
215152

216153

217-
async def list_all_users_as_admin(
218-
app: web.Application,
219-
*,
220-
product_name: ProductName,
221-
filter_any_account_request_status: list[AccountRequestStatus] | None = None,
222-
pagination_limit: int = 50,
223-
pagination_offset: int = 0,
224-
) -> tuple[list[dict[str, Any]], int]:
225-
"""
226-
Get a paginated list of users for admin view with filtering options.
227-
228-
Args:
229-
app: The web application instance
230-
filter_approved: If set, filters users by their approval status
231-
pagination_limit: Maximum number of users to return
232-
pagination_offset: Number of users to skip for pagination
233-
234-
Returns:
235-
A tuple containing (list of user dictionaries, total count of users)
236-
"""
237-
engine = get_asyncpg_engine(app)
238-
239-
# Get user data with pagination
240-
users_data, total_count = (
241-
await _users_repository.list_merged_pre_and_registered_users(
242-
engine,
243-
product_name=product_name,
244-
filter_any_account_request_status=filter_any_account_request_status,
245-
pagination_limit=pagination_limit,
246-
pagination_offset=pagination_offset,
247-
)
248-
)
249-
250-
# For each user, append additional information if needed
251-
result = []
252-
for user in users_data:
253-
# Add any additional processing needed for admin view
254-
user_dict = dict(user)
255-
256-
# Add products information if needed
257-
user_id = user.get("user_id")
258-
if user_id:
259-
products = await _users_repository.get_user_products(
260-
engine, user_id=user_id
261-
)
262-
user_dict["products"] = [p.product_name for p in products]
263-
264-
user_dict["registered"] = (
265-
user_id is not None
266-
if user.get("pre_email")
267-
else user.get("status") is not None
268-
)
269-
270-
result.append(user_dict)
271-
272-
return result, total_count
273-
274-
275154
#
276155
# GET USER PROPERTIES
277156
#
@@ -456,6 +335,132 @@ async def update_my_profile(
456335
)
457336

458337

338+
#
339+
# USER ACCOUNTS
340+
#
341+
342+
343+
async def list_user_accounts(
344+
app: web.Application,
345+
*,
346+
product_name: ProductName,
347+
filter_any_account_request_status: list[AccountRequestStatus] | None = None,
348+
pagination_limit: int = 50,
349+
pagination_offset: int = 0,
350+
) -> tuple[list[dict[str, Any]], int]:
351+
"""
352+
Get a paginated list of users for admin view with filtering options.
353+
354+
Args:
355+
app: The web application instance
356+
filter_approved: If set, filters users by their approval status
357+
pagination_limit: Maximum number of users to return
358+
pagination_offset: Number of users to skip for pagination
359+
360+
Returns:
361+
A tuple containing (list of user dictionaries, total count of users)
362+
"""
363+
engine = get_asyncpg_engine(app)
364+
365+
# Get user data with pagination
366+
users_data, total_count = (
367+
await _users_repository.list_merged_pre_and_registered_users(
368+
engine,
369+
product_name=product_name,
370+
filter_any_account_request_status=filter_any_account_request_status,
371+
pagination_limit=pagination_limit,
372+
pagination_offset=pagination_offset,
373+
)
374+
)
375+
376+
# For each user, append additional information if needed
377+
result = []
378+
for user in users_data:
379+
# Add any additional processing needed for admin view
380+
user_dict = dict(user)
381+
382+
# Add products information if needed
383+
user_id = user.get("user_id")
384+
if user_id:
385+
products = await _users_repository.get_user_products(
386+
engine, user_id=user_id
387+
)
388+
user_dict["products"] = [p.product_name for p in products]
389+
390+
user_dict["registered"] = (
391+
user_id is not None
392+
if user.get("pre_email")
393+
else user.get("status") is not None
394+
)
395+
396+
result.append(user_dict)
397+
398+
return result, total_count
399+
400+
401+
async def search_users_accounts(
402+
app: web.Application,
403+
*,
404+
email_glob: str,
405+
product_name: ProductName | None = None,
406+
include_products: bool = False,
407+
) -> list[UserAccountGet]:
408+
"""
409+
WARNING: this information is reserved for admin users. Note that the returned model include UserForAdminGet
410+
411+
NOTE: Functions in the service layer typically validate the caller's access rights
412+
using parameters like product_name and user_id. However, this function skips
413+
such checks as it is designed for scenarios (e.g., background tasks) where
414+
no caller or context is available.
415+
"""
416+
417+
def _glob_to_sql_like(glob_pattern: str) -> str:
418+
# Escape SQL LIKE special characters in the glob pattern
419+
sql_like_pattern = glob_pattern.replace("%", r"\%").replace("_", r"\_")
420+
# Convert glob wildcards to SQL LIKE wildcards
421+
return sql_like_pattern.replace("*", "%").replace("?", "_")
422+
423+
rows = await _users_repository.search_merged_pre_and_registered_users(
424+
get_asyncpg_engine(app),
425+
email_like=_glob_to_sql_like(email_glob),
426+
product_name=product_name,
427+
)
428+
429+
async def _list_products_or_none(user_id):
430+
if user_id is not None and include_products:
431+
products = await _users_repository.get_user_products(
432+
get_asyncpg_engine(app), user_id=user_id
433+
)
434+
return [_.product_name for _ in products]
435+
return None
436+
437+
return [
438+
UserAccountGet(
439+
first_name=r.first_name or r.pre_first_name,
440+
last_name=r.last_name or r.pre_last_name,
441+
email=r.email or r.pre_email,
442+
institution=r.institution,
443+
phone=r.phone or r.pre_phone,
444+
address=r.address,
445+
city=r.city,
446+
state=r.state,
447+
postal_code=r.postal_code,
448+
country=r.country,
449+
extras=r.extras or {},
450+
invited_by=r.invited_by,
451+
pre_registration_id=r.id,
452+
account_request_status=r.account_request_status,
453+
account_request_reviewed_by=r.account_request_reviewed_by,
454+
account_request_reviewed_at=r.account_request_reviewed_at,
455+
products=await _list_products_or_none(r.user_id),
456+
# NOTE: old users will not have extra details
457+
registered=r.user_id is not None if r.pre_email else r.status is not None,
458+
status=r.status,
459+
)
460+
for r in rows
461+
]
462+
463+
459464
async def approve_user_account(
460465
app: web.Application,
461466
*,

services/web/server/tests/unit/with_dbs/03/users/test_users_service.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ async def test_search_users_as_admin_real_user(
8282
user_email = product_owner_user["email"]
8383

8484
# Act
85-
found_users = await _users_service.search_users_as_admin(
85+
found_users = await _users_service.search_users_accounts(
8686
app, email_glob=user_email, product_name=product_name, include_products=False
8787
)
8888

@@ -111,7 +111,7 @@ async def test_search_users_as_admin_pre_registered_user(
111111
pre_registration_details = pre_registered_user_created["details"]
112112

113113
# Act
114-
found_users = await _users_service.search_users_as_admin(
114+
found_users = await _users_service.search_users_accounts(
115115
app, email_glob=pre_registered_email, product_name=product_name
116116
)
117117

@@ -164,7 +164,7 @@ async def test_search_users_as_admin_wildcard(
164164
)
165165

166166
# Act - search with wildcard for the domain
167-
found_users = await _users_service.search_users_as_admin(
167+
found_users = await _users_service.search_users_accounts(
168168
app, email_glob=f"*{email_domain}", product_name=product_name
169169
)
170170

0 commit comments

Comments
 (0)