From e2f881351eb1bd66a801310eecc50d8abf00d339 Mon Sep 17 00:00:00 2001 From: StarryFolf Date: Tue, 13 Jun 2023 18:08:20 +0700 Subject: [PATCH 1/4] feat: implement get employees for the department the current user is in --- ...ithRoleEmployeePaginatedQueryParameters.cs | 6 ++ src/Api/Controllers/UsersController.cs | 27 +++++++++ .../GetAllUsersWithRoleEmployeePaginated.cs | 59 +++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 src/Api/Controllers/Payload/Requests/Users/GetAllUsersWithRoleEmployeePaginatedQueryParameters.cs create mode 100644 src/Application/Users/Queries/GetAllUsersWithRoleEmployeePaginated.cs diff --git a/src/Api/Controllers/Payload/Requests/Users/GetAllUsersWithRoleEmployeePaginatedQueryParameters.cs b/src/Api/Controllers/Payload/Requests/Users/GetAllUsersWithRoleEmployeePaginatedQueryParameters.cs new file mode 100644 index 00000000..a621f241 --- /dev/null +++ b/src/Api/Controllers/Payload/Requests/Users/GetAllUsersWithRoleEmployeePaginatedQueryParameters.cs @@ -0,0 +1,6 @@ +namespace Api.Controllers.Payload.Requests.Users; + +public class GetAllUsersWithRoleEmployeePaginatedQueryParameters : PaginatedQueryParameters +{ + +} \ No newline at end of file diff --git a/src/Api/Controllers/UsersController.cs b/src/Api/Controllers/UsersController.cs index 095e9605..bf32024b 100644 --- a/src/Api/Controllers/UsersController.cs +++ b/src/Api/Controllers/UsersController.cs @@ -160,4 +160,31 @@ public async Task>> Update([FromRoute] Guid userId, var result = await Mediator.Send(command); return Ok(Result.Succeed(result)); } + + /// + /// Get all users with the "Employee" role of the current user's department. + /// + /// Query parameters + /// A list of UserDtos with the employee role of that department + [RequiresRole(IdentityData.Roles.Employee)] + [HttpGet("employees")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task>>> GetAllWithRoleEmployeePaginated( + [FromQuery] GetAllUsersWithRoleEmployeePaginatedQueryParameters queryParameters) + { + var performingUserId = _currentUserService.GetId(); + var query = new GetAllUsersWithRoleEmployeePaginated.Query() + { + UserId = performingUserId, + Page = queryParameters.Page, + Size = queryParameters.Size, + SortBy = queryParameters.SortBy, + SortOrder = queryParameters.SortOrder, + }; + + var result = await Mediator.Send(query); + return Ok(Result>.Succeed(result)); + } } diff --git a/src/Application/Users/Queries/GetAllUsersWithRoleEmployeePaginated.cs b/src/Application/Users/Queries/GetAllUsersWithRoleEmployeePaginated.cs new file mode 100644 index 00000000..1c6932a8 --- /dev/null +++ b/src/Application/Users/Queries/GetAllUsersWithRoleEmployeePaginated.cs @@ -0,0 +1,59 @@ +using Application.Common.Extensions; +using Application.Common.Interfaces; +using Application.Common.Models; +using AutoMapper; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Application.Users.Queries; + +public class GetAllUsersWithRoleEmployeePaginated +{ + public record Query : IRequest> + { + public Guid UserId { get; init; } + public int? Page { get; init; } + public int? Size { get; init; } + public string? SortBy { get; init; } + public string? SortOrder { get; init; } + } + + public class QueryHandler : IRequestHandler> + { + private readonly IApplicationDbContext _context; + private readonly IMapper _mapper; + + public QueryHandler(IApplicationDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public async Task> Handle(Query request, CancellationToken cancellationToken) + { + var user = await _context.Users.Include(x => x.Department) + .FirstOrDefaultAsync(x => x.Id == request.UserId, cancellationToken); + var users = _context.Users.AsQueryable() + .Where(x => x.Department!.Equals(user!.Department)); + + var sortBy = request.SortBy; + if (sortBy is null || !sortBy.MatchesPropertyName()) + { + sortBy = nameof(UserDto.Id); + } + var sortOrder = request.SortOrder ?? "asc"; + var pageNumber = request.Page is null or <= 0 ? 1 : request.Page; + var sizeNumber = request.Size is null or <= 0 ? 5 : request.Size; + + var count = await users.CountAsync(cancellationToken); + var list = await users + .Paginate(pageNumber.Value, sizeNumber.Value) + .OrderByCustom(sortBy, sortOrder) + .ToListAsync(cancellationToken); + + var result = _mapper.Map>(list); + + return new PaginatedList(result, count, pageNumber.Value, sizeNumber.Value); + } + } +} \ No newline at end of file From 452008d00d76eeffc12909dab5ac2280566f4b9b Mon Sep 17 00:00:00 2001 From: StarryFolf Date: Tue, 13 Jun 2023 19:54:47 +0700 Subject: [PATCH 2/4] refactoring --- .../GetAllEmployeesPaginatedQueryParameters.cs | 6 ++++++ ...sWithRoleEmployeePaginatedQueryParameters.cs | 6 ------ src/Api/Controllers/UsersController.cs | 17 ++++++++++++----- src/Api/Services/CurrentUserService.cs | 2 +- ...Paginated.cs => GetAllEmployeesPaginated.cs} | 12 +++++++----- src/Infrastructure/Identity/IdentityService.cs | 2 +- 6 files changed, 27 insertions(+), 18 deletions(-) create mode 100644 src/Api/Controllers/Payload/Requests/Users/GetAllEmployeesPaginatedQueryParameters.cs delete mode 100644 src/Api/Controllers/Payload/Requests/Users/GetAllUsersWithRoleEmployeePaginatedQueryParameters.cs rename src/Application/Users/Queries/{GetAllUsersWithRoleEmployeePaginated.cs => GetAllEmployeesPaginated.cs} (84%) diff --git a/src/Api/Controllers/Payload/Requests/Users/GetAllEmployeesPaginatedQueryParameters.cs b/src/Api/Controllers/Payload/Requests/Users/GetAllEmployeesPaginatedQueryParameters.cs new file mode 100644 index 00000000..4a53d7f5 --- /dev/null +++ b/src/Api/Controllers/Payload/Requests/Users/GetAllEmployeesPaginatedQueryParameters.cs @@ -0,0 +1,6 @@ +namespace Api.Controllers.Payload.Requests.Users; + +public class GetAllEmployeesPaginatedQueryParameters : PaginatedQueryParameters +{ + +} \ No newline at end of file diff --git a/src/Api/Controllers/Payload/Requests/Users/GetAllUsersWithRoleEmployeePaginatedQueryParameters.cs b/src/Api/Controllers/Payload/Requests/Users/GetAllUsersWithRoleEmployeePaginatedQueryParameters.cs deleted file mode 100644 index a621f241..00000000 --- a/src/Api/Controllers/Payload/Requests/Users/GetAllUsersWithRoleEmployeePaginatedQueryParameters.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Api.Controllers.Payload.Requests.Users; - -public class GetAllUsersWithRoleEmployeePaginatedQueryParameters : PaginatedQueryParameters -{ - -} \ No newline at end of file diff --git a/src/Api/Controllers/UsersController.cs b/src/Api/Controllers/UsersController.cs index bf32024b..0777758f 100644 --- a/src/Api/Controllers/UsersController.cs +++ b/src/Api/Controllers/UsersController.cs @@ -6,6 +6,7 @@ using Application.Users.Queries; using Infrastructure.Identity.Authorization; using Microsoft.AspNetCore.Mvc; +using Org.BouncyCastle.Security; namespace Api.Controllers; @@ -171,13 +172,19 @@ public async Task>> Update([FromRoute] Guid userId, [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task>>> GetAllWithRoleEmployeePaginated( - [FromQuery] GetAllUsersWithRoleEmployeePaginatedQueryParameters queryParameters) + public async Task>>> GetAllEmployeesPaginated( + [FromQuery] GetAllEmployeesPaginatedQueryParameters queryParameters) { - var performingUserId = _currentUserService.GetId(); - var query = new GetAllUsersWithRoleEmployeePaginated.Query() + var performingUserDepartmentId = _currentUserService.GetDepartmentId(); + + if (performingUserDepartmentId is null) + { + throw new KeyNotFoundException("User does not belong to a department."); + } + + var query = new GetAllEmployeesPaginated.Query() { - UserId = performingUserId, + DepartmentId = performingUserDepartmentId.Value, Page = queryParameters.Page, Size = queryParameters.Size, SortBy = queryParameters.SortBy, diff --git a/src/Api/Services/CurrentUserService.cs b/src/Api/Services/CurrentUserService.cs index 25c349de..b0b7d5ea 100644 --- a/src/Api/Services/CurrentUserService.cs +++ b/src/Api/Services/CurrentUserService.cs @@ -47,7 +47,7 @@ public string GetRole() var claim = _httpContextAccessor.HttpContext!.User.Claims .FirstOrDefault(x => x.Type.Equals("departmentId")); var id = claim?.Value; - return id is not null ? Guid.Parse(id) : null; + return id is not null && Guid.TryParse(id, out _) ? Guid.Parse(id) : null; } public User GetCurrentUser() diff --git a/src/Application/Users/Queries/GetAllUsersWithRoleEmployeePaginated.cs b/src/Application/Users/Queries/GetAllEmployeesPaginated.cs similarity index 84% rename from src/Application/Users/Queries/GetAllUsersWithRoleEmployeePaginated.cs rename to src/Application/Users/Queries/GetAllEmployeesPaginated.cs index 1c6932a8..cbc90a79 100644 --- a/src/Application/Users/Queries/GetAllUsersWithRoleEmployeePaginated.cs +++ b/src/Application/Users/Queries/GetAllEmployeesPaginated.cs @@ -1,17 +1,18 @@ using Application.Common.Extensions; using Application.Common.Interfaces; using Application.Common.Models; +using Application.Identity; using AutoMapper; using MediatR; using Microsoft.EntityFrameworkCore; namespace Application.Users.Queries; -public class GetAllUsersWithRoleEmployeePaginated +public class GetAllEmployeesPaginated { public record Query : IRequest> { - public Guid UserId { get; init; } + public Guid DepartmentId { get; init; } public int? Page { get; init; } public int? Size { get; init; } public string? SortBy { get; init; } @@ -31,10 +32,11 @@ public QueryHandler(IApplicationDbContext context, IMapper mapper) public async Task> Handle(Query request, CancellationToken cancellationToken) { - var user = await _context.Users.Include(x => x.Department) - .FirstOrDefaultAsync(x => x.Id == request.UserId, cancellationToken); var users = _context.Users.AsQueryable() - .Where(x => x.Department!.Equals(user!.Department)); + .Where(x => x.Department!.Id == request.DepartmentId + && x.Role.Equals(IdentityData.Roles.Employee) + && x.IsActive + && x.IsActivated); var sortBy = request.SortBy; if (sortBy is null || !sortBy.MatchesPropertyName()) diff --git a/src/Infrastructure/Identity/IdentityService.cs b/src/Infrastructure/Identity/IdentityService.cs index f4b8e5b7..d4fa8552 100644 --- a/src/Infrastructure/Identity/IdentityService.cs +++ b/src/Infrastructure/Identity/IdentityService.cs @@ -296,7 +296,7 @@ private SecurityToken CreateJweToken(User user) new(JwtRegisteredClaimNames.Email, user.Email!), new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new(JwtRegisteredClaimNames.Iat, utcNow.ToString(CultureInfo.InvariantCulture)), - new("departmentId", user.Department!.Id.ToString()), + new("departmentId", user.Department is not null ? user.Department.Id.ToString() : String.Empty), new("isActive", user.IsActive.ToString()), }; var publicEncryptionKey = new RsaSecurityKey(_encryptionKey.ExportParameters(false)) {KeyId = _jweSettings.EncryptionKeyId}; From 340e7b8d03325d4462462ee467b846985d6ee789 Mon Sep 17 00:00:00 2001 From: StarryFolf <67864500+StarryFolf@users.noreply.github.com> Date: Tue, 13 Jun 2023 21:57:50 +0700 Subject: [PATCH 3/4] Update GetAllEmployeesPaginated.cs --- src/Application/Users/Queries/GetAllEmployeesPaginated.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Application/Users/Queries/GetAllEmployeesPaginated.cs b/src/Application/Users/Queries/GetAllEmployeesPaginated.cs index cbc90a79..65d06b0c 100644 --- a/src/Application/Users/Queries/GetAllEmployeesPaginated.cs +++ b/src/Application/Users/Queries/GetAllEmployeesPaginated.cs @@ -19,7 +19,7 @@ public record Query : IRequest> public string? SortOrder { get; init; } } - public class QueryHandler : IRequestHandler> + public class Handler : IRequestHandler> { private readonly IApplicationDbContext _context; private readonly IMapper _mapper; @@ -58,4 +58,4 @@ public async Task> Handle(Query request, CancellationToke return new PaginatedList(result, count, pageNumber.Value, sizeNumber.Value); } } -} \ No newline at end of file +} From be2be64c0e0c9a89549753afd3027281a8f0e13a Mon Sep 17 00:00:00 2001 From: StarryFolf Date: Wed, 14 Jun 2023 09:09:36 +0700 Subject: [PATCH 4/4] refactoring --- src/Application/Users/Queries/GetAllEmployeesPaginated.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Application/Users/Queries/GetAllEmployeesPaginated.cs b/src/Application/Users/Queries/GetAllEmployeesPaginated.cs index 65d06b0c..d6189405 100644 --- a/src/Application/Users/Queries/GetAllEmployeesPaginated.cs +++ b/src/Application/Users/Queries/GetAllEmployeesPaginated.cs @@ -24,7 +24,7 @@ public class Handler : IRequestHandler> private readonly IApplicationDbContext _context; private readonly IMapper _mapper; - public QueryHandler(IApplicationDbContext context, IMapper mapper) + public Handler(IApplicationDbContext context, IMapper mapper) { _context = context; _mapper = mapper; @@ -33,6 +33,7 @@ public QueryHandler(IApplicationDbContext context, IMapper mapper) public async Task> Handle(Query request, CancellationToken cancellationToken) { var users = _context.Users.AsQueryable() + .Include(x => x.Department) .Where(x => x.Department!.Id == request.DepartmentId && x.Role.Equals(IdentityData.Roles.Employee) && x.IsActive