1+ from datetime import datetime
12from uuid import UUID
23
3- from sqlalchemy import delete , select
4+ from sqlalchemy import and_ , delete , or_ , select
45from sqlalchemy .ext .asyncio import AsyncSession
56
7+ from src .core .utils .cursor import CursorDirection
68from src .core .authorization .infrastructure .models .casbin_rule_model import (
79 CasbinRuleModel ,
810)
1214from src .core .authorization .infrastructure .models .resource_model import (
1315 AuthorizationResourceModel ,
1416)
17+ from src .core .authorization .infrastructure .models .role_model import RoleModel
1518from src .core .authorization .infrastructure .models .role_permission_model import (
1619 RolePermissionModel ,
1720)
18- from src .core .authorization .infrastructure .models .role_model import RoleModel
1921from src .core .authorization .infrastructure .models .user_has_role_model import (
2022 UserHasRoleModel ,
2123)
@@ -122,10 +124,7 @@ async def create_resource(
122124
123125 async def list_resources (self ) -> list [AuthorizationResource ]:
124126 result = await self ._db .execute (select (AuthorizationResourceModel ))
125- return [
126- self ._resource_from_model (model )
127- for model in result .scalars ().all ()
128- ]
127+ return [self ._resource_from_model (model ) for model in result .scalars ().all ()]
129128
130129 async def create_role (self , role : Role ) -> Role :
131130 model = RoleModel (
@@ -138,7 +137,9 @@ async def create_role(self, role: Role) -> Role:
138137 return self ._role_from_model (model )
139138
140139 async def get_role (self , role_id : UUID ) -> Role | None :
141- result = await self ._db .execute (select (RoleModel ).where (RoleModel .id == role_id ))
140+ result = await self ._db .execute (
141+ select (RoleModel ).where (RoleModel .id == role_id )
142+ )
142143 model = result .scalar_one_or_none ()
143144 if model is None :
144145 return None
@@ -148,8 +149,36 @@ async def list_roles(self) -> list[Role]:
148149 result = await self ._db .execute (select (RoleModel ))
149150 return [self ._role_from_model (model ) for model in result .scalars ().all ()]
150151
152+ async def list_roles_cursor (
153+ self ,
154+ cursor_created_at : datetime | None = None ,
155+ cursor_id : UUID | None = None ,
156+ limit : int = 10 ,
157+ direction : CursorDirection = CursorDirection .DIRECTION_NEXT ,
158+ ) -> tuple [list [Role ], bool ]:
159+ query = select (RoleModel )
160+ query = self ._apply_cursor_pagination (
161+ query ,
162+ RoleModel ,
163+ cursor_created_at ,
164+ cursor_id ,
165+ direction ,
166+ ).limit (limit + 1 )
167+
168+ result = await self ._db .execute (query )
169+ models = list (result .scalars ().all ())
170+ has_more = len (models ) > limit
171+ models = models [:limit ]
172+
173+ if direction == CursorDirection .DIRECTION_PREV :
174+ models = list (reversed (models ))
175+
176+ return [self ._role_from_model (model ) for model in models ], has_more
177+
151178 async def update_role (self , role : Role ) -> Role | None :
152- result = await self ._db .execute (select (RoleModel ).where (RoleModel .id == role .id ))
179+ result = await self ._db .execute (
180+ select (RoleModel ).where (RoleModel .id == role .id )
181+ )
153182 model = result .scalar_one_or_none ()
154183 if model is None :
155184 return None
@@ -209,10 +238,33 @@ async def get_permission(self, permission_id: UUID) -> Permission | None:
209238
210239 async def list_permissions (self ) -> list [Permission ]:
211240 result = await self ._db .execute (select (PermissionModel ))
212- return [
213- self ._permission_from_model (model )
214- for model in result .scalars ().all ()
215- ]
241+ return [self ._permission_from_model (model ) for model in result .scalars ().all ()]
242+
243+ async def list_permissions_cursor (
244+ self ,
245+ cursor_created_at : datetime | None = None ,
246+ cursor_id : UUID | None = None ,
247+ limit : int = 10 ,
248+ direction : CursorDirection = CursorDirection .DIRECTION_NEXT ,
249+ ) -> tuple [list [Permission ], bool ]:
250+ query = select (PermissionModel )
251+ query = self ._apply_cursor_pagination (
252+ query ,
253+ PermissionModel ,
254+ cursor_created_at ,
255+ cursor_id ,
256+ direction ,
257+ ).limit (limit + 1 )
258+
259+ result = await self ._db .execute (query )
260+ models = list (result .scalars ().all ())
261+ has_more = len (models ) > limit
262+ models = models [:limit ]
263+
264+ if direction == CursorDirection .DIRECTION_PREV :
265+ models = list (reversed (models ))
266+
267+ return [self ._permission_from_model (model ) for model in models ], has_more
216268
217269 async def update_permission (self , permission : Permission ) -> Permission | None :
218270 result = await self ._db .execute (
@@ -287,9 +339,13 @@ async def list_role_permissions(self) -> list[tuple[str, str]]:
287339 result = await self ._db .execute (
288340 select (RoleModel .name , PermissionModel .key )
289341 .join (RolePermissionModel , RolePermissionModel .role_id == RoleModel .id )
290- .join (PermissionModel , PermissionModel .id == RolePermissionModel .permission_id )
342+ .join (
343+ PermissionModel , PermissionModel .id == RolePermissionModel .permission_id
344+ )
291345 )
292- return [(role_name , permission_key ) for role_name , permission_key in result .all ()]
346+ return [
347+ (role_name , permission_key ) for role_name , permission_key in result .all ()
348+ ]
293349
294350 async def remove_permission_from_role (
295351 self ,
@@ -319,7 +375,9 @@ def _to_policy_tuple(self, rule: CasbinRuleModel) -> tuple[str, ...]:
319375 populated = [value for value in values if value is not None ]
320376 return (rule .ptype , * populated )
321377
322- async def _get_or_create_resource (self , resource_key : str ) -> AuthorizationResourceModel :
378+ async def _get_or_create_resource (
379+ self , resource_key : str
380+ ) -> AuthorizationResourceModel :
323381 result = await self ._db .execute (
324382 select (AuthorizationResourceModel ).where (
325383 AuthorizationResourceModel .key == resource_key ,
@@ -384,6 +442,8 @@ def _role_from_model(self, model: RoleModel) -> Role:
384442 id = model .id ,
385443 name = model .name ,
386444 description = model .description ,
445+ created_at = model .created_at .isoformat (),
446+ updated_at = model .updated_at .isoformat (),
387447 )
388448
389449 def _resource_from_model (
@@ -404,4 +464,40 @@ def _permission_from_model(self, model: PermissionModel) -> Permission:
404464 resource = model .resource ,
405465 action = model .action ,
406466 description = model .description ,
467+ created_at = model .created_at .isoformat (),
468+ updated_at = model .updated_at .isoformat (),
407469 )
470+
471+ def _apply_cursor_pagination (
472+ self ,
473+ query ,
474+ model ,
475+ cursor_created_at : datetime | None ,
476+ cursor_id : UUID | None ,
477+ direction : CursorDirection ,
478+ ):
479+ if cursor_created_at and cursor_id :
480+ if direction == CursorDirection .DIRECTION_NEXT :
481+ query = query .where (
482+ or_ (
483+ model .created_at < cursor_created_at ,
484+ and_ (
485+ model .created_at == cursor_created_at ,
486+ model .id < cursor_id ,
487+ ),
488+ )
489+ )
490+ return query .order_by (model .created_at .desc (), model .id .desc ())
491+
492+ query = query .where (
493+ or_ (
494+ model .created_at > cursor_created_at ,
495+ and_ (
496+ model .created_at == cursor_created_at ,
497+ model .id > cursor_id ,
498+ ),
499+ )
500+ )
501+ return query .order_by (model .created_at .asc (), model .id .asc ())
502+
503+ return query .order_by (model .created_at .desc (), model .id .desc ())
0 commit comments