From dc3fb100b1b07bfb21041f784741df0a70afddd7 Mon Sep 17 00:00:00 2001 From: OlgunR Date: Tue, 17 Sep 2024 16:54:38 +0200 Subject: [PATCH] FlexibleQueries+GetUsersByRole --- .../Controllers/AuthController.cs | 2 +- .../Controllers/UserController.cs | 30 +++++++--- .../Dtos/Outgoing/ReadingUserRolesDto.cs | 1 + UserManagement.App/Interfaces/IUserService.cs | 12 ++-- .../MappingProfiles/BasicDtoMappingProfile.cs | 4 +- UserManagement.App/Services/AuthService.cs | 4 +- UserManagement.App/Services/UserService.cs | 41 +++++-------- UserManagement.Domain/Entities/Role.cs | 2 +- UserManagement.Domain/Entities/User.cs | 8 +-- .../Interfaces/IUserRepository.cs | 12 ++-- .../Repositories/UserRepository.cs | 57 ++++++++++++------- 11 files changed, 94 insertions(+), 79 deletions(-) diff --git a/UserManagement.API/Controllers/AuthController.cs b/UserManagement.API/Controllers/AuthController.cs index 13fc241..068d648 100644 --- a/UserManagement.API/Controllers/AuthController.cs +++ b/UserManagement.API/Controllers/AuthController.cs @@ -29,7 +29,7 @@ namespace UserManagement.API.Controllers public async Task Login([FromBody] LoginDto login) { // Validate user - var user = await _userService.GetByUsernameAsync(login.Username); + var user = await _userService.GetByUsernameAsync(login.Username, includeRoles: true); if (user is null) { return Unauthorized(); diff --git a/UserManagement.API/Controllers/UserController.cs b/UserManagement.API/Controllers/UserController.cs index 9dff221..111fa90 100644 --- a/UserManagement.API/Controllers/UserController.cs +++ b/UserManagement.API/Controllers/UserController.cs @@ -25,7 +25,7 @@ namespace UserManagement.API.Controllers [ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] - //[Authorize(Roles = "Admin")] + [Authorize(Roles = "Admin")] public async Task CreateUser([FromBody] CreatingUserDto creatingUserDto) { // Validate incomming model @@ -63,9 +63,9 @@ namespace UserManagement.API.Controllers [HttpGet] [SwaggerOperation(Summary = "Get all Users")] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task GetAllUsers() + public async Task GetAllUsers([FromQuery] bool includeRoles = true) { - var users = await _userService.GetUsersAsync(); + var users = await _userService.GetUsersAsync(includeRoles); return Ok(users); } @@ -75,13 +75,13 @@ namespace UserManagement.API.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetUserById(int id) + public async Task GetUserById(int id, [FromQuery] bool includeRoles = true) { if (id <= 0) { return BadRequest("Invalid Id"); } - var user = await _userService.GetByIdAsync(id); + var user = await _userService.GetByIdAsync(id, includeRoles); if (user == null) { return NotFound(); @@ -95,13 +95,13 @@ namespace UserManagement.API.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetUserByUsername(string username) + public async Task GetUserByUsername(string username, [FromQuery] bool includeRoles = true) { if (string.IsNullOrEmpty(username)) { return BadRequest("Username connot be empty"); } - var user = await _userService.GetByUsernameAsync(username); + var user = await _userService.GetByUsernameAsync(username, includeRoles); if (user == null) { return NotFound(); @@ -109,6 +109,22 @@ namespace UserManagement.API.Controllers return Ok(user); } + // READ BY ROLE + [HttpGet("role/{role}", Name = "GetUsersByRole")] + [SwaggerOperation(Summary = "Get Users by Role")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task GetUsersByRole(string role) + { + if (string.IsNullOrEmpty(role)) + { + return BadRequest("Role cannot be empty"); + } + var users = await _userService.GetByRoleAsync(role); + return Ok(users); + } + // UPDATE [HttpPut("id/{id}", Name = "UpdateUser")] [SwaggerOperation(Summary = "Update User")] diff --git a/UserManagement.App/Dtos/Outgoing/ReadingUserRolesDto.cs b/UserManagement.App/Dtos/Outgoing/ReadingUserRolesDto.cs index 0e7ece4..0bd3541 100644 --- a/UserManagement.App/Dtos/Outgoing/ReadingUserRolesDto.cs +++ b/UserManagement.App/Dtos/Outgoing/ReadingUserRolesDto.cs @@ -3,6 +3,7 @@ public class ReadingUserRolesDto { public int RoleId { get; set; } + public string RoleName { get; set; } } } diff --git a/UserManagement.App/Interfaces/IUserService.cs b/UserManagement.App/Interfaces/IUserService.cs index e165b97..3e7b3fd 100644 --- a/UserManagement.App/Interfaces/IUserService.cs +++ b/UserManagement.App/Interfaces/IUserService.cs @@ -10,20 +10,20 @@ namespace UserManagement.Application.Interfaces Task AddUserAsync(CreatingUserDto creatingUserDto); // READ ALL - Task> GetUsersAsync(); + Task> GetUsersAsync(bool includeRoles = true); // READ BY ID - Task GetByIdAsync(int id); + Task GetByIdAsync(int id, bool includeRoles = true); // READ BY USERNAME - Task GetByUsernameAsync(string username); + Task GetByUsernameAsync(string username, bool includeRoles = true); + + // READ BY ROLE + Task> GetByRoleAsync(string role); // UPDATE Task UpdateUserAsync(UpdatingUserDto updatingUserDto); - //// UPDATE USER ROLE - //Task UpdateUserRoleAsync(int userId, int roleId); - // DELETE Task DeleteUserAsync(int id); } diff --git a/UserManagement.App/MappingProfiles/BasicDtoMappingProfile.cs b/UserManagement.App/MappingProfiles/BasicDtoMappingProfile.cs index a4b6656..35a0068 100644 --- a/UserManagement.App/MappingProfiles/BasicDtoMappingProfile.cs +++ b/UserManagement.App/MappingProfiles/BasicDtoMappingProfile.cs @@ -33,9 +33,9 @@ namespace UserManagement.Application.MappingProfiles CreateMap() .ForMember(dest => dest.UserRoles, opt => opt.MapFrom(src => - src.UserRoles.Select(ur => new ReadingRoleDto + src.UserRoles!.Select(ur => new ReadingRoleDto { - Id = ur.Role.Id, + Id = ur.Role!.Id, Name = ur.Role.Name }).ToList())); diff --git a/UserManagement.App/Services/AuthService.cs b/UserManagement.App/Services/AuthService.cs index ba43155..2de0791 100644 --- a/UserManagement.App/Services/AuthService.cs +++ b/UserManagement.App/Services/AuthService.cs @@ -15,9 +15,9 @@ namespace UserManagement.Application.Services // AUTHENTICATE public async Task ValidateAsync(string username, string password) { - var user = await _userRepository.GetByUsernameAsync(username); + var user = await _userRepository.GetByUsernameAsync(username, includeRoles: true); - return BCrypt.Net.BCrypt.Verify(password, user.PasswordHash); + return BCrypt.Net.BCrypt.Verify(password, user!.PasswordHash); } } } diff --git a/UserManagement.App/Services/UserService.cs b/UserManagement.App/Services/UserService.cs index 53bc7d1..8f65282 100644 --- a/UserManagement.App/Services/UserService.cs +++ b/UserManagement.App/Services/UserService.cs @@ -50,29 +50,37 @@ namespace UserManagement.Application.Services } // READ ALL - public async Task> GetUsersAsync() + public async Task> GetUsersAsync(bool includeRoles = true) { - var users = await _userRepository.GetAllAsync(); + var users = await _userRepository.GetAllAsync(includeRoles); var readDto = _mapper.Map>(users); return readDto; } // READ BY ID - public async Task GetByIdAsync(int id) + public async Task GetByIdAsync(int id, bool includeRoles = true) { - var user = await _userRepository.GetByIdAsync(id); + var user = await _userRepository.GetByIdAsync(id, includeRoles); var readDto = _mapper.Map(user); return readDto; } // READ BY USERNAME - public async Task GetByUsernameAsync(string username) + public async Task GetByUsernameAsync(string username, bool includeRoles = true) { - var user = await _userRepository.GetByUsernameAsync(username); + var user = await _userRepository.GetByUsernameAsync(username, includeRoles); var readDto = _mapper.Map(user); return readDto; } + // READ BY ROLE + public async Task> GetByRoleAsync(string role) + { + var users = await _userRepository.GetByRoleAsync(role); + var readDto = _mapper.Map>(users); + return readDto; + } + // UPDATE public async Task UpdateUserAsync(UpdatingUserDto updatingUserDto) { @@ -90,7 +98,7 @@ namespace UserManagement.Application.Services user.PasswordHash = BCrypt.Net.BCrypt.HashPassword(updatingUserDto.Password); } - user.UserRoles.Clear(); + user.UserRoles!.Clear(); foreach(var roleId in updatingUserDto.RoleIds) { @@ -109,25 +117,6 @@ namespace UserManagement.Application.Services return isUpdated; } - //// UPDATE USER ROLE -- die Rolle eines Users aktualisieren - //public async Task UpdateUserRoleAsync(int userId, int roleId) - //{ - // var user = await _userRepository.GetByIdAsync(userId); - // if (user == null) - // { - // throw new ArgumentException("User not found"); - // } - - // var role = await _roleRepository.GetByIdAsync(roleId); - // if (role == null) - // { - // throw new ArgumentException("Role not found"); - // } - - // user.UserRoles = roleId; - // await _userRepository.SaveAsync(); - //} - // DELETE public async Task DeleteUserAsync(int id) { diff --git a/UserManagement.Domain/Entities/Role.cs b/UserManagement.Domain/Entities/Role.cs index ffabd48..7d6d630 100644 --- a/UserManagement.Domain/Entities/Role.cs +++ b/UserManagement.Domain/Entities/Role.cs @@ -16,7 +16,7 @@ namespace UserManagement.Domain.Entities public required string Name { get; set; } [Column("USERS")] - public ICollection? UserRoles { get; init; } = new Collection(); + public ICollection? UserRoles { get; set; } = new Collection(); [Required] [Column("CREATION_DATE", TypeName = "datetime")] diff --git a/UserManagement.Domain/Entities/User.cs b/UserManagement.Domain/Entities/User.cs index b259964..443ddaf 100644 --- a/UserManagement.Domain/Entities/User.cs +++ b/UserManagement.Domain/Entities/User.cs @@ -23,16 +23,10 @@ namespace UserManagement.Domain.Entities [Column("LAST_NAME")] public string LastName { get; set; } - //[Obsolete("Use password hash")] - //[Required] - //[Column("PASSWORD")] - //public required string Password { get; set; } - [Required] [Column("PASSWORD_HASH")] public required string PasswordHash { get; set; } - [Column("ROLES")] public ICollection? UserRoles { get; set; } = new Collection(); } -} +} \ No newline at end of file diff --git a/UserManagement.Infrastructure/Interfaces/IUserRepository.cs b/UserManagement.Infrastructure/Interfaces/IUserRepository.cs index 90ac205..f713ca1 100644 --- a/UserManagement.Infrastructure/Interfaces/IUserRepository.cs +++ b/UserManagement.Infrastructure/Interfaces/IUserRepository.cs @@ -8,21 +8,21 @@ namespace UserManagement.Infrastructure.Interfaces Task AddAsync(User user); // READ ALL - Task> GetAllAsync(); + Task> GetAllAsync(bool includeRoles = true); // READ BY ID - Task GetByIdAsync(int id); + Task GetByIdAsync(int id, bool includeRoles = true); // READ BY USERNAME - Task GetByUsernameAsync(string username); + Task GetByUsernameAsync(string username, bool includeRoles = true); + + // READ BY ROLE + Task> GetByRoleAsync(string role); // UPDATE Task UpdateAsync(User user); // DELETE Task DeleteAsync(User user); - - // SAVE - Task SaveAsync(); } } diff --git a/UserManagement.Infrastructure/Repositories/UserRepository.cs b/UserManagement.Infrastructure/Repositories/UserRepository.cs index 8f59032..d3ad916 100644 --- a/UserManagement.Infrastructure/Repositories/UserRepository.cs +++ b/UserManagement.Infrastructure/Repositories/UserRepository.cs @@ -22,30 +22,52 @@ namespace UserManagement.Infrastructure.Repositories } // READ ALL - public async Task> GetAllAsync() + public async Task> GetAllAsync(bool includeRoles = true) { - return await _context.Users - .Include(u => u.UserRoles) - .ThenInclude(ur => ur.Role) - .ToListAsync(); + var query = _context.Users.AsNoTracking(); + + if (includeRoles) + query = query.Include(user => user.UserRoles)!.ThenInclude(ur => ur.Role); + + return await query.ToListAsync(); } // READ BY ID - public async Task GetByIdAsync(int id) + public async Task GetByIdAsync(int id, bool includeRoles = true) { - return await _context.Users.Where(user => user.Id == id) - .Include(user => user.UserRoles) - .ThenInclude(ur =>ur.Role) - .FirstAsync(); + var query = _context.Users.AsNoTracking(); + + if (id > 0) + query = query.Where(u => u.Id == id); + + if (includeRoles) + query = query.Include(user => user.UserRoles)!.ThenInclude(ur => ur.Role); + + return await query.FirstOrDefaultAsync(); } // READ BY USERNAME - public async Task GetByUsernameAsync(string username) + public async Task GetByUsernameAsync(string username, bool includeRoles = true) + { + var query = _context.Users.AsNoTracking(); + + if (!string.IsNullOrEmpty(username)) + query = query.Where(u => u.UserName == username); + + if (includeRoles) + query = query.Include(user => user.UserRoles)!.ThenInclude(ur => ur.Role); + + return await query.FirstOrDefaultAsync(); + } + + // READ BY ROLE + public async Task> GetByRoleAsync(string role) { return await _context.Users - .Include(user => user.UserRoles) - .ThenInclude(ur => ur.Role) - .FirstOrDefaultAsync(u => u.UserName == username); + .Include(u => u.UserRoles)! + .ThenInclude(ur => ur.Role) + .Where(ur => ur.UserRoles!.Any(r => r.Role!.Name == role)) + .ToListAsync(); } // UPDATE @@ -63,12 +85,5 @@ namespace UserManagement.Infrastructure.Repositories var result = await _context.SaveChangesAsync(); return result > 0; } - - // SAVE - public async Task SaveAsync() - { - var saved = await _context.SaveChangesAsync(); - return saved > 0 ? true : false; - } } }