Skip to content

Commit

Permalink
Merge pull request #100 from lvchkn/paging
Browse files Browse the repository at this point in the history
add paging
  • Loading branch information
lvchkn authored Aug 20, 2023
2 parents 6946682 + 398c2a7 commit eb20547
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 37 deletions.
2 changes: 1 addition & 1 deletion Application/Sort/ISorter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

public interface ISorter<T> where T : class
{
List<T> Sort(IQueryable<T> unsorted, IEnumerable<SortingParameters> parameters);
IQueryable<T> Sort(IQueryable<T> unsorted, IEnumerable<SortingParameters> parameters);
}

2 changes: 1 addition & 1 deletion Application/Stories/IStoriesRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public interface IStoriesRepository
Task<Story?> GetByIdAsync(int id);
Task<List<Story>> GetByAuthorAsync(string author);
Task<List<Story>> GetAllAsync();
List<Story> GetAll(IEnumerable<SortingParameters> sortingParameters, string? search);
List<Story> GetAll(IEnumerable<SortingParameters> sortingParameters, string? search, int skip, int take);
Task AddAsync(Story story);
Task UpdateAsync(int id, Story updatedStory);
Task DeleteAsync(int id);
Expand Down
2 changes: 1 addition & 1 deletion Application/Stories/IStoriesService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ public interface IStoriesService
{
Task AddAsync(StoryDto storyDto);
Task<List<StoryDto>> GetAllAsync();
List<StoryDto> GetStories(string? orderBy, string? search);
List<StoryDto> GetStories(string? orderBy, string? search, int pageNumber, int pageSize);
}
7 changes: 5 additions & 2 deletions Application/Stories/StoriesService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,13 @@ public async Task<List<StoryDto>> GetAllAsync()
return _mapper.Map<List<StoryDto>>(stories);
}

public List<StoryDto> GetStories(string? orderBy, string? search)
public List<StoryDto> GetStories(string? orderBy, string? search, int pageNumber, int pageSize)
{
var parsedSortingParameters = _sortingParameteresParser.Parse(orderBy);
var sortedStories = _storiesRepository.GetAll(parsedSortingParameters, search);
var skip = (pageNumber - 1) * pageSize;
var take = pageSize;

var sortedStories = _storiesRepository.GetAll(parsedSortingParameters, search, skip, take);

return _mapper.Map<List<StoryDto>>(sortedStories);
}
Expand Down
4 changes: 3 additions & 1 deletion HackerNewsCommentsFeed/Controllers/StoriesController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ public static IEndpointRouteBuilder MapItemsEndpoints(this IEndpointRouteBuilder
app.MapGet("/api/stories", (
[FromQuery] string? orderBy,
[FromQuery] string? search,
[FromQuery] int? pageNumber,
[FromQuery] int? pageSize,
[FromServices] IStoriesService storiesService) =>
{
var stories = storiesService.GetStories(orderBy, search);
var stories = storiesService.GetStories(orderBy, search, pageNumber ?? 1, pageSize ?? 10);
return Results.Ok(stories);
Expand Down
10 changes: 8 additions & 2 deletions Infrastructure/Db/Repositories/StoriesRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public async Task<List<Story>> GetAllAsync()
return stories;
}

public List<Story> GetAll(IEnumerable<SortingParameters> sortingParameters, string? search)
public List<Story> GetAll(IEnumerable<SortingParameters> sortingParameters, string? search, int skip, int take)
{
var included = _dbContext.Stories
.AsNoTracking()
Expand All @@ -70,7 +70,13 @@ public List<Story> GetAll(IEnumerable<SortingParameters> sortingParameters, stri
.AsSplitQuery();

var filteredStories = _storiesFilter.Filter(included, search);
var sortedStories = _storiesSorter.Sort(filteredStories, sortingParameters);

var sortedStories = _storiesSorter
.Sort(filteredStories, sortingParameters)
.OrderByDescending(s => s.Time)
.Skip(skip)
.Take(take)
.ToList();

return sortedStories;
}
Expand Down
4 changes: 2 additions & 2 deletions Infrastructure/Db/StoriesSorter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Infrastructure.Db;

public class StoriesSorter : ISorter<Story>
{
public List<Story> Sort(IQueryable<Story> stories, IEnumerable<SortingParameters> parameters)
public IQueryable<Story> Sort(IQueryable<Story> stories, IEnumerable<SortingParameters> parameters)
{
var index = 0;

Expand All @@ -16,7 +16,7 @@ public List<Story> Sort(IQueryable<Story> stories, IEnumerable<SortingParameters
stories = UpdateListOrder(stories, field, order, thenable);
}

return stories.ToList();
return stories;
}

private static IQueryable<Story> UpdateListOrder(
Expand Down
65 changes: 38 additions & 27 deletions Tests/Integration/StoriesControllerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ public class StoriesControllerTests
private readonly CustomWebApplicationFactory<Program> _webAppFactory;
private record CreateInterestRequest(string Text, int? Id);

private readonly JsonSerializerOptions _jsonSerializerOptions = new ()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};

public StoriesControllerTests(CustomWebApplicationFactory<Program> webAppFactory)
{
_webAppFactory = webAppFactory;
Expand All @@ -26,22 +31,17 @@ public StoriesControllerTests(CustomWebApplicationFactory<Program> webAppFactory
public async Task All_stories_are_returned_when_no_query_provided()
{
// Arrange
var jsonSerializerOptions = new JsonSerializerOptions()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};

var client = _webAppFactory.CreateClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Test");

using var scope = _webAppFactory.Services.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
dbContext?.SeedStories();

// Act
var response = await client.GetAsync("/api/stories");
var responseJson = await response.Content.ReadAsStringAsync();
var returnedStories = JsonSerializer.Deserialize<StoryDto[]>(responseJson, jsonSerializerOptions);
var returnedStories = JsonSerializer.Deserialize<StoryDto[]>(responseJson, _jsonSerializerOptions);

// Assert
returnedStories.Should().NotBeNull();
Expand All @@ -52,14 +52,9 @@ public async Task All_stories_are_returned_when_no_query_provided()
public async Task Stories_are_sorted_by_title_in_desc_order()
{
// Arrange
var jsonSerializerOptions = new JsonSerializerOptions()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};

var client = _webAppFactory.CreateClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Test");

using var scope = _webAppFactory.Services.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
dbContext?.SeedStories();
Expand All @@ -72,7 +67,7 @@ public async Task Stories_are_sorted_by_title_in_desc_order()
// Act
var response = await client.GetAsync("/api/stories?orderBy=title,asc");
var responseJson = await response.Content.ReadAsStringAsync();
var returnedStories = JsonSerializer.Deserialize<StoryDto[]>(responseJson, jsonSerializerOptions);
var returnedStories = JsonSerializer.Deserialize<StoryDto[]>(responseJson, _jsonSerializerOptions);

// Assert
returnedStories.Should().BeEquivalentTo(expectedResults);
Expand All @@ -85,22 +80,17 @@ public async Task Stories_are_sorted_by_title_in_desc_order()
public async Task Stories_are_filtered_by_title_with_fuzzy_search(string search)
{
// Arrange
var jsonSerializerOptions = new JsonSerializerOptions()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};

var client = _webAppFactory.CreateClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Test");

using var scope = _webAppFactory.Services.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
dbContext?.SeedStories();

// Act
var response = await client.GetAsync($"/api/stories?search={search}");
var responseJson = await response.Content.ReadAsStringAsync();
var returnedStories = JsonSerializer.Deserialize<StoryDto[]>(responseJson, jsonSerializerOptions);
var returnedStories = JsonSerializer.Deserialize<StoryDto[]>(responseJson, _jsonSerializerOptions);

// Assert
returnedStories?.Length.Should().Be(5);
Expand All @@ -113,11 +103,6 @@ public async Task Stories_are_filtered_by_title_with_fuzzy_search(string search)
public async Task Stories_are_ordered_and_filtered(string search)
{
// Arrange
var jsonSerializerOptions = new JsonSerializerOptions()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};

var client = _webAppFactory.CreateClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Test");

Expand All @@ -135,12 +120,38 @@ public async Task Stories_are_ordered_and_filtered(string search)
// Act
var response = await client.GetAsync($"/api/stories?orderBy={orderBy}&search={search}");
var responseJson = await response.Content.ReadAsStringAsync();
var returnedStories = JsonSerializer.Deserialize<StoryDto[]>(responseJson, jsonSerializerOptions);
var returnedStories = JsonSerializer.Deserialize<StoryDto[]>(responseJson, _jsonSerializerOptions);

// Assert
returnedStories?.Should().BeEquivalentTo(expectedResults);
}

[Theory]
[InlineData(2, 2, 2)]
[InlineData(2, 1, 1)]
[InlineData(4, 1, 1)]
[InlineData(5, 0, 0)]
[InlineData(5, 1, 1)]
[InlineData(1, 5, 5)]
[InlineData(3, 2, 1)]
public async Task Pagination_Works(int pageNumber, int pageSize, int result)
{
// Arrange
var client = _webAppFactory.CreateClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Test");

using var scope = _webAppFactory.Services.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
dbContext?.SeedStories();

// Act
var response = await client.GetAsync($"/api/stories?pageNumber={pageNumber}&pageSize={pageSize}");
var responseJson = await response.Content.ReadAsStringAsync();
var returnedStories = JsonSerializer.Deserialize<StoryDto[]>(responseJson, _jsonSerializerOptions);
// Assert
returnedStories?.Length.Should().Be(result);
}

private static StoryDto MapToStoryDto(Story story)
{
return new StoryDto()
Expand Down

0 comments on commit eb20547

Please sign in to comment.