from typing import Optional, List, Tuple from fastapi.exceptions import RequestValidationError from backend.models.permission.models import PermissionDoc, RoleDoc from beanie import PydanticObjectId from datetime import datetime class PermissionHandler: def __init__(self): pass async def create_permission(self, permission_key: str, permission_name: str, description: Optional[str] = None) -> Optional[PermissionDoc]: """Create a new permission document""" if not permission_key or not permission_name: raise RequestValidationError("permission_key and permission_name are required.") # if exists. if await PermissionDoc.find_one( {str(PermissionDoc.permission_key): permission_key}) or await PermissionDoc.find_one( {str(PermissionDoc.permission_name): permission_name}): raise RequestValidationError("permission has already been created.") doc = PermissionDoc( permission_key=permission_key, permission_name=permission_name, description=description, created_at=datetime.now(), updated_at=datetime.now() ) await doc.insert() return doc async def update_permission(self, permission_id: PydanticObjectId, permission_key: Optional[str] = None, permission_name: Optional[str] = None, description: Optional[str] = None) -> Optional[ PermissionDoc]: """Update an existing permission document by id, ensuring permission_key is unique""" if not permission_id or not permission_key or not permission_name: raise RequestValidationError("permission_id, permission_key and permission_name is required.") doc = await PermissionDoc.get(permission_id) if not doc: raise RequestValidationError("Permission not found.") if doc.is_default: raise RequestValidationError("Default permission cannot be updated.") # Check for uniqueness (exclude self) conflict = await PermissionDoc.find_one({ "$and": [ {"_id": {"$ne": permission_id}}, {"$or": [ {str(PermissionDoc.permission_key): permission_key}, {str(PermissionDoc.permission_name): permission_name} ]} ] }) if conflict: raise RequestValidationError("Permission name or permission key already exists.") doc.permission_key = permission_key doc.permission_name = permission_name doc.description = description doc.updated_at = datetime.now() await doc.save() return doc async def query_permissions( self, permission_key: Optional[str] = None, permission_name: Optional[str] = None, skip: int = 0, limit: int = 10 ) -> Tuple[List[PermissionDoc], int]: """Query permissions with pagination and fuzzy search""" query = {} if permission_key: query[str(PermissionDoc.permission_key)] = {"$regex": permission_key, "$options": "i"} if permission_name: query[str(PermissionDoc.permission_name)] = {"$regex": permission_name, "$options": "i"} cursor = PermissionDoc.find(query) total = await cursor.count() docs = await cursor.skip(skip).limit(limit).to_list() return docs, total async def delete_permission(self, permission_id: PydanticObjectId) -> None: """Delete a permission document after checking if it is referenced by any role and is not default""" if not permission_id: raise RequestValidationError("permission_id is required.") # Check if any role references this permission role = await RoleDoc.find_one({"permission_ids": str(permission_id)}) if role: raise RequestValidationError("Permission is referenced by a role and cannot be deleted.") doc = await PermissionDoc.get(permission_id) if not doc: raise RequestValidationError("Permission not found.") # Check if the permission is default if doc.is_default: raise RequestValidationError("Default permission cannot be deleted.") await doc.delete()