@@ -268,75 +268,55 @@ async def get_role(
268268 content = response_formatter .buildErrorResponse ('Access denied' ),
269269 )
270270 item_to_select = select_item .split (',' ) if select_item else []
271- valid_columns = []
272271 for item in item_to_select :
273- if not getattr (Role , item ):
272+ if not hasattr (Role , item ):
274273 return JSONResponse (
275274 status_code = status .HTTP_400_BAD_REQUEST ,
276275 content = response_formatter .buildErrorResponse (
277276 error = f'Invalid column { item } '
278277 ),
279278 )
280- valid_columns .append (getattr (Role , item ))
281279
282- search_filter = None
280+ # Roles having at least one resource in the requested scopes. The IN-subquery
281+ # keeps the outer query join-free, so each role is returned exactly once.
282+ filters = [
283+ Role .id .in_ (
284+ select (RoleResource .role_id )
285+ .join (Resource , Resource .id == RoleResource .resource_id )
286+ .where (Resource .scope .in_ (scopes ))
287+ )
288+ ]
283289 if search and search .strip ():
284290 term = f'%{ search .strip ()} %'
285- search_filter = or_ (Role .name .ilike (term ), Role .description .ilike (term ))
291+ filters . append ( or_ (Role .name .ilike (term ), Role .description .ilike (term ) ))
286292
287293 async with role_repository .session () as session :
288- if valid_columns :
289- statement = select (Role ).options (selectinload (Role .resources ))
290- count_statement = select (func .count ()).select_from (Role )
291- if search_filter is not None :
292- statement = statement .where (search_filter )
293- count_statement = count_statement .where (search_filter )
294-
295- total = (await session .execute (count_statement )).scalar () or 0
296-
297- statement = statement .offset (offset )
298- if limit is not None :
299- statement = statement .limit (limit )
300- result = await session .execute (statement )
301- roles = result .scalars ().unique ().all ()
302- data = []
303- for role in roles :
304- role_dict = {}
305- for col in item_to_select :
306- if col == 'resources' :
307- role_dict [col ] = [
308- resource .to_dict () for resource in role .resources
309- ]
310- else :
311- role_dict [col ] = str (getattr (role , col ))
312- data .append (role_dict )
313- else :
314- statement = (
315- select (Role )
316- .join (RoleResource , Role .id == RoleResource .role_id )
317- .join (Resource , Resource .id == RoleResource .resource_id )
318- .where (Resource .scope .in_ (scopes ))
319- .distinct ()
294+ total = (
295+ await session .execute (
296+ select (func .count ()).select_from (Role ).where (* filters )
320297 )
321- count_statement = (
322- select (func .count (func .distinct (Role .id )))
323- .select_from (Role )
324- .join (RoleResource , Role .id == RoleResource .role_id )
325- .join (Resource , Resource .id == RoleResource .resource_id )
326- .where (Resource .scope .in_ (scopes ))
327- )
328- if search_filter is not None :
329- statement = statement .where (search_filter )
330- count_statement = count_statement .where (search_filter )
331-
332- total = (await session .execute (count_statement )).scalar () or 0
333-
334- statement = statement .offset (offset )
335- if limit is not None :
336- statement = statement .limit (limit )
337- result : Result = await session .execute (statement )
338- roles = result .scalars ().unique ().all ()
339-
298+ ).scalar () or 0
299+
300+ statement = select (Role ).where (* filters )
301+ if 'resources' in item_to_select :
302+ statement = statement .options (selectinload (Role .resources ))
303+ statement = statement .offset (offset )
304+ if limit is not None :
305+ statement = statement .limit (limit )
306+
307+ roles = (await session .execute (statement )).scalars ().all ()
308+
309+ if item_to_select :
310+ data = [
311+ {
312+ col : [resource .to_dict () for resource in role .resources ]
313+ if col == 'resources'
314+ else str (getattr (role , col ))
315+ for col in item_to_select
316+ }
317+ for role in roles
318+ ]
319+ else :
340320 data = [role .to_dict () for role in roles ]
341321
342322 return JSONResponse (
0 commit comments