Skip to content

Commit

Permalink
Merge pull request #21 from quizer-app/develop
Browse files Browse the repository at this point in the history
Add pagination to quizes
  • Loading branch information
EloToJaa authored Jan 9, 2024
2 parents 9aeed11 + 438271b commit 62f5ccd
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 21 deletions.
2 changes: 2 additions & 0 deletions src/Quizer.Api/Common/Mapping/QuizMappingConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
using Quizer.Contracts.Question;
using Quizer.Application.Quizes.Common;
using Quizer.Domain.QuestionAggregate.Entities;
using Quizer.Contracts.Common;
using Quizer.Application.Utils;

namespace Quizer.Api.Common.Mapping;

Expand Down
20 changes: 17 additions & 3 deletions src/Quizer.Api/Controllers/V1/QuizController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Quizer.Application.Quizes.Queries.GetQuizById;
using Quizer.Application.Quizes.Queries.GetQuizByName;
using Quizer.Application.Quizes.Queries.GetQuizes;
using Quizer.Contracts.Common;
using Quizer.Contracts.Quiz;
using System.Security.Claims;

Expand All @@ -28,13 +29,26 @@ public QuizController(ISender mediator, IMapper mapper)

[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> GetQuizes(string? userId = null)
public async Task<IActionResult> GetQuizes(
string? searchTerm,
string? sortColumn,
string? sortOrder,
int pageNumber,
int pageSize,
string? userName)
{
var query = new GetQuizesQuery(userId);
var query = new GetQuizesQuery(
searchTerm,
sortColumn,
sortOrder,
pageNumber,
pageSize,
userName);

var result = await _mediator.Send(query);

return result.Match(
quizes => Ok(_mapper.Map<List<ShortQuizResponse>>(quizes)),
quizes => Ok(_mapper.Map<PaginatedResponse<ShortQuizResponse>>(quizes)),
Problem);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ namespace Quizer.Application.Common.Interfaces.Persistance;

public interface IQuizRepository
{
IQueryable<Quiz> Quizes { get; }

Task Add(Quiz quiz);
Task<List<Quiz>> GetAll(Guid? userId = null);
Task<Quiz?> Get(QuizId id);
Task<Quiz?> Get(string userName, string quizSlug);
void Delete(Quiz quiz);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
using ErrorOr;
using MediatR;
using Quizer.Application.Utils;
using Quizer.Domain.QuizAggregate;

namespace Quizer.Application.Quizes.Queries.GetQuizes;

public record GetQuizesQuery(
string? UserId = null) : IRequest<ErrorOr<List<Quiz>>>;
string? SearchTerm,
string? SortColumn,
string? SortOrder,
int PageNumber,
int PageSize,
string? UserName) : IRequest<ErrorOr<PagedList<Quiz>>>;
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using ErrorOr;
using MediatR;
using Quizer.Application.Common.Interfaces.Persistance;
using Quizer.Application.Utils;
using Quizer.Domain.QuizAggregate;
using System.Linq.Expressions;

namespace Quizer.Application.Quizes.Queries.GetQuizes;

public class GetQuizesQueryHandler : IRequestHandler<GetQuizesQuery, ErrorOr<List<Quiz>>>
public class GetQuizesQueryHandler : IRequestHandler<GetQuizesQuery, ErrorOr<PagedList<Quiz>>>
{
private readonly IQuizRepository _quizRepository;

Expand All @@ -14,9 +16,40 @@ public GetQuizesQueryHandler(IQuizRepository quizRepository)
_quizRepository = quizRepository;
}

public async Task<ErrorOr<List<Quiz>>> Handle(GetQuizesQuery request, CancellationToken cancellationToken)
public async Task<ErrorOr<PagedList<Quiz>>> Handle(GetQuizesQuery request, CancellationToken cancellationToken)
{
Guid? userId = request.UserId is null ? null : new Guid(request.UserId);
return await _quizRepository.GetAll(userId);
IQueryable<Quiz> quizesQuery = _quizRepository.Quizes;

if (!string.IsNullOrWhiteSpace(request.SearchTerm))
{
quizesQuery = quizesQuery.Where(q =>
q.Name.Contains(request.SearchTerm) ||
q.UserName.Contains(request.SearchTerm));
}

if (!string.IsNullOrWhiteSpace(request.UserName))
{
quizesQuery = quizesQuery.Where(q => q.UserName == request.UserName);
}

Expression<Func<Quiz, object>> keySelector = request.SortColumn?.ToLower() switch
{
"name" => q => q.Name,
"userName" => q => q.UserName,
"createdAt" => q => q.CreatedAt,
_ => q => q.CreatedAt
};

if (request.SortOrder?.ToLower() == "desc")
{
quizesQuery = quizesQuery.OrderByDescending(keySelector);
}
else
{
quizesQuery = quizesQuery.OrderBy(keySelector);
}

var quizes = await PagedList<Quiz>.CreateAsync(quizesQuery, request.PageNumber, request.PageSize);
return quizes;
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
using FluentValidation;
using Quizer.Application.Common.Validation;

namespace Quizer.Application.Quizes.Queries.GetQuizes;

public class GetQuizesValidator : AbstractValidator<GetQuizesQuery>
{
public GetQuizesValidator()
{
RuleFor(q => q.UserId)
.BeValidGuid()
.When(q => !string.IsNullOrEmpty(q.UserId));
RuleFor(q => q.PageSize)
.GreaterThanOrEqualTo(1)
.LessThanOrEqualTo(100);

RuleFor(q => q.PageNumber)
.GreaterThanOrEqualTo(1);
}
}
37 changes: 37 additions & 0 deletions src/Quizer.Application/Utils/PagedList.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Microsoft.EntityFrameworkCore;

namespace Quizer.Application.Utils;

public class PagedList<T>
{
public List<T> Items { get; private set; }
public int PageNumber { get; private set; }
public int PageSize { get; private set; }
public int TotalCount { get; private set; }

public int TotalPages => (int)Math.Ceiling(TotalCount / (double)PageSize);
public bool HasNextPage => PageNumber < TotalPages;
public bool HasPreviousPage => PageNumber > 1;

private PagedList(List<T> items, int pageNumber, int pageSize, int totalCount)
{
Items = items;
PageNumber = pageNumber;
PageSize = pageSize;
TotalCount = totalCount;
}

public static async Task<PagedList<T>> CreateAsync(
IQueryable<T> query,
int pageNumber,
int pageSize)
{
var totalCount = await query.CountAsync();
var items = await query
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.ToListAsync();

return new(items, pageNumber, pageSize, totalCount);
}
}
10 changes: 10 additions & 0 deletions src/Quizer.Contracts/Common/PaginatedResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Quizer.Contracts.Common;

public record PaginatedResponse<T>(
List<T> Items,
int PageNumber,
int PageSize,
int TotalCount,
int TotalPages,
bool HasNextPage,
bool HasPreviousPage);
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace Quizer.Infrastructure.Persistance.Repositories;
public class QuizRepository : IQuizRepository
{
private readonly QuizerDbContext _context;
public IQueryable<Quiz> Quizes => _context.Quizes;

public QuizRepository(QuizerDbContext context)
{
Expand All @@ -33,12 +34,4 @@ public void Delete(Quiz quiz)
{
return await _context.Quizes.FirstOrDefaultAsync(q => q.UserName == userName && q.Slug == quizSlug);
}

public async Task<List<Quiz>> GetAll(Guid? userId = null)
{
if (userId is null)
return await _context.Quizes.ToListAsync();
else
return await _context.Quizes.Where(q => q.UserId == userId).ToListAsync();
}
}

0 comments on commit 62f5ccd

Please sign in to comment.