using System.Dynamic; using AutoMapper; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Server.Data; using Server.Helpers; using Server.Models; using SharedModels.DataTransferObjects.Model; using SharedModels.QueryParameters; using SharedModels.QueryParameters.Objects; using SharedModels.Requests; namespace Server.Services; public class UserManagementService : IUserManagementService { private readonly UserManager _userManager; private readonly RoleManager _roleManager; private readonly IMapper _mapper; private readonly ISortHelper _userSortHelper; private readonly IDataShaper _userDataShaper; private readonly IPager _pager; public UserManagementService(IMapper mapper, UserManager userManager, RoleManager roleManager, ISortHelper userSortHelper, IDataShaper userDataShaper, IPager pager) { _mapper = mapper; _userManager = userManager; _roleManager = roleManager; _userSortHelper = userSortHelper; _userDataShaper = userDataShaper; _pager = pager; _userManager.UserValidators.Clear(); } public async Task<(bool isSucceeded, IActionResult actionResult, UserDto user)> AddUser(CreateUserDto createUserDto) { var user = _mapper.Map(createUserDto); user.BirthDate = user.BirthDate == null ? null : new DateTime(user.BirthDate.Value.Ticks, DateTimeKind.Utc); var userDto = _mapper.Map(user); userDto.Roles = new List(); if (await _userManager.FindByEmailAsync(user.Email) != null) { return (false, new BadRequestObjectResult("Email already registered"), null!); } if (user.PhoneNumber != null && await _userManager.Users.FirstOrDefaultAsync(u => u.PhoneNumber == user.PhoneNumber) != null) { return (false, new BadRequestObjectResult("Phone number already registered"), null!); } if (createUserDto.Roles != null!) { foreach (var role in createUserDto.Roles) { if (!await _roleManager.RoleExistsAsync(role)) { return (false, new BadRequestObjectResult($"Roles \"{role}\" doesn't exist"), null!); } userDto.Roles.Add(role); } } await _userManager.CreateAsync(user, createUserDto.Password); await _userManager.AddToRolesAsync(user, createUserDto.Roles); userDto.Id = user.Id; return (true, null!, userDto); } public async Task<(bool isSucceeded, IActionResult actionResult, IEnumerable users, PagingMetadata pagingMetadata)> GetUsers(UserParameters parameters) { var dbUsers = _userManager.Users.Include(u => u.Company) .Include(u => u.Reviews).Include(u => u.TicketGroups) .ThenInclude(tg => tg.Tickets).AsQueryable(); SearchByAllUserFields(ref dbUsers, parameters.Search); var userDtos = _mapper.ProjectTo(dbUsers); var shapedData = _userDataShaper.ShapeData(userDtos, parameters.Fields).AsQueryable(); try { shapedData = _userSortHelper.ApplySort(shapedData, parameters.Sort); } catch (Exception) { return (false, new BadRequestObjectResult("Invalid sorting string"), null!, null!); } var pagingMetadata = _pager.ApplyPaging(ref shapedData, parameters.PageNumber, parameters.PageSize); if ((bool)parameters.Fields?.Contains("roles")) { foreach (var user in shapedData) { dynamic dynamicUser = user as IDictionary; var roles = await _userManager.GetRolesAsync(new User { Id = dynamicUser.Id }); dynamicUser.Roles = roles; } } return (true, null!, shapedData, pagingMetadata); void SearchByAllUserFields(ref IQueryable users, string? search) { if (!users.Any() || search == null) { return; } users = users.Where(u => u.FirstName.ToLower().Contains(search.ToLower()) || u.LastName.ToLower().Contains(search.ToLower()) || u.Patronymic.ToLower().Contains(search.ToLower()) || u.Email.ToLower().Contains(search.ToLower()) || u.PhoneNumber.ToLower().Contains(search.ToLower())); } } public async Task<(bool isSucceeded, IActionResult actionResult, ExpandoObject user)> GetUser(string id, string? fields) { var dbUser = await _userManager.Users.Include(u => u.Employer). FirstOrDefaultAsync(u => u.Id == id); if (dbUser == null) { return (false, new NotFoundResult(), null!); } if (String.IsNullOrWhiteSpace(fields)) { fields = UserParameters.DefaultFields; } var userDto = _mapper.Map(dbUser); var shapedData = _userDataShaper.ShapeData(userDto, fields); return (true, null!, shapedData); } public async Task<(bool isSucceeded, IActionResult actionResult, UserDto user)> UpdateUser(string id, UpdateUserDto updateUserDto) { if (id != updateUserDto.Id) { return (false, new BadRequestObjectResult("Object and query ids don't match"), null!); } if (!await _userManager.Users.AnyAsync(u => u.Id == id)) { return (false, new NotFoundResult(), null!); } var dbUser = await _userManager.FindByIdAsync(id); var user = _mapper.Map(updateUserDto); dbUser.FirstName = user.FirstName; dbUser.LastName = user.LastName; dbUser.Patronymic = user.Patronymic; dbUser.BirthDate = new DateTime(user.BirthDate.Value.Ticks, DateTimeKind.Utc); dbUser.Gender = user.Gender; dbUser.Document = user.Document; dbUser.DocumentDetails = user.DocumentDetails; dbUser.Email = user.Email; dbUser.EmailConfirmed = user.EmailConfirmed; dbUser.PhoneNumber = user.PhoneNumber; dbUser.PhoneNumberConfirmed = user.PhoneNumberConfirmed; var userDto = _mapper.Map(user); userDto.Roles = new List(); updateUserDto.Roles = updateUserDto.Roles == null ? new List() : updateUserDto.Roles; var roles = await _userManager.GetRolesAsync(user); var rolesToDelete = roles.Except(updateUserDto.Roles).ToArray(); var rolesToAdd = updateUserDto.Roles.Except(roles).ToArray(); userDto.Roles = roles.Except(rolesToDelete).Union(rolesToAdd).ToList(); foreach (var role in rolesToDelete) { if (await _roleManager.RoleExistsAsync(role)) { await _userManager.RemoveFromRoleAsync(dbUser, role); } } foreach (var role in rolesToAdd) { if (!await _roleManager.RoleExistsAsync(role)) { return (false, new BadRequestObjectResult($"Roles \"{role}\" doesn't exist"), null!); } } await _userManager.AddToRolesAsync(dbUser, rolesToAdd); if (updateUserDto.Password != null) { await _userManager.RemovePasswordAsync(dbUser); await _userManager.AddPasswordAsync(dbUser, updateUserDto.Password); } await _userManager.UpdateAsync(dbUser); return (true, null!, userDto); } public async Task<(bool isSucceed, IActionResult actionResult)> DeleteUser(string id) { var dbUser = await _userManager.FindByIdAsync(id); if (dbUser == null) { return (false, new NotFoundResult()); } await _userManager.DeleteAsync(dbUser); return (true, null!); } }