diff --git a/src/Orion.Api/Controllers/CustomersController.cs b/src/Orion.Api/Controllers/CustomersController.cs index 2a85198..53dec04 100644 --- a/src/Orion.Api/Controllers/CustomersController.cs +++ b/src/Orion.Api/Controllers/CustomersController.cs @@ -26,7 +26,7 @@ public CustomersController(ICustomerService customerService, IMapper mapper) : b [HttpGet] [ProducesResponseType((int)HttpStatusCode.OK)] - public async Task Get([FromQuery] CustomerFilter filter) + public async Task Get([FromQuery] BaseFilter filter) { var customer = await _customerService.ListPaginateAsync(filter); diff --git a/src/Orion.Api/Controllers/UsersController.cs b/src/Orion.Api/Controllers/UsersController.cs index 55e9620..09a07e4 100644 --- a/src/Orion.Api/Controllers/UsersController.cs +++ b/src/Orion.Api/Controllers/UsersController.cs @@ -26,7 +26,7 @@ public UsersController(IUserService userService, IMapper mapper) : base(mapper) [HttpGet] [ProducesResponseType((int)HttpStatusCode.OK)] - public async Task Get([FromQuery] UserFilter filter) + public async Task Get([FromQuery] BaseFilter filter) { var user = await _userService.ListPaginateAsync(filter); diff --git a/src/Orion.Api/appsettings.Development.json b/src/Orion.Api/appsettings.Development.json index fa32847..4b0a89c 100644 --- a/src/Orion.Api/appsettings.Development.json +++ b/src/Orion.Api/appsettings.Development.json @@ -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" diff --git a/src/Orion.Data/Repository/Generic/BaseEntityRepository.cs b/src/Orion.Data/Repository/Generic/BaseEntityRepository.cs index 66de50d..00f1bac 100644 --- a/src/Orion.Data/Repository/Generic/BaseEntityRepository.cs +++ b/src/Orion.Data/Repository/Generic/BaseEntityRepository.cs @@ -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 : IBaseEntityRepository where T : BaseEntity + public abstract class BaseEntityRepository : IBaseEntityRepository where T : BaseEntity { protected DataContext DataContext { get; } @@ -18,14 +20,14 @@ public BaseEntityRepository(DataContext dataContext) DataContext = dataContext; } - public async Task AddAsync(T entity) + public virtual async Task AddAsync(T entity) { var added = await DataContext.Set().AddAsync(entity); return added.Entity; } - public async Task DeleteAsync(string publicId) + public virtual async Task DeleteAsync(string publicId) { var existing = await GetByIdAsync(publicId); @@ -41,16 +43,44 @@ public virtual async Task GetByIdAsync(string publicId) return await DataContext.Set().AsNoTracking().SingleOrDefaultAsync(x => x.PublicId == publicId); } - public async Task> SearchByAsync(Expression> predicate) + public virtual async Task> SearchByAsync(Expression> predicate) { return await DataContext.Set().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().Update(entity); } + + public virtual async Task> ListPaginateAsync(BaseFilter filter) + { + IQueryable query = DataContext.Set(); + + 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(entityList, query.Count()); + } + + /// + /// 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 + /// + /// + /// + /// IQuerable with filters applied + protected virtual IQueryable ApplyFilters(BaseFilter filter, IQueryable query) + { + return query; + } } } diff --git a/src/Orion.Data/Repository/Implementations/CustomerRepository.cs b/src/Orion.Data/Repository/Implementations/CustomerRepository.cs index 5bfa468..fd437af 100644 --- a/src/Orion.Data/Repository/Implementations/CustomerRepository.cs +++ b/src/Orion.Data/Repository/Implementations/CustomerRepository.cs @@ -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 { @@ -17,20 +14,12 @@ public CustomerRepository(DataContext context) : base(context) } - public async Task> ListPaginate(CustomerFilter filter) + protected override IQueryable ApplyFilters(BaseFilter filter, IQueryable query) { - var pagination = (filter.Page * filter.Quantity) - filter.Quantity; - - IQueryable 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(customerList, listQuerable.Count()); + return query; } } } diff --git a/src/Orion.Data/Repository/Implementations/UserRepository.cs b/src/Orion.Data/Repository/Implementations/UserRepository.cs index 0151a12..d65d968 100644 --- a/src/Orion.Data/Repository/Implementations/UserRepository.cs +++ b/src/Orion.Data/Repository/Implementations/UserRepository.cs @@ -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 { @@ -18,31 +17,19 @@ public UserRepository(DataContext context) : base(context) public async Task 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> ListPaginateAsync(UserFilter filter) + protected override IQueryable ApplyFilters(BaseFilter filter, IQueryable query) { - var pagination = (filter.Page * filter.Quantity) - filter.Quantity; - - IQueryable 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(customerList, listQuerable.Count()); + return query; } public async Task FindByEmailAsync(string email) diff --git a/src/Orion.Domain/Repositories/ICustomerRepository.cs b/src/Orion.Domain/Repositories/ICustomerRepository.cs index b9d0b0e..bb84688 100644 --- a/src/Orion.Domain/Repositories/ICustomerRepository.cs +++ b/src/Orion.Domain/Repositories/ICustomerRepository.cs @@ -8,6 +8,6 @@ namespace Orion.Domain.Repositories { public interface ICustomerRepository : IBaseEntityRepository { - Task> ListPaginate(CustomerFilter filter); + Task> ListPaginateAsync(BaseFilter filter); } } diff --git a/src/Orion.Domain/Repositories/IUserRepository.cs b/src/Orion.Domain/Repositories/IUserRepository.cs index 93ac7dc..a159d80 100644 --- a/src/Orion.Domain/Repositories/IUserRepository.cs +++ b/src/Orion.Domain/Repositories/IUserRepository.cs @@ -9,7 +9,7 @@ namespace Orion.Domain.Repositories public interface IUserRepository : IBaseEntityRepository { Task LoginAsync(string email, string password); - Task> ListPaginateAsync(UserFilter filter); + Task> ListPaginateAsync(BaseFilter filter); Task FindByEmailAsync(string email); } } diff --git a/src/Orion.Domain/Services/CustomerService.cs b/src/Orion.Domain/Services/CustomerService.cs index 6184170..25d1625 100644 --- a/src/Orion.Domain/Services/CustomerService.cs +++ b/src/Orion.Domain/Services/CustomerService.cs @@ -43,9 +43,9 @@ public async Task FindByIdAsync(string publicId) return await _unitOfWork.CustomerRepository.GetByIdAsync(publicId); } - public async Task> ListPaginateAsync(CustomerFilter filter) + public async Task> ListPaginateAsync(BaseFilter filter) { - return await _unitOfWork.CustomerRepository.ListPaginate(filter); + return await _unitOfWork.CustomerRepository.ListPaginateAsync(filter); } public async Task UpdateAsync(Customer entity) diff --git a/src/Orion.Domain/Services/Interfaces/ICustomerService.cs b/src/Orion.Domain/Services/Interfaces/ICustomerService.cs index df35d13..edd9b86 100644 --- a/src/Orion.Domain/Services/Interfaces/ICustomerService.cs +++ b/src/Orion.Domain/Services/Interfaces/ICustomerService.cs @@ -8,6 +8,6 @@ namespace Orion.Domain.Services.Interfaces { public interface ICustomerService : IBaseService { - Task> ListPaginateAsync(CustomerFilter filter); + Task> ListPaginateAsync(BaseFilter filter); } } diff --git a/src/Orion.Domain/Services/Interfaces/IUserService.cs b/src/Orion.Domain/Services/Interfaces/IUserService.cs index 30caea0..c163cee 100644 --- a/src/Orion.Domain/Services/Interfaces/IUserService.cs +++ b/src/Orion.Domain/Services/Interfaces/IUserService.cs @@ -11,6 +11,6 @@ public interface IUserService : IBaseService Task LoginAsync(string email, string password); Task AddRefreshTokenAsync(RefreshToken refreshToken); Task GetUserByRefreshTokenAsync(string refreshToken); - Task> ListPaginateAsync(UserFilter filter); + Task> ListPaginateAsync(BaseFilter filter); } } diff --git a/src/Orion.Domain/Services/UserService.cs b/src/Orion.Domain/Services/UserService.cs index 6021087..49d666f 100644 --- a/src/Orion.Domain/Services/UserService.cs +++ b/src/Orion.Domain/Services/UserService.cs @@ -121,7 +121,7 @@ public async Task GetUserByRefreshTokenAsync(string refreshToken) throw new UnauthorizedUserException(_messages[UserMessages.InvalidRefreshToken], _messages[ExceptionsTitles.AuthenticationError]); } - public async Task> ListPaginateAsync(UserFilter filter) + public async Task> ListPaginateAsync(BaseFilter filter) { return await _unitOfWork.UserRepository.ListPaginateAsync(filter); } diff --git a/src/Orion.Entities/Filter/BaseFilter.cs b/src/Orion.Entities/Filter/BaseFilter.cs index 7ed6633..257b5b4 100644 --- a/src/Orion.Entities/Filter/BaseFilter.cs +++ b/src/Orion.Entities/Filter/BaseFilter.cs @@ -1,6 +1,6 @@ namespace Orion.Entities.Filter { - public abstract class BaseFilter + public class BaseFilter { public T Entity { get; set; } public int Page { get; set; } = 1; diff --git a/src/Orion.Entities/Filter/CustomerFilter.cs b/src/Orion.Entities/Filter/CustomerFilter.cs deleted file mode 100644 index f07d3ed..0000000 --- a/src/Orion.Entities/Filter/CustomerFilter.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Orion.Entities.Domain; - -namespace Orion.Entities.Filter -{ - public class CustomerFilter : BaseFilter - { - - } -} diff --git a/src/Orion.Entities/Filter/UserFilter.cs b/src/Orion.Entities/Filter/UserFilter.cs deleted file mode 100644 index 39c8adb..0000000 --- a/src/Orion.Entities/Filter/UserFilter.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Orion.Entities.Domain; - -namespace Orion.Entities.Filter -{ - public class UserFilter : BaseFilter - { - - } -} diff --git a/tests/Orion.Test/Api/Controllers/CustomersControllerTest.cs b/tests/Orion.Test/Api/Controllers/CustomersControllerTest.cs index d1044dd..c3027a6 100644 --- a/tests/Orion.Test/Api/Controllers/CustomersControllerTest.cs +++ b/tests/Orion.Test/Api/Controllers/CustomersControllerTest.cs @@ -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()); var contentResult = (OkObjectResult)result; var customersPagedList = (PagedList)contentResult.Value; @@ -119,7 +119,7 @@ private void SetupServiceMock() customerServiceMock.Setup(x => x.AddAsync(It.Is(x => x.Name == _validCustomerInput.Name))).ReturnsAsync(_validCustomer); customerServiceMock.Setup(x => x.UpdateAsync(It.IsAny())).Verifiable(); customerServiceMock.Setup(x => x.DeleteAsync(CustomerFaker.Get().PublicId)).Verifiable(); - customerServiceMock.Setup(x => x.ListPaginateAsync(It.IsAny())). + customerServiceMock.Setup(x => x.ListPaginateAsync(It.IsAny>())). ReturnsAsync(customerListPaginated); _customersController = new CustomersController(customerServiceMock.Object, Mapper); diff --git a/tests/Orion.Test/Api/Controllers/UsersControllerTest.cs b/tests/Orion.Test/Api/Controllers/UsersControllerTest.cs index dde566b..9ac650a 100644 --- a/tests/Orion.Test/Api/Controllers/UsersControllerTest.cs +++ b/tests/Orion.Test/Api/Controllers/UsersControllerTest.cs @@ -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()); var contentResult = (OkObjectResult)result; var userPagedList = (PagedList)contentResult.Value; @@ -113,7 +113,7 @@ private void SetupServiceMock() userServiceMock.Setup(x => x.AddAsync(It.IsAny())).ReturnsAsync(UserFaker.Get()); userServiceMock.Setup(x => x.UpdateAsync(It.IsAny())).Verifiable(); userServiceMock.Setup(x => x.DeleteAsync(_validUser.PublicId)).Verifiable(); - userServiceMock.Setup(x => x.ListPaginateAsync(It.IsAny())). + userServiceMock.Setup(x => x.ListPaginateAsync(It.IsAny>())). ReturnsAsync(userListPaginated); _usersController = new UsersController(userServiceMock.Object, Mapper); diff --git a/tests/Orion.Test/Domain/Services/CustomerServiceTest.cs b/tests/Orion.Test/Domain/Services/CustomerServiceTest.cs index bb2a684..ab0ae42 100644 --- a/tests/Orion.Test/Domain/Services/CustomerServiceTest.cs +++ b/tests/Orion.Test/Domain/Services/CustomerServiceTest.cs @@ -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 { @@ -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 { Quantity = 99 }); //aseert Assert.NotNull(customerList); @@ -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 { Query = customer.Name }); //assert Assert.NotNull(customerList); @@ -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 { Quantity = expectedQuantity }); //assert Assert.NotNull(customerList); diff --git a/tests/Orion.Test/Domain/Services/UserServiceTest.cs b/tests/Orion.Test/Domain/Services/UserServiceTest.cs index 22eaaff..3be2b90 100644 --- a/tests/Orion.Test/Domain/Services/UserServiceTest.cs +++ b/tests/Orion.Test/Domain/Services/UserServiceTest.cs @@ -80,10 +80,10 @@ public async Task ListPaginateAsync_WithFilterByName_GetAllMatchedUsers() //act var userPaginated = await userService.ListPaginateAsync( - new UserFilter + new BaseFilter { Query = user.Name, - Entity = new User + Entity = new () { Name = user.Name }