Add code for using MongoDB
This commit is contained in:
parent
dd370e27cb
commit
313d668bfe
8 changed files with 218 additions and 1 deletions
|
@ -20,6 +20,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robware.Api.Blog.Tests", "R
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "..\build\_build.csproj", "{9F55FA01-FEC9-4326-8338-4EF014E127A4}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robware.Data.MongoDB", "Robware.Data.MongoDB\Robware.Data.MongoDB.csproj", "{D48723D5-053A-4654-B37D-CC2FA62625C3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robware.Data.MongoDB.Tests", "Robware.Data.MongoDB.Tests\Robware.Data.MongoDB.Tests.csproj", "{E4361445-DD13-4DB3-A278-4198F4DEE00A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -48,6 +52,14 @@ Global
|
|||
{54DEC274-5F28-42FB-9B60-1E4BAF3D7BF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{54DEC274-5F28-42FB-9B60-1E4BAF3D7BF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{54DEC274-5F28-42FB-9B60-1E4BAF3D7BF9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D48723D5-053A-4654-B37D-CC2FA62625C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D48723D5-053A-4654-B37D-CC2FA62625C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D48723D5-053A-4654-B37D-CC2FA62625C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D48723D5-053A-4654-B37D-CC2FA62625C3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E4361445-DD13-4DB3-A278-4198F4DEE00A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E4361445-DD13-4DB3-A278-4198F4DEE00A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E4361445-DD13-4DB3-A278-4198F4DEE00A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E4361445-DD13-4DB3-A278-4198F4DEE00A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
@ -5,12 +5,14 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.10.4" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Robware.Blog\Robware.Blog.csproj" />
|
||||
<ProjectReference Include="..\Robware.Data\Robware.Data.csproj" />
|
||||
<ProjectReference Include="..\Robware.Data.MongoDB\Robware.Data.MongoDB.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using System;
|
||||
|
||||
namespace Robware.Data {
|
||||
namespace Robware.Blog {
|
||||
public class ItemNotFoundException:Exception {
|
||||
public object Parameters { get; }
|
||||
|
54
src/Robware.Data.MongoDB.Tests/BlogRepositoryTests.cs
Normal file
54
src/Robware.Data.MongoDB.Tests/BlogRepositoryTests.cs
Normal file
|
@ -0,0 +1,54 @@
|
|||
using FluentAssertions;
|
||||
using MongoDB.Driver;
|
||||
using NSubstitute;
|
||||
using Robware.Blog;
|
||||
using Robware.Blog.Data.MongoDB.State;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Robware.Data.MongoDB.Tests {
|
||||
public class BlogRepositoryTests {
|
||||
[Fact]
|
||||
public async Task SavePost_WithNewPost_GetsNewIdAndSetsTimestampAndSavesAsync() {
|
||||
var collection = Substitute.For<IMongoCollection<BlogPostState>>();
|
||||
collection.CountDocumentsAsync(Arg.Any<FilterDefinition<BlogPostState>>()).Returns(10);
|
||||
var database = Substitute.For<IMongoDatabase>();
|
||||
database.GetCollection<BlogPostState>("blog").Returns(collection);
|
||||
|
||||
var repo = new BlogRepository(database);
|
||||
var post = new BlogPost();
|
||||
await repo.SavePost(post);
|
||||
post.Id.Should().Be(11);
|
||||
post.Timestamp.Should().BeCloseTo(DateTime.Now);
|
||||
await collection.Received(1).InsertOneAsync(Arg.Any<BlogPostState>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SavePost_WithExistingPost_DoesNotSetTimestampAndSavesAsync() {
|
||||
var collection = Substitute.For<IMongoCollection<BlogPostState>>();
|
||||
var database = Substitute.For<IMongoDatabase>();
|
||||
database.GetCollection<BlogPostState>("blog").Returns(collection);
|
||||
|
||||
var repo = new BlogRepository(database);
|
||||
var post = new BlogPost() {
|
||||
Id = 1
|
||||
};
|
||||
await repo.SavePost(post);
|
||||
post.Id.Should().Be(1);
|
||||
post.Timestamp.Should().Be(new DateTime());
|
||||
await collection.Received(1).InsertOneAsync(Arg.Any<BlogPostState>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetCountAsync_ReturnsCountFromDatabase() {
|
||||
var collection = Substitute.For<IMongoCollection<BlogPostState>>();
|
||||
collection.CountDocumentsAsync(Arg.Any<FilterDefinition<BlogPostState>>()).Returns(10);
|
||||
var database = Substitute.For<IMongoDatabase>();
|
||||
database.GetCollection<BlogPostState>("blog").Returns(collection);
|
||||
|
||||
var repo = new BlogRepository(database);
|
||||
(await repo.GetCountAsync()).Should().Be(10);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="5.10.3" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.10.4" />
|
||||
<PackageReference Include="NSubstitute" Version="4.2.2" />
|
||||
<PackageReference Include="xunit" Version="2.4.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="1.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Robware.Data.MongoDB\Robware.Data.MongoDB.csproj" />
|
||||
<ProjectReference Include="..\Robware.Blog\Robware.Blog.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
77
src/Robware.Data.MongoDB/BlogRepository.cs
Normal file
77
src/Robware.Data.MongoDB/BlogRepository.cs
Normal file
|
@ -0,0 +1,77 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using MongoDB.Driver;
|
||||
using Robware.Blog;
|
||||
using Robware.Blog.Data.MongoDB.State;
|
||||
|
||||
namespace Robware.Data.MongoDB {
|
||||
public class BlogRepository : IBlogRepository {
|
||||
private readonly IMongoCollection<BlogPostState> _collection;
|
||||
|
||||
public BlogRepository(IMongoDatabase database) {
|
||||
_collection = database.GetCollection<BlogPostState>("blog");
|
||||
}
|
||||
|
||||
private BlogPost MapStateToPost(BlogPostState state) =>
|
||||
new BlogPost {
|
||||
Id = state.Id,
|
||||
Title = state.Title,
|
||||
Content = state.Content,
|
||||
Timestamp = state.Timestamp,
|
||||
Draft = state.Draft,
|
||||
Url = state.Url,
|
||||
UserId = state.UserId,
|
||||
};
|
||||
|
||||
private IEnumerable<BlogPost> MapStateToPost(IEnumerable<BlogPostState> states) => states.Select(MapStateToPost);
|
||||
|
||||
public async Task DeletePostAsync(int id) {
|
||||
var update = Builders<BlogPostState>.Update.Set(nameof(BlogPostState.Deleted), true);
|
||||
await _collection.UpdateOneAsync(post => post.Id == id, update);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<BlogPost>> GetAllPostsAsync() => MapStateToPost((await _collection.FindAsync(post => true)).ToEnumerable());
|
||||
|
||||
public async Task<int> GetCountAsync() => (int)await _collection.CountDocumentsAsync(post => !post.Deleted);
|
||||
|
||||
public async Task<BlogPost> GetLatestPostAsync() => (await GetLatestPostsAsync(1)).FirstOrDefault();
|
||||
|
||||
public async Task<IEnumerable<BlogPost>> GetLatestPostsAsync(int limit, int offset = 0) {
|
||||
var filter = Builders<BlogPostState>.Filter.Eq(nameof(BlogPostState.Deleted), false);
|
||||
var sort = Builders<BlogPostState>.Sort.Descending(nameof(BlogPostState.Timestamp));
|
||||
var options = new FindOptions<BlogPostState> {
|
||||
Sort = sort,
|
||||
Limit = limit,
|
||||
Skip = offset
|
||||
};
|
||||
var states = (await _collection.FindAsync<BlogPostState>(filter, options)).ToEnumerable();
|
||||
return MapStateToPost(states);
|
||||
}
|
||||
|
||||
private async Task<BlogPost> GetPostAsync(Expression<Func<BlogPostState, bool>> filter) {
|
||||
var result = (await _collection.FindAsync(filter)).FirstOrDefault();
|
||||
if (result == null)
|
||||
throw new ItemNotFoundException(nameof(GetPostAsync), null);
|
||||
return MapStateToPost(result);
|
||||
}
|
||||
|
||||
public async Task<BlogPost> GetPostByIdAsync(int id) => await GetPostAsync(post => post.Id == id && !post.Deleted);
|
||||
|
||||
public async Task<BlogPost> GetPostByUrlAsync(string url) => await GetPostAsync(post => post.Url == url && !post.Deleted);
|
||||
|
||||
public async Task<BlogPost> SavePost(BlogPost post) {
|
||||
if (post.Id == 0) {
|
||||
post.Id = (await GetCountAsync()) + 1;
|
||||
post.Timestamp = DateTime.Now;
|
||||
}
|
||||
|
||||
var mongoPost = new BlogPostState(post);
|
||||
await _collection.InsertOneAsync(mongoPost);
|
||||
|
||||
return post;
|
||||
}
|
||||
}
|
||||
}
|
15
src/Robware.Data.MongoDB/Robware.Data.MongoDB.csproj
Normal file
15
src/Robware.Data.MongoDB/Robware.Data.MongoDB.csproj
Normal file
|
@ -0,0 +1,15 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Robware.Blog\Robware.Blog.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.10.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
33
src/Robware.Data.MongoDB/State/BlogPostState.cs
Normal file
33
src/Robware.Data.MongoDB/State/BlogPostState.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using System;
|
||||
|
||||
namespace Robware.Blog.Data.MongoDB.State {
|
||||
public class BlogPostState {
|
||||
public BlogPostState()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public BlogPostState(BlogPost basePost) {
|
||||
Id = basePost.Id;
|
||||
Title = basePost.Title;
|
||||
Content = basePost.Content;
|
||||
Timestamp = basePost.Timestamp;
|
||||
Draft = basePost.Draft;
|
||||
Url = basePost.Url;
|
||||
UserId = basePost.UserId;
|
||||
Deleted = false;
|
||||
}
|
||||
|
||||
[BsonId]
|
||||
public int Id { get; set; }
|
||||
public string Title { get; private set; }
|
||||
public string Content { get; private set; }
|
||||
public DateTime Timestamp { get; private set; }
|
||||
public string Draft { get; private set; }
|
||||
public string Url { get; private set; }
|
||||
public int UserId { get; private set; }
|
||||
public bool Deleted { get; private set; }
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue