DD.WellWorkover.Cloud/AsbCloudInfrastructure/Repository/UserRepository.cs

249 lines
9.8 KiB
C#
Raw Normal View History

using AsbCloudApp.Data;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudDb;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.EfCache;
using Mapster;
2022-10-27 15:44:04 +05:00
using Microsoft.AspNetCore.Mvc;
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 UserRepository : IUserRepository
{
private readonly IAsbCloudDbContext dbContext;
private readonly IUserRoleRepository userRoleRepository;
private const string userCacheTag = "User";
private const string relationUserUserRoleCacheTag = "RelationUserUserRole";
private static readonly TimeSpan cacheObsolence = TimeSpan.FromMinutes(15);
private static readonly TypeAdapterConfig userTypeAdapterConfig = TypeAdapterConfig<UserExtendedDto, User>
.NewConfig()
.Ignore(dst => dst.Company,
dst => dst.FileMarks,
dst => dst.Files,
dst => dst.RelationUsersUserRoles)
.Config;
public UserRepository(IAsbCloudDbContext dbContext, IUserRoleRepository userRoleRepository) {
this.dbContext = dbContext;
this.userRoleRepository = userRoleRepository;
}
public async Task<int> InsertAsync(UserExtendedDto dto, CancellationToken token = default)
{
dto.Id = default;
var entity = Convert(dto);
await AssertLoginIsBusyAsync(dto.Login, token);
var userRoles = await userRoleRepository.GetByNamesAsync(dto.RoleNames, token).ConfigureAwait(false);
var updatedEntity = await dbContext.Users.AddAsync(entity, token).ConfigureAwait(false);
if (userRoles?.Any() == true)
await UpdateRolesCacheForUserAsync(updatedEntity.Entity.Id, userRoles, token);
await dbContext.SaveChangesAsync(token);
DropCacheUsers();
return updatedEntity?.Entity?.Id ?? 0;
}
public Task<int> InsertRangeAsync(IEnumerable<UserExtendedDto> newItems, CancellationToken token = default)
{
throw new NotImplementedException();
}
public async Task<IEnumerable<UserExtendedDto>> GetAllAsync(CancellationToken token = default)
{
2022-10-27 15:44:04 +05:00
var dtos = (await GetCacheUserAsync(token)).ToList();
if (dtos is null)
return Enumerable.Empty<UserExtendedDto>();
for (var i = 0; i < dtos.Count; i++)
dtos[i].RoleNames = GetRolesNamesByIdUser(dtos[i].Id);
return dtos;
}
public UserExtendedDto? GetOrDefault(int id)
{
2022-10-27 15:44:04 +05:00
var dto = GetCacheUser().FirstOrDefault(u => u.Id == id);
if (dto is null)
return null;
2022-10-27 15:44:04 +05:00
var entity = Convert(dto);
dto.RoleNames = GetRolesNamesByIdUser(dto.Id);
return dto;
}
public async Task<UserExtendedDto?> GetOrDefaultAsync(int id, CancellationToken token = default)
{
2022-10-27 15:44:04 +05:00
var dto = (await GetCacheUserAsync(token)).FirstOrDefault(u => u.Id == id);
if (dto is null)
return null;
2022-10-27 15:44:04 +05:00
dto.RoleNames = GetRolesNamesByIdUser(dto.Id);
return dto;
}
public async Task<int> UpdateAsync(UserExtendedDto dto, CancellationToken token = default)
{
if (dto.Id <= 1)
throw new ArgumentInvalidException($"Invalid id {dto.Id}. You can't edit this user.", nameof(dto));
var oldUser = (await GetCacheUserAsync(token)).FirstOrDefault(u => u.Id == dto.Id);
if (oldUser is null)
return 0;
if (oldUser.Login != dto.Login)
await AssertLoginIsBusyAsync(dto.Login, token);
var userRoles = await userRoleRepository.GetByNamesAsync(dto.RoleNames, token).ConfigureAwait(false);
if (userRoles is not null)
await UpdateRolesCacheForUserAsync(dto.Id, userRoles, token);
var entity = Convert(dto);
var result = dbContext.Users.Upsert(entity);
await dbContext.SaveChangesAsync(token);
DropCacheUsers();
return result?.Entity?.Id ?? 0;
}
public async Task<int> DeleteAsync(int id, CancellationToken token = default)
{
2022-10-27 15:44:04 +05:00
var dto = (await GetCacheUserAsync(token)).FirstOrDefault(u => u.Id == id);
if (dto is null)
return 0;
2022-10-27 15:44:04 +05:00
var entity = Convert(dto);
var result = dbContext.Users.Remove(entity);
await dbContext.SaveChangesAsync(token);
DropCacheUsers();
return result?.Entity?.Id ?? 0;
}
public async Task<int> DeleteAsync(IEnumerable<int> ids, CancellationToken token = default)
{
2022-10-27 15:44:04 +05:00
var dto = (await GetCacheUserAsync(token)).Where(r => ids.Contains(r.Id));
if (dto is null)
return 0;
2022-10-27 15:44:04 +05:00
var count = dto.Count();
var entities = dto.Select(Convert);
dbContext.Users.RemoveRange(entities);
await dbContext.SaveChangesAsync(token);
DropCacheUsers();
return count;
}
2022-10-27 14:18:59 +05:00
public IEnumerable<UserRoleDto> GetRolesByIdUser(int idUser, int nestedLevel = 0)
{
var roles = GetCachRelationUserUserRoleCacheTag().Where(r => r.IdUser == idUser);
if (roles is null)
2022-10-27 14:18:59 +05:00
return Enumerable.Empty<UserRoleDto>();
return roles.SelectMany(r => userRoleRepository.GetNestedById(r.IdUserRole, nestedLevel));
}
2022-10-27 14:18:59 +05:00
public IEnumerable<PermissionDto> GetNestedPermissions(int idUser)
{
var roles = GetRolesByIdUser(idUser, 7);
if (roles is null)
2022-10-27 14:18:59 +05:00
return Enumerable.Empty<PermissionDto>(); ;
var permissions = roles
.Where(r => r.Permissions is not null)
.SelectMany(r => r.Permissions);
return permissions;
}
public bool HasPermission(int idUser, string permissionName)
{
if (idUser == 1)
return true;
2022-10-27 14:18:59 +05:00
var relationsToRoles = GetCachRelationUserUserRoleCacheTag()
.Where(r => r.IdUser == idUser);
if (relationsToRoles is null)
return false;
2022-10-27 14:18:59 +05:00
return userRoleRepository.HasPermission(relationsToRoles
.Select(r => r.IdUserRole), permissionName);
}
private IEnumerable<string>? GetRolesNamesByIdUser(int idUser)
=> GetRolesByIdUser(idUser)
?.Select(r => r.Caption)
.Distinct();
private async Task AssertLoginIsBusyAsync(string login, CancellationToken token = default)
{
2022-10-27 15:44:04 +05:00
var existingUserDto = (await GetCacheUserAsync(token))
.FirstOrDefault(u => u.Login.ToLower() == login.ToLower());
2022-10-27 15:44:04 +05:00
var existingUser = Convert(existingUserDto);
if (existingUser is not null)
throw new ArgumentInvalidException($"Login {login} is busy by {existingUser.MakeDisplayName()}, id{existingUser.Id}", nameof(login));
}
2022-10-27 15:44:04 +05:00
private Task<IEnumerable<UserExtendedDto>> GetCacheUserAsync(CancellationToken token)
=> dbContext.Users
.Include(r => r.Company)
.Include(r => r.RelationUsersUserRoles)
2022-10-27 15:44:04 +05:00
.FromCacheAsync(userCacheTag, cacheObsolence, Convert, token);
private IEnumerable<UserExtendedDto> GetCacheUser()
=> dbContext.Users
.Include(r => r.Company)
.Include(r => r.RelationUsersUserRoles)
2022-10-27 15:44:04 +05:00
.FromCache(userCacheTag, cacheObsolence, Convert);
private void DropCacheUsers()
=> dbContext.Users.DropCache(userCacheTag);
private Task<IEnumerable<RelationUserUserRole>> GetCacheRelationUserUserRoleAsync(CancellationToken token)
=> dbContext.RelationUserUserRoles
.Include(r => r.UserRole)
.Include(r => r.User)
.FromCacheAsync(relationUserUserRoleCacheTag, cacheObsolence, token);
private IEnumerable<RelationUserUserRole> GetCachRelationUserUserRoleCacheTag()
=> dbContext.RelationUserUserRoles
.Include(r => r.UserRole)
.Include(r => r.User)
.FromCache(relationUserUserRoleCacheTag, cacheObsolence);
private void DropCacheRelationUserUserRoleCacheTag()
=> dbContext.RelationUserUserRoles.DropCache(relationUserUserRoleCacheTag);
private async Task UpdateRolesCacheForUserAsync(int idUser, IEnumerable<UserRoleDto> newRoles, CancellationToken token)
{
var relations = (await GetCacheRelationUserUserRoleAsync(token)).Where(r => r.IdUser == idUser);
dbContext.RelationUserUserRoles.RemoveRange(relations);
if (newRoles?.Any() == true)
await dbContext.RelationUserUserRoles.AddRangeAsync(newRoles.Select(role => new RelationUserUserRole
{
IdUser = idUser,
IdUserRole = role.Id
}), token).ConfigureAwait(false);
await dbContext.SaveChangesAsync(token);
DropCacheRelationUserUserRoleCacheTag();
}
protected virtual User Convert(UserExtendedDto dto)
{
var entity = dto.Adapt<User>(userTypeAdapterConfig);
if (string.IsNullOrEmpty(entity.PasswordHash))
entity.PasswordHash = dbContext.Users.FirstOrDefault(u => u.Id == dto.Id)?.PasswordHash;
return entity;
}
protected virtual UserExtendedDto Convert(User entity)
{
var dto = entity.Adapt<UserExtendedDto>();
return dto;
}
}
#nullable disable
}