"""Utility Functions for Roles.""" from typing import List from typing_extensions import Self from pdp.fastapi.schemas.identity import Role class RolesListHelper: """Helper for common operations performed on a list of Roles.""" def __init__(self, roles: List[Role]): """Init method.""" self._roles = roles def attach(self, roles_to_attach: List[Role]) -> Self: """Attach roles.""" self._roles = attach_roles(self._roles, roles_to_attach) return self def detach(self, roles_to_detach: List[Role]) -> Self: """Detach roles.""" self._roles = detach_roles(self._roles, roles_to_detach) return self def get_roles_list(self) -> List[Role]: """Get roles.""" return self._roles def attach_roles(existing_roles: List[Role], roles_to_attach: List[Role]) -> List[Role]: """Add roles to an existing set of roles. Args: existing_roles (list): List of Role objects roles_to_attach (list): List of Role objects to be added/updated Returns: list: List of Role objects to keep/update Note: If both lists have the same role.role, the Role from `roles_to_attach` will be returned in the final list. """ # Transform existing_roles into dictionary for easy indexing existing_roles_dict = {role.role: role for role in existing_roles} # Add or replace any roles from roles_to_add list for role in roles_to_attach: existing_roles_dict[role.role] = role # Turn existing_roles_dict back into list return list(existing_roles_dict.values()) def detach_roles(existing_roles: List[Role], roles_to_detach: List[Role]) -> List[Role]: """Remove roles from an existing list of roles. Args: existing_roles (list): List of Role objects roles_to_delete (list): List of Role objects to be removed Returns: list: List of Role objects to keep/update """ # Transform existing_roles into dictionary for easy indexing existing_roles_dict = {role.role: role for role in existing_roles} # Remove any role that is in roles_to_detach for role in roles_to_detach: existing_roles_dict.pop(role.role, None) # Turn existing_roles_dict back into list return list(existing_roles_dict.values()) def is_new_list_different(old_roles: List[Role], new_roles: List[Role]) -> bool: """Compare two lists of roles for equality. Use this function to determine if an update operation is necessary. Args: old_roles (list): An old list of Role objects new_roles (list): A new list of Role objects Returns: boolean: True if `old_roles` and `new_roles` are different """ # First if the counts are different, they are inherently different if len(old_roles) != len(new_roles): return True filtered_old_roles = filter(lambda role: role in old_roles, new_roles) filtered_old_roles_dict = {role.role: role for role in filtered_old_roles} new_roles_dict = {role.role: role for role in new_roles} return filtered_old_roles_dict != new_roles_dict