Skip to content

Commit

Permalink
Merge pull request #116 from lvchkn/paged-api-response
Browse files Browse the repository at this point in the history
Change `/api/stories` return object to include the total number of pages
  • Loading branch information
lvchkn authored Oct 8, 2023
2 parents 75519b4 + 91be0e6 commit 7d400c6
Show file tree
Hide file tree
Showing 13 changed files with 177 additions and 43 deletions.
8 changes: 8 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
RMQ_USER=
RMQ_PW=
POSTGRES_DB=
POSTGRES_USER=
POSTGRES_PW=
GH_APP_NAME=
GH_CLIENT_ID=
GH_CLIENT_SECRET=
9 changes: 9 additions & 0 deletions Application/Paging/PagedStoriesDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Application.Stories;

namespace Application.Paging;

public record PagedStoriesDto
{
public List<StoryDto> Stories { get; init; } = new ();
public int TotalPagesCount { get; init; }
}
20 changes: 20 additions & 0 deletions Application/Paging/PagingExtension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Application.Stories;

namespace Application.Paging;

public static class PagingExtension
{
public static PagedStoriesDto Paginate(this IEnumerable<StoryDto> stories, int skip, int take)
{
if (take <= 0) throw new ArgumentOutOfRangeException();

var pagedStories = stories.Skip(skip).Take(take).ToList();
var totalPagesCount = (int)Math.Ceiling((double)stories.Count() / take);

return new ()
{
Stories = pagedStories,
TotalPagesCount = totalPagesCount,
};
}
}
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, int skip, int take);
List<Story> GetAll(IEnumerable<SortingParameters> sortingParameters, string? search);
Task AddAsync(Story story);
Task UpdateAsync(int id, Story updatedStory);
Task DeleteAsync(int id);
Expand Down
4 changes: 3 additions & 1 deletion Application/Stories/IStoriesService.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using Application.Paging;

namespace Application.Stories;

public interface IStoriesService
{
Task AddAsync(StoryDto storyDto);
Task<List<StoryDto>> GetAllAsync();
List<StoryDto> GetStories(string? orderBy, string? search, int pageNumber, int pageSize);
PagedStoriesDto GetStories(string? orderBy, string? search, int pageNumber, int pageSize);
}
11 changes: 7 additions & 4 deletions Application/Stories/StoriesService.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Application.Paging;
using Application.Sort;
using AutoMapper;
using Domain.Entities;
Expand Down Expand Up @@ -35,17 +36,19 @@ public async Task<List<StoryDto>> GetAllAsync()
return _mapper.Map<List<StoryDto>>(stories);
}

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

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

var dtos = _mapper.Map<List<StoryDto>>(sortedStories);
var rankedStories = _rankingService.Rank(dtos);

return rankedStories;
var pagedStories = rankedStories.Paginate(skip, take);

return pagedStories;
}
}
}
14 changes: 14 additions & 0 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
This is a respectful environment for everyone before everything else!

I'd like to ensure that everyone can participate in this project in a friendly and constructive manner.

So, the **standard guidelines** are:

- Be respectful
- Be collaborative

The things I consider **inappropriate**:

- Personal attacks, insults, or derogatory comments.
- Posting or sharing explicit content.
- Sharing personal information without consent.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ private static Task HandleExceptionAsync(HttpContext httpContext, Exception exce
{
NotFoundException => (int) HttpStatusCode.NotFound,
AlreadyExistsException => (int) HttpStatusCode.BadRequest,
ArgumentException => (int) HttpStatusCode.BadRequest,
_ => (int) HttpStatusCode.InternalServerError
};

Expand Down
6 changes: 5 additions & 1 deletion HackerNewsCommentsFeed/Configuration/Registrations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ private static IServiceCollection AddGithubAuth(this IServiceCollection services
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = "Github";
})
.AddCookie()
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => {
options.Cookie.SameSite = SameSiteMode.None;
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
})
.AddOAuth("Github", config =>
{
config.ClientId = clientId ?? string.Empty;
Expand Down
5 changes: 1 addition & 4 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, int skip, int take)
public List<Story> GetAll(IEnumerable<SortingParameters> sortingParameters, string? search)
{
var included = _dbContext.Stories
.AsNoTracking()
Expand All @@ -73,9 +73,6 @@ public List<Story> GetAll(IEnumerable<SortingParameters> sortingParameters, stri

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

return sortedStories;
Expand Down
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,29 @@
[![.NET](https://github.com/lvchkn/Hackernews-Feed/actions/workflows/build-and-test.yml/badge.svg?branch=main)](https://github.com/lvchkn/Hackernews-Feed/actions/workflows/build-and-test.yml)

# Hackernews-Feed

This API acts as a kind of proxy backend for my custom [HN client](https://github.com/lvchkn/hn-client) and provides RESTful endpoints that allow for sorting, filtering and paging stories from Hackernews.

This is only partially implemented and still is under development, but the idea is that users may authenticate to the custom client page via Github, specify their topics of interest, and get a personal stories feed based on their preferences.

## How to run locally

1. Create a `.env` file in the project's root directory and fill it in according to the `.env.example` file.
1. Start the compose stack

```bash
docker compose -f docker-compose.local up
```

1. Run the following command from the root's directory:
```bash
dotnet run --project HackerNewsCommentsFeed/HackerNewsCommentsFeed.csproj
```
1. Navigate to `http://localhost/index.html` or `https://localhost:7245` to see the Swagger OpenAPI definition.
## How to run the tests
Just run the `dotnet test` command in the project's root directory.
Note that the entries in the `.env` file related to the authentication must be prepopulated before running the tests to avoid errors during the test server's startup.
15 changes: 10 additions & 5 deletions Tests/Integration/DataSeeder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ namespace Tests.Integration;

public static class DataSeeder
{
public static void SeedStories(this AppDbContext dbContext)
public static int SeedStories(this AppDbContext dbContext)
{
dbContext.Stories.RemoveRange(dbContext.Stories);

dbContext.Stories?.AddRange(

var stories = new []
{
new Story()
{
By = "User1",
Expand Down Expand Up @@ -44,10 +45,14 @@ public static void SeedStories(this AppDbContext dbContext)
Title = "B Story",
Score = 250,
Time = 9,
}
);
},
};

dbContext.Stories?.AddRange(stories);

dbContext.SaveChanges();

return stories.Length;
}

public static void SeedUsers(this AppDbContext dbContext)
Expand Down
Loading

0 comments on commit 7d400c6

Please sign in to comment.