forked from gothinkster/realworld-starter-kit
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#20 get comments, query, tests, migration
- Loading branch information
Showing
14 changed files
with
345 additions
and
15 deletions.
There are no files selected for viewing
33 changes: 33 additions & 0 deletions
33
Conduit/Conduit.Migrations/008_CreateIndexForGetComments.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
using NoSqlMigrator.Infrastructure; | ||
|
||
namespace Conduit.Migrations; | ||
|
||
// Manual alternative: CREATE INDEX `idx_comments_author_follows2` ON `Conduit`.`_default`.`Comments`((meta().`id`),(distinct (array (`c`.`authorUsername`) for `c` in ((`Conduit`.`_default`).`Comments`) end))) | ||
[Migration(8)] | ||
public class CreateIndexForGetComments : MigrateBase | ||
{ | ||
private readonly string? _scopeName; | ||
|
||
public CreateIndexForGetComments() | ||
{ | ||
_scopeName = _config["Couchbase:ScopeName"]; | ||
} | ||
|
||
public override void Up() | ||
{ | ||
// there are other options for more complex and/or covering indexes | ||
// but this index is the simplest for the List Articles SQL++ query | ||
Create.Index("ix_get_comments") | ||
.OnScope(_scopeName) | ||
.OnCollection("Comments") | ||
.OnFieldRaw("META().`id`") | ||
.OnFieldRaw("DISTINCT (array (`c`.`authorUsername`) for `c` in ((`Conduit`.`_default`).`Comments`"); | ||
} | ||
|
||
public override void Down() | ||
{ | ||
Delete.Index("ix_get_comments") | ||
.FromScope(_scopeName) | ||
.FromCollection("Comments"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 95 additions & 0 deletions
95
...Conduit.Tests/Functional/Articles/Controllers/CommentsControllerTests/GetCommentsTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
using System.Net; | ||
using System.Net.Http.Headers; | ||
using Conduit.Tests.TestHelpers; | ||
using Conduit.Tests.TestHelpers.Data; | ||
using Conduit.Web.DataAccess.Models; | ||
using Conduit.Web.DataAccess.Providers; | ||
using Conduit.Web.Extensions; | ||
using Microsoft.Extensions.DependencyInjection; | ||
|
||
namespace Conduit.Tests.Functional.Articles.Controllers.CommentsControllerTests; | ||
|
||
[TestFixture] | ||
public class GetCommentsTests : FunctionalTestBase | ||
{ | ||
private IConduitArticlesCollectionProvider _articleCollectionProvider; | ||
private IConduitUsersCollectionProvider _usersCollectionProvider; | ||
private User _user; | ||
private Random _random; | ||
private IConduitCommentsCollectionProvider _commentsCollectionProvider; | ||
private IConduitFollowsCollectionProvider _followCollectionProvider; | ||
|
||
[SetUp] | ||
public async Task Setup() | ||
{ | ||
await base.Setup(); | ||
|
||
// setup database objects for arranging | ||
var service = WebAppFactory.Services; | ||
_articleCollectionProvider = service.GetRequiredService<IConduitArticlesCollectionProvider>(); | ||
_usersCollectionProvider = service.GetRequiredService<IConduitUsersCollectionProvider>(); | ||
_commentsCollectionProvider = service.GetRequiredService<IConduitCommentsCollectionProvider>(); | ||
_followCollectionProvider = service.GetRequiredService<IConduitFollowsCollectionProvider>(); | ||
|
||
// setup an authorized header | ||
_user = await _usersCollectionProvider.CreateUserInDatabase(); | ||
var jwtToken = AuthSvc.GenerateJwtToken(_user.Email, _user.Username); | ||
WebClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", jwtToken); | ||
|
||
_random = new Random(); | ||
} | ||
|
||
[Test] | ||
public async Task Comments_for_an_article_are_returned() | ||
{ | ||
// arrange - an article | ||
var author = await _usersCollectionProvider.CreateUserInDatabase(); | ||
var article = await _articleCollectionProvider.CreateArticleInDatabase(authorUsername: author.Username); | ||
// arrange - two comments from two different users | ||
var commenter1 = await _usersCollectionProvider.CreateUserInDatabase(); | ||
var commenter2 = await _usersCollectionProvider.CreateUserInDatabase(); | ||
var commentBody1 = _random.String(64); | ||
var commentBody2 = _random.String(64); | ||
await _commentsCollectionProvider.CreateCommentInDatabase(article.Slug, commenter1.Username, commentBody1); | ||
await _commentsCollectionProvider.CreateCommentInDatabase(article.Slug, commenter2.Username, commentBody2); | ||
|
||
// act | ||
var response = await WebClient.GetAsync($"/api/articles/{article.Slug}/comments"); | ||
var responseString = await response.Content.ReadAsStringAsync(); | ||
var articleViewModel = responseString.SubDoc<List<CommentListDataView>>("comments"); | ||
|
||
// assert | ||
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); | ||
Assert.That(articleViewModel.Count, Is.EqualTo(2)); | ||
await _commentsCollectionProvider.AssertExists(article.Slug, x => | ||
{ | ||
Assert.That(x.Any(c => c.Body == commentBody1)); | ||
Assert.That(x.Any(c => c.Body == commentBody2)); | ||
}); | ||
} | ||
|
||
[TestCase(true)] | ||
[TestCase(false)] | ||
public async Task Comments_include_correct_following_information(bool doesFollowAuthor) | ||
{ | ||
// arrange - an article | ||
var author = await _usersCollectionProvider.CreateUserInDatabase(); | ||
var article = await _articleCollectionProvider.CreateArticleInDatabase(authorUsername: author.Username); | ||
// arrange - comment | ||
var commenter = await _usersCollectionProvider.CreateUserInDatabase(); | ||
await _commentsCollectionProvider.CreateCommentInDatabase(article.Slug, commenter.Username, _random.String(64)); | ||
// arrange - follow (or not) the comment author | ||
if (doesFollowAuthor) | ||
await _followCollectionProvider.CreateFollow(commenter.Username, _user.Username); | ||
|
||
// act | ||
var response = await WebClient.GetAsync($"/api/articles/{article.Slug}/comments"); | ||
var responseString = await response.Content.ReadAsStringAsync(); | ||
var articleViewModel = responseString.SubDoc<List<CommentListDataView>>("comments"); | ||
|
||
// assert | ||
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); | ||
Assert.That(articleViewModel.Count, Is.EqualTo(1)); | ||
Assert.That(articleViewModel[0].Author.Following, Is.EqualTo(doesFollowAuthor)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,50 @@ | ||
using Conduit.Web.DataAccess.Models; | ||
using Conduit.Web.Articles.Services; | ||
using Conduit.Web.DataAccess.Models; | ||
using Conduit.Web.DataAccess.Providers; | ||
using Conduit.Web.Extensions; | ||
using Couchbase.KeyValue; | ||
|
||
namespace Conduit.Tests.TestHelpers.Data; | ||
|
||
public static class CommentHelper | ||
{ | ||
public static async Task AssertExists(this IConduitCommentsCollectionProvider @this, string key, Action<IList<Comment>>? assertions = null) | ||
public static async Task AssertExists(this IConduitCommentsCollectionProvider @this, string slug, Action<IList<Comment>>? assertions = null) | ||
{ | ||
var collection = await @this.GetCollectionAsync(); | ||
|
||
var key = CommentsDataService.GetCommentsKey(slug.GetArticleKey()); | ||
var doesExist = await collection.ExistsAsync(key); | ||
Assert.That(doesExist.Exists, Is.True); | ||
|
||
var listOfComments = collection.List<Comment>(key); | ||
if (assertions != null) | ||
assertions(listOfComments); | ||
} | ||
|
||
public static async Task<Comment> CreateCommentInDatabase(this IConduitCommentsCollectionProvider @this, | ||
string slug, | ||
string username, | ||
string? body = null, | ||
ulong? id = null) | ||
{ | ||
var random = new Random(); | ||
|
||
body ??= random.String(64); | ||
id ??= (ulong)random.Next(100000, 200000); | ||
|
||
var collection = await @this.GetCollectionAsync(); | ||
|
||
var key = CommentsDataService.GetCommentsKey(slug.GetArticleKey()); | ||
|
||
var comments = collection.List<Comment>(key); | ||
var comment = new Comment | ||
{ | ||
AuthorUsername = username, | ||
Body = body, | ||
CreatedAt = DateTimeOffset.Now, | ||
Id = id.Value | ||
}; | ||
await comments.AddAsync(comment); | ||
return comment; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
51 changes: 51 additions & 0 deletions
51
Conduit/Conduit.Web/Articles/Handlers/GetCommentsHandler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
using Conduit.Web.Articles.Services; | ||
using Conduit.Web.Articles.ViewModels; | ||
using Conduit.Web.Users.ViewModels; | ||
using MediatR; | ||
|
||
namespace Conduit.Web.Articles.Handlers; | ||
|
||
public class GetCommentsHandler : IRequestHandler<GetCommentsRequest, GetCommentsResponse> | ||
{ | ||
private readonly IArticlesDataService _articlesDataService; | ||
private readonly ICommentsDataService _commentsDataService; | ||
|
||
public GetCommentsHandler(IArticlesDataService articlesDataService, ICommentsDataService commentsDataService) | ||
{ | ||
_articlesDataService = articlesDataService; | ||
_commentsDataService = commentsDataService; | ||
} | ||
|
||
public async Task<GetCommentsResponse> Handle(GetCommentsRequest request, CancellationToken cancellationToken) | ||
{ | ||
// article must exist | ||
var doesArticleExist = await _articlesDataService.Exists(request.Slug); | ||
if (!doesArticleExist) | ||
{ | ||
return new GetCommentsResponse | ||
{ | ||
IsArticleNotFound = true | ||
}; | ||
} | ||
|
||
var results = await _commentsDataService.Get(request.Slug, request.Username); | ||
|
||
return new GetCommentsResponse | ||
{ | ||
CommentsView = results.DataResult.Select(x => new CommentViewModel | ||
{ | ||
Id = x.Id, | ||
Body = x.Body, | ||
CreatedAt = x.CreatedAt, | ||
UpdatedAt = x.CreatedAt, | ||
Author = new ProfileViewModel | ||
{ | ||
Bio = x.Author.Bio, | ||
Following = x.Author.Following, | ||
Image = x.Author.Image, | ||
Username = x.Author.Username | ||
} | ||
}).ToList() | ||
}; | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
Conduit/Conduit.Web/Articles/Handlers/GetCommentsRequest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
using Conduit.Web.DataAccess.Models; | ||
using MediatR; | ||
|
||
namespace Conduit.Web.Articles.Handlers; | ||
|
||
public class GetCommentsRequest : IRequest<GetCommentsResponse> | ||
{ | ||
public string? Username { get; set; } | ||
public string Slug { get; set; } | ||
|
||
public GetCommentsRequest(string slug, string? username) | ||
{ | ||
Slug = slug; | ||
Username = username; | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
Conduit/Conduit.Web/Articles/Handlers/GetCommentsResponse.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
using Conduit.Web.Articles.ViewModels; | ||
|
||
namespace Conduit.Web.Articles.Handlers; | ||
|
||
public class GetCommentsResponse | ||
{ | ||
public bool IsArticleNotFound { get; set; } | ||
public bool IsFailed { get; set; } | ||
public List<CommentViewModel> CommentsView { get; set; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.