using AsbCloudApp.Comparators; using AsbCloudApp.Data; using AsbCloudApp.Exceptions; using AsbCloudApp.Repositories; using AsbCloudDb; using AsbCloudDb.Model; using AsbCloudInfrastructure.EfCache; using DocumentFormat.OpenXml.Drawing; using Mapster; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace AsbCloudInfrastructure.Repository { #nullable enable public class UserRoleRepository : IUserRoleRepository { private readonly IAsbCloudDbContext dbContext; private const string userRoleCacheTag = "UserRole"; private const string relationUserRoleUserRoleCacheTag = "RelationUserRoleUserRole"; private const string relationUserRolePermissionsCacheTag = "RelationUserRolePermissions"; private static readonly TimeSpan relationCacheObsolence = TimeSpan.FromMinutes(15); public UserRoleRepository(IAsbCloudDbContext dbContext) { this.dbContext = dbContext; } public async Task InsertAsync(UserRoleDto dto, CancellationToken token) { var entity = dto.Adapt(); var updatedEntity = await dbContext.UserRoles.AddAsync(entity, token) .ConfigureAwait(false); dto.Id = updatedEntity.Entity.Id; await UpdatePermissionsAsync(dto, token); await UpdateIncludedRolesAsync(dto, token); await dbContext.SaveChangesAsync(token); DropCacheUserRole(); return updatedEntity?.Entity?.Id ?? 0; } public Task InsertRangeAsync(IEnumerable newItems, CancellationToken token) { throw new NotImplementedException(); } public async Task> GetAllAsync(CancellationToken token) { var dtos = await GetCacheUserRoleAsync(token) .ConfigureAwait(false); if (dtos is null) return Enumerable.Empty(); return dtos.Select(Convert); } public UserRoleDto? GetOrDefault(int id) { var dto = GetCacheUserRole().FirstOrDefault(x => x.Id == id); if (dto is null) return null; return Convert(dto); } public async Task GetOrDefaultAsync(int id, CancellationToken token) { var dto = (await GetCacheUserRoleAsync(token) .ConfigureAwait(false)).FirstOrDefault(r => r.Id == id); if (dto is null) return null; return Convert(dto); } public async Task> GetByNamesAsync(IEnumerable names, CancellationToken token) { if (names?.Any() != true) return Enumerable.Empty(); var dtos = (await GetCacheUserRoleAsync(token)) .Where(r => names.Contains(r.Caption)); if (dtos?.Count() != names.Count()) throw new ArgumentInvalidException("Invalid role names", nameof(names)); return dtos.Select(Convert); } public async Task UpdateAsync(UserRoleDto dto, CancellationToken token) { var entity = Convert(dto); await UpdatePermissionsAsync(dto, token); await UpdateIncludedRolesAsync(dto, token); var result = dbContext.UserRoles.Upsert(entity); await dbContext.SaveChangesAsync(token); DropCacheUserRole(); return result?.Entity?.Id ?? 0; } public IEnumerable GetNestedById(int id, int recursionLevel = 7) { var role = GetCacheUserRole() .FirstOrDefault(r => r.Id == id); if (role is null) return Enumerable.Empty(); var roles = new SortedSet(ComparerIId.GetInstance()) { Convert(role) }; if (recursionLevel <= 0 || role.RelationUserRoleUserRoles?.Any() != true) return roles; foreach (var relation in role.RelationUserRoleUserRoles) { var nestedRoles = GetNestedById(relation.IdInclude, --recursionLevel); if (nestedRoles?.Any() != true) continue; foreach (var nestedRole in nestedRoles) roles.Add(nestedRole); } return roles; } public async Task DeleteAsync(int id, CancellationToken token) { var dto = (await GetCacheUserRoleAsync(token)).FirstOrDefault(r => r.Id == id); if (dto is not null) { var entity = Convert(dto); var removeEntity = dbContext.UserRoles.Remove(Convert(entity)); await dbContext.SaveChangesAsync(token); DropCacheUserRole(); return removeEntity?.Entity?.Id ?? 0; } else return 0; } public async Task DeleteAsync(IEnumerable ids, CancellationToken token) { var entities = (await GetCacheUserRoleAsync(token)).Where(r => ids.Contains(r.Id)); if (entities is not null) { var count = entities.Count(); dbContext.UserRoles.RemoveRange(entities); await dbContext.SaveChangesAsync(token); DropCacheUserRole(); return count; } else return 0; } public bool HasPermission(IEnumerable rolesIds, string permissionName) { var permissionInfo = GetCacheRelationUserRolePermissions() .FirstOrDefault(p => p. Permission?.Name.ToLower() == permissionName.ToLower()) ?.Permission; if (permissionInfo is null) return false; if (rolesIds.Contains(1)) return true; var idPermissionInfo = permissionInfo.Id; var dtos = GetCacheUserRole() .Where(r => rolesIds.Contains(r.Id)); var roles = dtos.Select(Convert); foreach (var role in roles) if (HasPermission(Convert(role), idPermissionInfo)) return true; return false; } private bool HasPermission(UserRole userRole, int idPermission, int recursionLevel = 7) { if (userRole.RelationUserRolePermissions.Any(p => p.IdPermission == idPermission)) return true; if (recursionLevel <= 0 || userRole.RelationUserRoleUserRoles?.Any() != true) return false; foreach (var relation in userRole.RelationUserRoleUserRoles) { var dto = GetCacheUserRole() .First(p => p.Id == relation.IdInclude); var includedRole = Convert(dto); if (HasPermission(Convert(includedRole), idPermission, --recursionLevel)) return true; } return false; } private async Task UpdateIncludedRolesAsync(UserRoleDto dto, CancellationToken token) { if (dto?.Roles is null) return; var relations = (await GetCacheRelationUserRoleUserRoleAsync(token).ConfigureAwait(false)) .Where(r => r.Id == dto.Id); dbContext.RelationUserRoleUserRoles.RemoveRange(relations); if (dto.Roles.Any()) { var newRelations = dto.Roles.Select(r => new RelationUserRoleUserRole { Id = dto.Id, IdInclude = r.Id }); await dbContext.RelationUserRoleUserRoles.AddRangeAsync(newRelations, token); await dbContext.SaveChangesAsync(token); DropCacheRelationUserRoleUserRole(); } } private async Task UpdatePermissionsAsync(UserRoleDto dto, CancellationToken token) { if (dto?.Permissions is null) return; var relations = (await GetCacheRelationUserRolePermissionsAsync(token).ConfigureAwait(false)) .Where(r => r.IdUserRole == dto.Id); dbContext.RelationUserRolePermissions.RemoveRange(relations); if (dto.Permissions.Any()) { var newRelations = dto.Permissions.Select(p => new RelationUserRolePermission { IdPermission = p.Id, IdUserRole = dto.Id, }); await dbContext.RelationUserRolePermissions.AddRangeAsync(newRelations, token); await dbContext.SaveChangesAsync(token); DropCacheRelationCompanyWell(); } } private Task> GetCacheUserRoleAsync(CancellationToken token) => dbContext.UserRoles .Include(r => r.RelationUserRolePermissions) .Include(r => r.RelationUserRoleUserRoles) .Include(r => r.RelationUsersUserRoles) .FromCacheAsync(userRoleCacheTag, relationCacheObsolence, token); private IEnumerable GetCacheUserRole() => dbContext.UserRoles .Include(r => r.RelationUserRolePermissions) .Include(r => r.RelationUserRoleUserRoles) .Include(r => r.RelationUsersUserRoles) .FromCache(userRoleCacheTag, relationCacheObsolence); private void DropCacheUserRole() => dbContext.RelationUserUserRoles.DropCache(userRoleCacheTag); private Task> GetCacheRelationUserRoleUserRoleAsync(CancellationToken token) => dbContext.RelationUserRoleUserRoles .Include(r => r.IncludeRole) .Include(r => r.Role) .FromCacheAsync(relationUserRoleUserRoleCacheTag, relationCacheObsolence, token); private IEnumerable GetCacheRelationUserRoleUserRole() => dbContext.RelationUserRoleUserRoles .Include(r => r.IncludeRole) .Include(r => r.Role) .FromCache(relationUserRoleUserRoleCacheTag, relationCacheObsolence); private void DropCacheRelationUserRoleUserRole() => dbContext.RelationUserUserRoles.DropCache(relationUserRoleUserRoleCacheTag); private Task> GetCacheRelationUserRolePermissionsAsync(CancellationToken token) => dbContext.RelationUserRolePermissions .Include(r => r.UserRole) .Include(r => r.Permission) .FromCacheAsync(relationUserRolePermissionsCacheTag, relationCacheObsolence, token); private IEnumerable GetCacheRelationUserRolePermissions() => dbContext.RelationUserRolePermissions .Include(r => r.UserRole) .Include(r => r.Permission) .FromCache(relationUserRolePermissionsCacheTag, relationCacheObsolence); private void DropCacheRelationCompanyWell() => dbContext.RelationUserRolePermissions.DropCache(relationUserRolePermissionsCacheTag); private UserRoleDto Convert(UserRole entity) { var dto = entity.Adapt(); if (entity.RelationUserRolePermissions?.Any() == true) { dto.Permissions = GetCacheRelationUserRolePermissions() .Where(r => entity.Id == r.IdUserRole) .Select(r => Convert(r.Permission)); } if (entity.RelationUserRoleUserRoles?.Any() == true) { var rolesCache = GetCacheUserRole(); dto.Roles = entity.RelationUserRoleUserRoles .Select(rel => Convert(rolesCache .First(r => r.Id == rel.IdInclude))) .ToArray(); } return dto; } private static PermissionDto Convert(Permission entity) { var dto = entity.Adapt(); return dto; } private static UserRole Convert(UserRoleDto dto) { var entity = dto.Adapt(); return entity; } } #nullable disable }