Skip to content

Commit

Permalink
Apply improvements on the repository filters
Browse files Browse the repository at this point in the history
  • Loading branch information
vanderlan committed Oct 13, 2023
1 parent f23c749 commit 1699e0f
Show file tree
Hide file tree
Showing 19 changed files with 68 additions and 79 deletions.
2 changes: 1 addition & 1 deletion src/Orion.Api/Controllers/CustomersController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public CustomersController(ICustomerService customerService, IMapper mapper) : b

[HttpGet]
[ProducesResponseType((int)HttpStatusCode.OK)]
public async Task<IActionResult> Get([FromQuery] CustomerFilter filter)
public async Task<IActionResult> Get([FromQuery] BaseFilter<Customer> filter)
{
var customer = await _customerService.ListPaginateAsync(filter);

Expand Down
2 changes: 1 addition & 1 deletion src/Orion.Api/Controllers/UsersController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public UsersController(IUserService userService, IMapper mapper) : base(mapper)

[HttpGet]
[ProducesResponseType((int)HttpStatusCode.OK)]
public async Task<IActionResult> Get([FromQuery] UserFilter filter)
public async Task<IActionResult> Get([FromQuery] BaseFilter<User> filter)
{
var user = await _userService.ListPaginateAsync(filter);

Expand Down
2 changes: 1 addition & 1 deletion src/Orion.Api/appsettings.Development.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"SymmetricSecurityKey": "5cCI6IkpXVCJ9.eyJlbWFpbCI6InZhbmRlcmxhbi5nc0BnbWFpbC5jb20iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJhZG1p",
"Issuer": "Orion API",
"Audience": "http://www.orion-api.com",
"TokenExpirationMinutes": 15
"TokenExpirationMinutes": 120
},
"ElasticConfiguration": {
"Uri": "http://localhost:9200"
Expand Down
40 changes: 35 additions & 5 deletions src/Orion.Data/Repository/Generic/BaseEntityRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
using System.Threading.Tasks;
using Orion.Data.Context;
using Orion.Entities.Domain;
using Orion.Entities.Filter;
using Orion.Entities.ValueObjects.Pagination;

namespace Orion.Data.Repository.Generic
{
public class BaseEntityRepository<T> : IBaseEntityRepository<T> where T : BaseEntity
public abstract class BaseEntityRepository<T> : IBaseEntityRepository<T> where T : BaseEntity
{
protected DataContext DataContext { get; }

Expand All @@ -18,14 +20,14 @@ public BaseEntityRepository(DataContext dataContext)
DataContext = dataContext;
}

public async Task<T> AddAsync(T entity)
public virtual async Task<T> AddAsync(T entity)
{
var added = await DataContext.Set<T>().AddAsync(entity);

return added.Entity;
}

public async Task DeleteAsync(string publicId)
public virtual async Task DeleteAsync(string publicId)
{
var existing = await GetByIdAsync(publicId);

Expand All @@ -41,16 +43,44 @@ public virtual async Task<T> GetByIdAsync(string publicId)
return await DataContext.Set<T>().AsNoTracking().SingleOrDefaultAsync(x => x.PublicId == publicId);
}

public async Task<IEnumerable<T>> SearchByAsync(Expression<Func<T, bool>> predicate)
public virtual async Task<IEnumerable<T>> SearchByAsync(Expression<Func<T, bool>> predicate)
{
return await DataContext.Set<T>().AsNoTracking().Where(predicate).ToListAsync();
}

public void Update(T entity)
public virtual void Update(T entity)
{
DataContext.ChangeTracker.Clear();
DataContext.Entry(entity).State = EntityState.Modified;
DataContext.Set<T>().Update(entity);
}

public virtual async Task<PagedList<T>> ListPaginateAsync(BaseFilter<T> filter)
{
IQueryable<T> query = DataContext.Set<T>();

query = ApplyFilters(filter, query);

var pagination = (filter.Page * filter.Quantity) - filter.Quantity;

var entityList = await query.OrderBy(x => x.CreatedAt)
.AsNoTracking()
.Skip(pagination)
.Take(filter.Quantity)
.ToListAsync();

return new PagedList<T>(entityList, query.Count());
}

/// <summary>
/// Each repository must implement its filter, but if the class does not need a custom filter, only pagination and the OrderBy pattern will be applied
/// </summary>
/// <param name="filter"></param>
/// <param name="query"></param>
/// <returns>IQuerable with filters applied</returns>
protected virtual IQueryable<T> ApplyFilters(BaseFilter<T> filter, IQueryable<T> query)
{
return query;
}
}
}
19 changes: 4 additions & 15 deletions src/Orion.Data/Repository/Implementations/CustomerRepository.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Threading.Tasks;
using Orion.Data.Context;
using Orion.Data.Repository.Generic;
using Orion.Domain.Repositories;
using Orion.Entities.Domain;
using Orion.Entities.Filter;
using Orion.Entities.ValueObjects.Pagination;
using System.Linq;

namespace Orion.Data.Repository.Implementations
{
Expand All @@ -17,20 +14,12 @@ public CustomerRepository(DataContext context) : base(context)

}

public async Task<PagedList<Customer>> ListPaginate(CustomerFilter filter)
protected override IQueryable<Customer> ApplyFilters(BaseFilter<Customer> filter, IQueryable<Customer> query)
{
var pagination = (filter.Page * filter.Quantity) - filter.Quantity;

IQueryable<Customer> listQuerable = DataContext.Customers;

if (!string.IsNullOrWhiteSpace(filter.Query))
{
listQuerable = listQuerable.Where(x => x.Name.Contains(filter.Query));
}

var customerList = await listQuerable.OrderBy(x => x.Name).Skip(pagination).Take(filter.Quantity).ToListAsync();
query = query.Where(x => x.Name.Contains(filter.Query));

return new PagedList<Customer>(customerList, listQuerable.Count());
return query;
}
}
}
Expand Down
29 changes: 8 additions & 21 deletions src/Orion.Data/Repository/Implementations/UserRepository.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Threading.Tasks;
using Orion.Data.Context;
using Orion.Data.Repository.Generic;
using Orion.Domain.Repositories;
using Orion.Entities.Domain;
using Orion.Entities.Filter;
using Orion.Entities.ValueObjects.Pagination;
using System.Linq;
using System.Threading.Tasks;

namespace Orion.Data.Repository.Implementations
{
Expand All @@ -18,31 +17,19 @@ public UserRepository(DataContext context) : base(context)

public async Task<User> LoginAsync(string email, string password)
{
var user = await DataContext.Users.AsNoTracking().Where(x => x.Email.Equals(email) && x.Password.Equals(password)).FirstOrDefaultAsync();
var user = await DataContext.Users.AsNoTracking()
.Where(x => x.Email.Equals(email) && x.Password.Equals(password))
.FirstOrDefaultAsync();

return user ?? null;
}

public async Task<PagedList<User>> ListPaginateAsync(UserFilter filter)
protected override IQueryable<User> ApplyFilters(BaseFilter<User> filter, IQueryable<User> query)
{
var pagination = (filter.Page * filter.Quantity) - filter.Quantity;

IQueryable<User> listQuerable = DataContext.Users;

if (!string.IsNullOrWhiteSpace(filter.Query))
{
listQuerable = listQuerable.Where(x => x.Name.Contains(filter.Query));
}
if (!string.IsNullOrWhiteSpace(filter?.Entity?.Name))
{
listQuerable = listQuerable.Where(x => x.Name.Contains(filter.Entity.Name));
}

var customerList = await listQuerable.OrderBy(x => x.Name).Skip(pagination).Take(filter.Quantity)
.AsNoTracking()
.ToListAsync();
query = query.Where(x => x.Name.Contains(filter.Query));

return new PagedList<User>(customerList, listQuerable.Count());
return query;
}

public async Task<User> FindByEmailAsync(string email)
Expand Down
2 changes: 1 addition & 1 deletion src/Orion.Domain/Repositories/ICustomerRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ namespace Orion.Domain.Repositories
{
public interface ICustomerRepository : IBaseEntityRepository<Customer>
{
Task<PagedList<Customer>> ListPaginate(CustomerFilter filter);
Task<PagedList<Customer>> ListPaginateAsync(BaseFilter<Customer> filter);
}
}
2 changes: 1 addition & 1 deletion src/Orion.Domain/Repositories/IUserRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Orion.Domain.Repositories
public interface IUserRepository : IBaseEntityRepository<User>
{
Task<User> LoginAsync(string email, string password);
Task<PagedList<User>> ListPaginateAsync(UserFilter filter);
Task<PagedList<User>> ListPaginateAsync(BaseFilter<User> filter);
Task<User> FindByEmailAsync(string email);
}
}
4 changes: 2 additions & 2 deletions src/Orion.Domain/Services/CustomerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ public async Task<Customer> FindByIdAsync(string publicId)
return await _unitOfWork.CustomerRepository.GetByIdAsync(publicId);
}

public async Task<PagedList<Customer>> ListPaginateAsync(CustomerFilter filter)
public async Task<PagedList<Customer>> ListPaginateAsync(BaseFilter<Customer> filter)
{
return await _unitOfWork.CustomerRepository.ListPaginate(filter);
return await _unitOfWork.CustomerRepository.ListPaginateAsync(filter);
}

public async Task UpdateAsync(Customer entity)
Expand Down
2 changes: 1 addition & 1 deletion src/Orion.Domain/Services/Interfaces/ICustomerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ namespace Orion.Domain.Services.Interfaces
{
public interface ICustomerService : IBaseService<Customer>
{
Task<PagedList<Customer>> ListPaginateAsync(CustomerFilter filter);
Task<PagedList<Customer>> ListPaginateAsync(BaseFilter<Customer> filter);
}
}
2 changes: 1 addition & 1 deletion src/Orion.Domain/Services/Interfaces/IUserService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ public interface IUserService : IBaseService<User>
Task<User> LoginAsync(string email, string password);
Task<RefreshToken> AddRefreshTokenAsync(RefreshToken refreshToken);
Task<User> GetUserByRefreshTokenAsync(string refreshToken);
Task<PagedList<User>> ListPaginateAsync(UserFilter filter);
Task<PagedList<User>> ListPaginateAsync(BaseFilter<User> filter);
}
}
2 changes: 1 addition & 1 deletion src/Orion.Domain/Services/UserService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public async Task<User> GetUserByRefreshTokenAsync(string refreshToken)
throw new UnauthorizedUserException(_messages[UserMessages.InvalidRefreshToken], _messages[ExceptionsTitles.AuthenticationError]);
}

public async Task<PagedList<User>> ListPaginateAsync(UserFilter filter)
public async Task<PagedList<User>> ListPaginateAsync(BaseFilter<User> filter)
{
return await _unitOfWork.UserRepository.ListPaginateAsync(filter);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Orion.Entities/Filter/BaseFilter.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Orion.Entities.Filter
{
public abstract class BaseFilter<T>
public class BaseFilter<T>
{
public T Entity { get; set; }
public int Page { get; set; } = 1;
Expand Down
9 changes: 0 additions & 9 deletions src/Orion.Entities/Filter/CustomerFilter.cs

This file was deleted.

9 changes: 0 additions & 9 deletions src/Orion.Entities/Filter/UserFilter.cs

This file was deleted.

4 changes: 2 additions & 2 deletions tests/Orion.Test/Api/Controllers/CustomersControllerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public async Task GetCustomers_WithPagination_ReturnsAllCustomers()
var expectedCount = 4;

//act
var result = await _customersController.Get(new CustomerFilter());
var result = await _customersController.Get(new BaseFilter<Customer>());

var contentResult = (OkObjectResult)result;
var customersPagedList = (PagedList<CustomerOutput>)contentResult.Value;
Expand All @@ -119,7 +119,7 @@ private void SetupServiceMock()
customerServiceMock.Setup(x => x.AddAsync(It.Is<Customer>(x => x.Name == _validCustomerInput.Name))).ReturnsAsync(_validCustomer);
customerServiceMock.Setup(x => x.UpdateAsync(It.IsAny<Customer>())).Verifiable();
customerServiceMock.Setup(x => x.DeleteAsync(CustomerFaker.Get().PublicId)).Verifiable();
customerServiceMock.Setup(x => x.ListPaginateAsync(It.IsAny<CustomerFilter>())).
customerServiceMock.Setup(x => x.ListPaginateAsync(It.IsAny<BaseFilter<Customer>>())).
ReturnsAsync(customerListPaginated);

_customersController = new CustomersController(customerServiceMock.Object, Mapper);
Expand Down
4 changes: 2 additions & 2 deletions tests/Orion.Test/Api/Controllers/UsersControllerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public async Task DeleteUser_WithExistantId_DeleteUser()
public async Task GetUsers_WithValidFilter_ReturnsAListOfUsers()
{
//arrange & act
var result = await _usersController.Get(new UserFilter());
var result = await _usersController.Get(new BaseFilter<User>());

var contentResult = (OkObjectResult)result;
var userPagedList = (PagedList<UserOutput>)contentResult.Value;
Expand All @@ -113,7 +113,7 @@ private void SetupServiceMock()
userServiceMock.Setup(x => x.AddAsync(It.IsAny<User>())).ReturnsAsync(UserFaker.Get());
userServiceMock.Setup(x => x.UpdateAsync(It.IsAny<User>())).Verifiable();
userServiceMock.Setup(x => x.DeleteAsync(_validUser.PublicId)).Verifiable();
userServiceMock.Setup(x => x.ListPaginateAsync(It.IsAny<UserFilter>())).
userServiceMock.Setup(x => x.ListPaginateAsync(It.IsAny<BaseFilter<User>>())).
ReturnsAsync(userListPaginated);

_usersController = new UsersController(userServiceMock.Object, Mapper);
Expand Down
7 changes: 4 additions & 3 deletions tests/Orion.Test/Domain/Services/CustomerServiceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Orion.Test.MotherObjects;
using Xunit;
using Orion.Test.Domain.Services.BaseService;
using Orion.Entities.Domain;

namespace Orion.Test.Domain.Services
{
Expand Down Expand Up @@ -104,7 +105,7 @@ public async Task ListPaginateAsync_WithEmptyFilter_GetAllfCustomers()
var customerSaved = await customerService.AddAsync(customer);
var customerSaved2 = await customerService.AddAsync(customer2);

var customerList = await customerService.ListPaginateAsync(new CustomerFilter { Quantity = 99 });
var customerList = await customerService.ListPaginateAsync(new BaseFilter<Customer> { Quantity = 99 });

//aseert
Assert.NotNull(customerList);
Expand All @@ -126,7 +127,7 @@ public async Task ListPaginateAsync_WithFilterByName_GetAllMatchedCustomers()
var customerSaved = await customerService.AddAsync(customer);
var customerSaved2 = await customerService.AddAsync(customer2);

var customerList = await customerService.ListPaginateAsync(new CustomerFilter { Query = customer.Name });
var customerList = await customerService.ListPaginateAsync(new BaseFilter<Customer> { Query = customer.Name });

//assert
Assert.NotNull(customerList);
Expand All @@ -149,7 +150,7 @@ public async Task ListPaginateAsync_WithConfiguredQuantity_ReturnsListWithTheExp
await customerService.AddAsync(CustomerFaker.Get());

//act
var customerList = await customerService.ListPaginateAsync(new CustomerFilter { Quantity = expectedQuantity });
var customerList = await customerService.ListPaginateAsync(new BaseFilter<Customer> { Quantity = expectedQuantity });

//assert
Assert.NotNull(customerList);
Expand Down
4 changes: 2 additions & 2 deletions tests/Orion.Test/Domain/Services/UserServiceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,10 @@ public async Task ListPaginateAsync_WithFilterByName_GetAllMatchedUsers()

//act
var userPaginated = await userService.ListPaginateAsync(
new UserFilter
new BaseFilter<User>
{
Query = user.Name,
Entity = new User
Entity = new ()
{
Name = user.Name
}
Expand Down

0 comments on commit 1699e0f

Please sign in to comment.