diff --git a/src/Api/Controllers/Payload/Requests/Lockers/GetAllLockersPaginatedQueryParameters.cs b/src/Api/Controllers/Payload/Requests/Lockers/GetAllLockersPaginatedQueryParameters.cs
index 656b96f6..9f701ed4 100644
--- a/src/Api/Controllers/Payload/Requests/Lockers/GetAllLockersPaginatedQueryParameters.cs
+++ b/src/Api/Controllers/Payload/Requests/Lockers/GetAllLockersPaginatedQueryParameters.cs
@@ -12,6 +12,10 @@ public class GetAllLockersPaginatedQueryParameters
///
public Guid? RoomId { get; set; }
///
+ /// Search term
+ ///
+ public string? SearchTerm { get; set; }
+ ///
/// Page number
///
public int? Page { get; set; }
diff --git a/src/Api/Controllers/Payload/Requests/Rooms/GetAllRoomsPaginatedQueryParameters.cs b/src/Api/Controllers/Payload/Requests/Rooms/GetAllRoomsPaginatedQueryParameters.cs
index 34721c39..89a7bbb6 100644
--- a/src/Api/Controllers/Payload/Requests/Rooms/GetAllRoomsPaginatedQueryParameters.cs
+++ b/src/Api/Controllers/Payload/Requests/Rooms/GetAllRoomsPaginatedQueryParameters.cs
@@ -5,6 +5,10 @@ namespace Api.Controllers.Payload.Requests.Rooms;
///
public class GetAllRoomsPaginatedQueryParameters
{
+ ///
+ /// Search term
+ ///
+ public string? SearchTerm { get; set; }
///
/// Page number
///
diff --git a/src/Api/Controllers/RoomsController.cs b/src/Api/Controllers/RoomsController.cs
index d46390cc..5ecb848d 100644
--- a/src/Api/Controllers/RoomsController.cs
+++ b/src/Api/Controllers/RoomsController.cs
@@ -36,14 +36,16 @@ public async Task>> GetById([FromRoute] Guid roomId
///
/// Get all rooms paginated details
/// A paginated list of rooms
+ [RequiresRole(IdentityData.Roles.Admin)]
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
public async Task>>> GetAllPaginated(
- [FromQuery] GetAllLockersPaginatedQueryParameters queryParameters)
+ [FromQuery] GetAllRoomsPaginatedQueryParameters queryParameters)
{
var query = new GetAllRoomsPaginated.Query()
{
+ SearchTerm = queryParameters.SearchTerm,
Page = queryParameters.Page,
Size = queryParameters.Size,
SortBy = queryParameters.SortBy,
diff --git a/src/Application/Common/Extensions/StringExtensions.cs b/src/Application/Common/Extensions/StringExtensions.cs
new file mode 100644
index 00000000..b2a723de
--- /dev/null
+++ b/src/Application/Common/Extensions/StringExtensions.cs
@@ -0,0 +1,13 @@
+namespace Application.Common.Extensions;
+
+public static class StringExtensions
+{
+ public static bool MatchesPropertyName(this string input)
+ where T : class
+ {
+ var type = typeof(T);
+ var properties = type.GetProperties();
+
+ return properties.Any(property => string.Equals(property.Name, input));
+ }
+}
\ No newline at end of file
diff --git a/src/Application/Rooms/Queries/GetAllRoomsPaginated.cs b/src/Application/Rooms/Queries/GetAllRoomsPaginated.cs
index 8094f631..aef45ae6 100644
--- a/src/Application/Rooms/Queries/GetAllRoomsPaginated.cs
+++ b/src/Application/Rooms/Queries/GetAllRoomsPaginated.cs
@@ -1,5 +1,10 @@
+using Application.Common.Extensions;
+using Application.Common.Interfaces;
+using Application.Common.Mappings;
using Application.Common.Models;
using Application.Common.Models.Dtos.Physical;
+using AutoMapper;
+using AutoMapper.QueryableExtensions;
using MediatR;
namespace Application.Rooms.Queries;
@@ -8,9 +13,49 @@ public class GetAllRoomsPaginated
{
public record Query : IRequest>
{
+ public string? SearchTerm { 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 rooms = _context.Rooms.AsQueryable();
+
+ if (!(request.SearchTerm is null || request.SearchTerm.Trim().Equals(string.Empty)))
+ {
+ rooms = rooms.Where(x =>
+ x.Name.ToLower().Contains(request.SearchTerm.ToLower()));
+ }
+
+ var sortBy = request.SortBy;
+ if (sortBy is null || !sortBy.MatchesPropertyName())
+ {
+ sortBy = nameof(RoomDto.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 result = await rooms
+ .ProjectTo(_mapper.ConfigurationProvider)
+ .OrderByCustom(sortBy, sortOrder)
+ .PaginatedListAsync(pageNumber.Value, sizeNumber.Value);
+
+ return result;
+ }
+ }
}
\ No newline at end of file
diff --git a/tests/Application.Tests.Integration/Rooms/Queries/GetAllRoomsPaginatedTests.cs b/tests/Application.Tests.Integration/Rooms/Queries/GetAllRoomsPaginatedTests.cs
new file mode 100644
index 00000000..83c6c955
--- /dev/null
+++ b/tests/Application.Tests.Integration/Rooms/Queries/GetAllRoomsPaginatedTests.cs
@@ -0,0 +1,84 @@
+using Application.Common.Mappings;
+using Application.Common.Models.Dtos.Physical;
+using Application.Rooms.Queries;
+using AutoMapper;
+using FluentAssertions;
+using Xunit;
+
+namespace Application.Tests.Integration.Rooms.Queries;
+
+public class GetAllRoomsPaginatedTests : BaseClassFixture
+{
+ private readonly IMapper _mapper;
+ public GetAllRoomsPaginatedTests(CustomApiFactory apiFactory) : base(apiFactory)
+ {
+ var configuration = new MapperConfiguration(config => config.AddProfile());
+
+ _mapper = configuration.CreateMapper();
+ }
+
+ [Fact]
+ public async Task ShouldReturnAllRooms()
+ {
+ // Arrange
+ var department1 = CreateDepartment();
+ var department2 = CreateDepartment();
+ var room1 = CreateRoom(department1);
+ var room2 = CreateRoom(department2);
+ await AddAsync(room1);
+ await AddAsync(room2);
+
+ var query = new GetAllRoomsPaginated.Query();
+
+ // Act
+ var result = await SendAsync(query);
+
+ // Assert
+ result.TotalCount.Should().Be(2);
+ result.Items.Should()
+ .ContainEquivalentOf(_mapper.Map(room1),
+ config => config.IgnoringCyclicReferences());
+ result.Items.Should()
+ .ContainEquivalentOf(_mapper.Map(room2),
+ config => config.IgnoringCyclicReferences());
+
+ // Cleanup
+ Remove(room1);
+ Remove(room2);
+ Remove(department1);
+ Remove(department2);
+ }
+
+ [Fact]
+ public async Task ShouldReturnOrderById_WhenWrongSortByIsProvided()
+ {
+ // Arrange
+ var department1 = CreateDepartment();
+ var department2 = CreateDepartment();
+ var room1 = CreateRoom(department1);
+ var room2 = CreateRoom(department2);
+ await AddAsync(room1);
+ await AddAsync(room2);
+
+ var query = new GetAllRoomsPaginated.Query()
+ {
+ SortBy = "e",
+ };
+
+ // Act
+ var result = await SendAsync(query);
+
+ // Assert
+ result.TotalCount.Should().Be(2);
+ result.Items.Should()
+ .BeEquivalentTo(_mapper.Map(new[] { room1, room2 })
+ .OrderBy(x => x.Id), config => config.IgnoringCyclicReferences());
+ result.Items.Should().BeInAscendingOrder(x => x.Id);
+
+ // Cleanup
+ Remove(room1);
+ Remove(room2);
+ Remove(department1);
+ Remove(department2);
+ }
+}
\ No newline at end of file