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/UsersController.cs b/src/Api/Controllers/UsersController.cs index 795769bc..ac5ec81e 100644 --- a/src/Api/Controllers/UsersController.cs +++ b/src/Api/Controllers/UsersController.cs @@ -8,6 +8,7 @@ using Application.Users.Queries; using Infrastructure.Identity.Authorization; using Microsoft.AspNetCore.Mvc; +using Org.BouncyCastle.Security; namespace Api.Controllers; @@ -164,6 +165,33 @@ public async Task>> Update([FromRoute] Guid userId, } /// + /// 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>>> GetAllEmployeesPaginated( + [FromQuery] GetAllEmployeesPaginatedQueryParameters queryParameters) + { + var performingUserDepartmentId = _currentUserService.GetDepartmentId(); + + if (performingUserDepartmentId is null) + { + throw new KeyNotFoundException("User does not belong to a department."); + } + + var query = new GetAllEmployeesPaginated.Query() + { + DepartmentId = performingUserDepartmentId.Value, + } + var result = await Mediator.Send(query); + return Ok(Result>.Succeed(result)); + } + /// Get all user related logs paginated /// /// Get all users related logs query parameters 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/GetAllEmployeesPaginated.cs b/src/Application/Users/Queries/GetAllEmployeesPaginated.cs new file mode 100644 index 00000000..d6189405 --- /dev/null +++ b/src/Application/Users/Queries/GetAllEmployeesPaginated.cs @@ -0,0 +1,62 @@ +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 GetAllEmployeesPaginated +{ + public record Query : IRequest> + { + public Guid DepartmentId { get; init; } + public int? Page { get; init; } + public int? Size { get; init; } + public string? SortBy { get; init; } + public string? SortOrder { get; init; } + } + + public class Handler : IRequestHandler> + { + private readonly IApplicationDbContext _context; + private readonly IMapper _mapper; + + public Handler(IApplicationDbContext context, IMapper mapper) + { + _context = context; + _mapper = 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 + && x.IsActivated); + + 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); + } + } +} 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};