Skip to content

Commit

Permalink
#24 added endpoint to get all valid tags
Browse files Browse the repository at this point in the history
  • Loading branch information
mgroves committed Aug 8, 2023
1 parent a3aab0d commit 5c64165
Show file tree
Hide file tree
Showing 13 changed files with 211 additions and 1 deletion.
39 changes: 39 additions & 0 deletions Conduit/Conduit.Migrations/004_CreateTagData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using NoSqlMigrator.Infrastructure;

namespace Conduit.Migrations;

[Migration(4)]
public class CreateTagData : MigrateBase
{
private readonly string? _collectionName;
private readonly string? _scopeName;

public CreateTagData()
{
_collectionName = _config["Couchbase:TagsCollectionName"];
_scopeName = _config["Couchbase:ScopeName"];
}

public override void Up()
{
Create.Collection(_collectionName)
.InScope(_scopeName);

Insert.Into
.Scope(_scopeName)
.Collection(_collectionName)
.Document("tagData", new
{
tags = new List<string>
{
"Couchbase", "NoSQL", ".NET", "cruising", "baseball"
}
});
}

public override void Down()
{
Delete.Collection(_collectionName)
.FromScope(_scopeName);
}
}
1 change: 1 addition & 0 deletions Conduit/Conduit.Tests/Conduit.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
</ItemGroup>

<ItemGroup>
<Folder Include="Integration\Articles\Handlers\" />
<Folder Include="Extensions\" />
<Folder Include="Integration\Users\Controllers\UserController\" />
<Folder Include="Integration\Users\Services\UserDataServiceTests\" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using Conduit.Web.Articles.Handlers;
using Conduit.Web.Articles.Services;
using Conduit.Web.Models;
using Couchbase.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;

namespace Conduit.Tests.Integration.Articles.Handlers;

[TestFixture]
public class GetTagsHandlerTests : CouchbaseIntegrationTest
{
private GetTagsHandler _handler;
private IConduitTagsCollectionProvider _tagsCollectionProvider;

[SetUp]
public async Task SetUp()
{
await base.Setup();

ServiceCollection.AddCouchbaseBucket<IConduitBucketProvider>("ConduitIntegrationTests", b =>
{
b
.AddScope("_default")
.AddCollection<IConduitTagsCollectionProvider>("Tags");
});

_tagsCollectionProvider = ServiceProvider.GetRequiredService<IConduitTagsCollectionProvider>();

// setup handler and dependencies
_handler = new GetTagsHandler(new TagsDataService(_tagsCollectionProvider));
}

[Test]
public async Task Handle_ReturnsGetTagsResult()
{
// Arrange
var request = new GetTagsRequest();

// Act
var result = await _handler.Handle(request, CancellationToken.None);

// Assert
Assert.NotNull(result);
Assert.That(result.Tags.Contains("cruising"), Is.True);
}
}
32 changes: 32 additions & 0 deletions Conduit/Conduit.Web/Articles/Controllers/TagController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Conduit.Web.Articles.Handlers;
using MediatR;
using Microsoft.AspNetCore.Mvc;

namespace Conduit.Web.Articles.Controllers;

public class TagController : Controller
{
private readonly IMediator _mediator;

public TagController(IMediator mediator)
{
_mediator = mediator;
}

/// <summary>
/// List all (allowed) tags
/// </summary>
/// <remarks>
/// <a href="https://realworld-docs.netlify.app/docs/specs/backend-specs/endpoints/#get-tags">Conduit Spec for get tags endpoint</a>
/// </remarks>
/// <returns>List of tags</returns>
/// <response code="200">Returns all allowed tags</response>
[Route("/api/tags")]
[HttpGet]
public async Task<IActionResult> Get()
{
var tags = await _mediator.Send(new GetTagsRequest());

return Ok(tags);
}
}
24 changes: 24 additions & 0 deletions Conduit/Conduit.Web/Articles/Handlers/GetTagsHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Conduit.Web.Articles.Services;
using MediatR;

namespace Conduit.Web.Articles.Handlers;

public class GetTagsHandler : IRequestHandler<GetTagsRequest, GetTagsResult>
{
private readonly ITagsDataService _tagsDataService;

public GetTagsHandler(ITagsDataService tagsDataService)
{
_tagsDataService = tagsDataService;
}

public async Task<GetTagsResult> Handle(GetTagsRequest request, CancellationToken cancellationToken)
{
var result = await _tagsDataService.GetAllTags();

return new GetTagsResult
{
Tags = result
};
}
}
13 changes: 13 additions & 0 deletions Conduit/Conduit.Web/Articles/Handlers/GetTagsRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using MediatR;

namespace Conduit.Web.Articles.Handlers;

public class GetTagsRequest : IRequest<GetTagsResult>
{

}

public class GetTagsResult
{
public List<string> Tags { get; set; }
}
28 changes: 28 additions & 0 deletions Conduit/Conduit.Web/Articles/Services/TagsDataService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Conduit.Web.Models;

namespace Conduit.Web.Articles.Services;

public interface ITagsDataService
{
Task<List<string>> GetAllTags();
}

public class TagsDataService : ITagsDataService
{
private readonly IConduitTagsCollectionProvider _tagsCollectionProvider;

public TagsDataService(IConduitTagsCollectionProvider tagsCollectionProvider)
{
_tagsCollectionProvider = tagsCollectionProvider;
}

public async Task<List<string>> GetAllTags()
{
var collection = await _tagsCollectionProvider.GetCollectionAsync();

var tagsDoc = await collection.GetAsync("tagData");
var tagData = tagsDoc.ContentAs<TagData>();

return tagData.Tags;
}
}
6 changes: 6 additions & 0 deletions Conduit/Conduit.Web/Conduit.Web.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,10 @@
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>

<ItemGroup>
<Folder Include="Articles\Controllers\" />
<Folder Include="Articles\Handlers\" />
<Folder Include="Articles\Services\" />
</ItemGroup>

</Project>
8 changes: 8 additions & 0 deletions Conduit/Conduit.Web/Models/IConduitTagsCollectionProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Couchbase.Extensions.DependencyInjection;

namespace Conduit.Web.Models;

public interface IConduitTagsCollectionProvider : INamedCollectionProvider
{

}
6 changes: 6 additions & 0 deletions Conduit/Conduit.Web/Models/TagData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Conduit.Web.Models;

public class TagData
{
public List<string> Tags { get; set; }
}
5 changes: 5 additions & 0 deletions Conduit/Conduit.Web/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using Conduit.Web.Articles.Services;
using Conduit.Web.Follows.Services;
using Conduit.Web.Models;
using Conduit.Web.Users.Handlers;
Expand Down Expand Up @@ -74,6 +75,7 @@ public static void AddConduitServiceDependencies(this IServiceCollection @this,
@this.AddTransient<IAuthService, AuthService>();
@this.AddTransient<IFollowDataService, FollowsDataService>();
@this.AddTransient<IUserDataService, UserDataService>();
@this.AddTransient<ITagsDataService, TagsDataService>();
@this.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<Program>());
@this.AddCouchbase(configManager.GetSection("Couchbase"));
@this.AddCouchbaseBucket<IConduitBucketProvider>(configManager["Couchbase:BucketName"], b =>
Expand All @@ -84,6 +86,9 @@ public static void AddConduitServiceDependencies(this IServiceCollection @this,
b
.AddScope(configManager["Couchbase:ScopeName"])
.AddCollection<IConduitFollowsCollectionProvider>(configManager["Couchbase:FollowsCollectionName"]);
b
.AddScope(configManager["Couchbase:ScopeName"])
.AddCollection<IConduitTagsCollectionProvider>(configManager["Couchbase:TagsCollectionName"]);
});
}
}
Expand Down
3 changes: 2 additions & 1 deletion Conduit/Conduit.Web/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"BucketName": "Conduit",
"ScopeName": "_default",
"UsersCollectionName": "Users",
"FollowsCollectionName": "Follows"
"FollowsCollectionName": "Follows",
"TagsCollectionName": "Tags"
}
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ You'll need to understand at least a little bit:
* *Sliced* folders - these contain ASP.NET Core Controller(s), Mediatr request, response, and handler classes, viewmodels, and services for the functionality of the slice.
* Users - Authorization/authentication, JWT, registration, login, anything for Users
* Follows - Follow/unfollow
* Articles - Articles and tags for articles
* ...more on the way...
* *Extensions* folder - extension methods for ASP.NET functionality

Expand Down

0 comments on commit 5c64165

Please sign in to comment.