From 25c320bf6be45262e49650a5a86bb71506d29617 Mon Sep 17 00:00:00 2001 From: Robert Marshall Date: Sat, 11 Apr 2020 13:37:14 +0100 Subject: [PATCH] Use new API for blog --- Infrastructure/.drone.yml | 3 + Website.Tests/Data/BlogApiTests.cs | 249 ++++++++++++++++++ Website.Tests/Data/BlogRepositoryTests.cs | 55 ---- Website.Tests/Models/BlogPostTests.cs | 42 --- .../BlogPostPreviewViewModelTests.cs | 15 +- .../VIewModels/BlogPostViewModelTests.cs | 37 ++- Website/Controllers/BlogController.cs | 27 +- Website/Controllers/HomeController.cs | 8 +- Website/Data/BlogApi.cs | 22 ++ Website/Data/BlogRepository.cs | 87 ------ Website/Data/IBlogApi.cs | 17 ++ Website/Data/States/BlogPostState.cs | 19 -- Website/Models/BlogPost.cs | 47 +--- Website/Startup.cs | 8 +- .../BlogNavigationViewComponent.cs | 6 +- Website/appsettings.Development.json | 27 +- Website/appsettings.json | 1 + 17 files changed, 356 insertions(+), 314 deletions(-) create mode 100644 Website.Tests/Data/BlogApiTests.cs delete mode 100644 Website.Tests/Data/BlogRepositoryTests.cs delete mode 100644 Website.Tests/Models/BlogPostTests.cs create mode 100644 Website/Data/BlogApi.cs delete mode 100644 Website/Data/BlogRepository.cs create mode 100644 Website/Data/IBlogApi.cs delete mode 100644 Website/Data/States/BlogPostState.cs diff --git a/Infrastructure/.drone.yml b/Infrastructure/.drone.yml index c13ae09..fc4faa2 100644 --- a/Infrastructure/.drone.yml +++ b/Infrastructure/.drone.yml @@ -23,6 +23,8 @@ steps: from_secret: GitDomain GitToken: from_secret: GitToken + BlogEndpoint: + from_secret: BlogEndpoint commands: - curl -sL https://deb.nodesource.com/setup_12.x | bash - - apt-get install -y nodejs @@ -31,6 +33,7 @@ steps: - sed -i "s//$ConnectionString/g" output/appsettings.json - sed -i "s//$GitDomain/g" output/appsettings.json - sed -i "s//$GitToken/g" output/appsettings.json + - sed -i "s//$BlogEndpoint/g" output/appsettings.json - cp Infrastructure/website.service output/ - cp -r ./output/* /output - name: restart service diff --git a/Website.Tests/Data/BlogApiTests.cs b/Website.Tests/Data/BlogApiTests.cs new file mode 100644 index 0000000..3ec22c5 --- /dev/null +++ b/Website.Tests/Data/BlogApiTests.cs @@ -0,0 +1,249 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using FluentAssertions; +using Website.Data; +using Website.Models; +using Xunit; + +namespace Website.Tests.Data { + public class BlogApiTests { + [Fact] + public async Task GetPostByUrlAsync_WithUrl_ReturnsBlogPost() { + const string json = @"{""id"":1,""title"":""title"",""content"":""content"",""timestamp"":""2020-04-10T13:00:42"",""draft"":""draft"",""url"":""url"",""userId"":0}"; + var httpClient = new HttpClientBuilder() + .WithMethod(HttpMethod.Get) + .WithUrl("/get/test") + .WithResponse(json) + .Build(); + + var expectation = new BlogPost { + Id = 1, + Title = "title", + Content = "content", + Draft = "draft", + Timestamp = new DateTime(2020, 04, 10, 13, 00, 42), + Url = "url", + UserId = 0 + }; + + var api = new BlogApi(httpClient); + (await api.GetPostByUrlAsync("test")).Should().BeEquivalentTo(expectation); + } + + [Fact] + public async Task GetLatestPostsAsync_WithNoParameters_ReturnsBlogPostCollection() { + const string json = @"[{""id"":1,""title"":""title"",""content"":""content"",""timestamp"":""2020-04-10T13:00:42"",""draft"":""draft"",""url"":""url"",""userId"":0}]"; + var httpClient = new HttpClientBuilder() + .WithMethod(HttpMethod.Get) + .WithUrl("/getlatestposts") + .WithResponse(json) + .Build(); + + var expectation = new[] { + new BlogPost { + Id = 1, + Title = "title", + Content = "content", + Draft = "draft", + Timestamp = new DateTime(2020, 04, 10, 13, 00, 42), + Url = "url", + UserId = 0 + } + }; + + var api = new BlogApi(httpClient); + (await api.GetLatestPostsAsync()).Should().BeEquivalentTo(expectation); + } + + [Fact] + public async Task GetLatestPostsAsync_WithCount_ReturnsBlogPostCollection() { + const string json = @"[{""id"":1,""title"":""title"",""content"":""content"",""timestamp"":""2020-04-10T13:00:42"",""draft"":""draft"",""url"":""url"",""userId"":0}]"; + var httpClient = new HttpClientBuilder() + .WithMethod(HttpMethod.Get) + .WithUrl("/getlatestposts?count=1") + .WithResponse(json) + .Build(); + + var expectation = new[] { + new BlogPost { + Id = 1, + Title = "title", + Content = "content", + Draft = "draft", + Timestamp = new DateTime(2020, 04, 10, 13, 00, 42), + Url = "url", + UserId = 0 + } + }; + + var api = new BlogApi(httpClient); + (await api.GetLatestPostsAsync(1)).Should().BeEquivalentTo(expectation); + } + + [Fact] + public async Task GetLatestPostsAsync_WithCountAndOffset_ReturnsBlogPostCollection() { + const string json = @"[{""id"":1,""title"":""title"",""content"":""content"",""timestamp"":""2020-04-10T13:00:42"",""draft"":""draft"",""url"":""url"",""userId"":0}]"; + var httpClient = new HttpClientBuilder() + .WithMethod(HttpMethod.Get) + .WithUrl("/getlatestposts") + .WithQueryString("count", "1") + .WithQueryString("offset", "1") + .WithResponse(json) + .Build(); + + var expectation = new[] { + new BlogPost { + Id = 1, + Title = "title", + Content = "content", + Draft = "draft", + Timestamp = new DateTime(2020, 04, 10, 13, 00, 42), + Url = "url", + UserId = 0 + } + }; + + var api = new BlogApi(httpClient); + (await api.GetLatestPostsAsync(1, 1)).Should().BeEquivalentTo(expectation); + } + + [Fact] + public async Task GetLatestPostAsync_ReturnsBlogPost() { + const string json = @"{""id"":1,""title"":""title"",""content"":""content"",""timestamp"":""2020-04-10T13:00:42"",""draft"":""draft"",""url"":""url"",""userId"":0}"; + var httpClient = new HttpClientBuilder() + .WithMethod(HttpMethod.Get) + .WithUrl("/getlatestpost") + .WithResponse(json) + .Build(); + + var expectation = new BlogPost { + Id = 1, + Title = "title", + Content = "content", + Draft = "draft", + Timestamp = new DateTime(2020, 04, 10, 13, 00, 42), + Url = "url", + UserId = 0 + }; + + var api = new BlogApi(httpClient); + (await api.GetLatestPostAsync()).Should().BeEquivalentTo(expectation); + } + + [Fact] + public async Task GetCountAsync_ReturnsCount() { + const string json = @"23"; + var httpClient = new HttpClientBuilder() + .WithMethod(HttpMethod.Get) + .WithUrl("/getcount") + .WithResponse(json) + .Build(); + + var api = new BlogApi(httpClient); + (await api.GetCountAsync()).Should().Be(23); + } + + [Fact] + public async Task GetPostByIdAsync_WithUrl_ReturnsBlogPost() { + const string json = @"{""id"":1,""title"":""title"",""content"":""content"",""timestamp"":""2020-04-10T13:00:42"",""draft"":""draft"",""url"":""url"",""userId"":0}"; + var httpClient = new HttpClientBuilder() + .WithMethod(HttpMethod.Get) + .WithUrl("/get/1") + .WithResponse(json) + .Build(); + + var expectation = new BlogPost { + Id = 1, + Title = "title", + Content = "content", + Draft = "draft", + Timestamp = new DateTime(2020, 04, 10, 13, 00, 42), + Url = "url", + UserId = 0 + }; + + var api = new BlogApi(httpClient); + (await api.GetPostByIdAsync(1)).Should().BeEquivalentTo(expectation); + } + + [Fact] + public async Task SavePost_WithPost_ReturnsNothing() { + const string requestJson = @"{""Id"":1,""Title"":""title"",""Content"":""content""}"; + const string responseJson = @"{""id"":1,""title"":""title"",""content"":""content"",""timestamp"":""2020-04-10T13:00:42"",""draft"":""draft"",""url"":""url"",""userId"":0}"; + var httpClient = new HttpClientBuilder() + .WithMethod(HttpMethod.Post) + .WithUrl("/savepost") + .WithPostBody(requestJson) + .WithResponse(responseJson) + .Build(); + + var post = new BlogPostSubmission {Id = 1, Title = "title", Content = "content"}; + + var expectation = new BlogPost { + Id = 1, + Title = "title", + Content = "content", + Draft = "draft", + Timestamp = new DateTime(2020, 04, 10, 13, 00, 42), + Url = "url", + UserId = 0 + }; + + var api = new BlogApi(httpClient); + (await api.SavePost(post)).Should().BeEquivalentTo(expectation); + } + + [Fact] + public async Task GetAllPostsAsync_ReturnsBlogPostCollection() { + const string json = + @"[{""id"":1,""title"":""title"",""content"":""content"",""timestamp"":""2020-04-10T13:00:42"",""draft"":""draft"",""url"":""url"",""userId"":0}]"; + var httpClient = new HttpClientBuilder() + .WithMethod(HttpMethod.Get) + .WithUrl("/getallposts") + .WithResponse(json) + .Build(); + + var expectation = new[] { + new BlogPost { + Id = 1, + Title = "title", + Content = "content", + Draft = "draft", + Timestamp = new DateTime(2020, 04, 10, 13, 00, 42), + Url = "url", + UserId = 0 + } + }; + + var api = new BlogApi(httpClient); + (await api.GetAllPostsAsync()).Should().BeEquivalentTo(expectation); + } + + [Fact] + public async Task DeletePost_WithId_ReturnsNothing() { + var httpClient = new HttpClientBuilder() + .WithMethod(HttpMethod.Post) + .WithUrl("/deletepost") + .WithPostBody("1") + .Build(out var mockHttpMessageHandler); + + var api = new BlogApi(httpClient); + await api.DeletePostAsync(1); + mockHttpMessageHandler.VerifyNoOutstandingExpectation(); + } + + [Fact] + public async Task PublishPost_WithId_ReturnsNothing() { + var httpClient = new HttpClientBuilder() + .WithMethod(HttpMethod.Post) + .WithUrl("/publishpost") + .WithPostBody("1") + .Build(out var mockHttpMessageHandler); + + var api = new BlogApi(httpClient); + await api.PublishPostAsync(1); + mockHttpMessageHandler.VerifyNoOutstandingExpectation(); + } + } +} diff --git a/Website.Tests/Data/BlogRepositoryTests.cs b/Website.Tests/Data/BlogRepositoryTests.cs deleted file mode 100644 index 3243449..0000000 --- a/Website.Tests/Data/BlogRepositoryTests.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data; -using System.Threading.Tasks; -using Dapper; -using FluentAssertions; -using NSubstitute; -using NSubstitute.ExceptionExtensions; -using Website.Data; -using Website.Data.States; -using Website.Models; -using Xunit; - -namespace Website.Tests.Data -{ - public class BlogRepositoryTests - { - // [Fact] - // public void GetPostAsync_WithValidId_ReturnsBlogPost() { - // var blogPostState = new BlogPostState - // { - // Post_Id = 1, - // Post_Title = "title", - // Post_Content = "content", - // Post_Timestamp = new DateTime(), - // Post_Deleted = false, - // Post_Draft = "draft", - // Post_Url = "url", - // User_Id = 1 - // }; - // var connection = Substitute.For(); - // connection.QueryAsync(null, null, (Func)null, null, null, false, null, null, null).ReturnsForAnyArgs(new[]{ blogPostState }); - - // var provider = Substitute.For(); - // provider.NewConnection().Returns(connection); - - // var repo = new BlogRepository(provider); - // var post = repo.GetPostAsync(1); - // var expected = new BlogPost - // { - // Id = 1, - // Title = "title", - // Content = "content", - // Timestamp = new DateTime(), - // Draft = "draft", - // Url = "url", - // UserId = 1 - // }; ; - // post.Should().BeEquivalentTo(expected); - // } - - //public async Task GetPostAsync_WithInvalidId_ReturnsNull() - //public async Task GetPostAsync_WithDeletedPost_ReturnsNull() - } -} \ No newline at end of file diff --git a/Website.Tests/Models/BlogPostTests.cs b/Website.Tests/Models/BlogPostTests.cs deleted file mode 100644 index 7c268de..0000000 --- a/Website.Tests/Models/BlogPostTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -using FluentAssertions; -using Website.Models; -using Xunit; - -namespace Website.Tests.Models { - public class BlogPostTests { - [Fact] - public void UpdateTitle_WithNewTitle_UpdatesTitlePropertyAndRegeneratesUrl() { - var post = new BlogPost(); - post.UpdateTitle("new title"); - - post.Title.Should().Be("new title"); - post.Url.Should().Be("new-title"); - } - - [Theory] - [InlineData("new:title", "new-title")] - [InlineData("new: title", "new-title")] - [InlineData("new$title", "new-title")] - [InlineData("new TITle", "new-title")] - public void UpdateTitle_WithUnsafeCharsInTitle_RegeneratesSafeUrl(string title, string url) { - var post = new BlogPost(); - post.UpdateTitle(title); - post.Url.Should().Be(url); - } - - [Fact] - public void UpdateDraft_WithContent_UpdatesDraftProperty() { - var post = new BlogPost(); - post.UpdateDraft("content"); - post.Draft.Should().Be("content"); - } - - [Fact] - public void Publish_SetsContentToDraft() { - var post = new BlogPost(); - post.UpdateDraft("content"); - post.Publish(); - post.Content.Should().Be("content"); - } - } -} \ No newline at end of file diff --git a/Website.Tests/VIewModels/BlogPostPreviewViewModelTests.cs b/Website.Tests/VIewModels/BlogPostPreviewViewModelTests.cs index 35feefd..af9cf35 100644 --- a/Website.Tests/VIewModels/BlogPostPreviewViewModelTests.cs +++ b/Website.Tests/VIewModels/BlogPostPreviewViewModelTests.cs @@ -1,5 +1,4 @@ -using FluentAssertions; -using Website.Data.States; +using FluentAssertions; using Website.Models; using Website.ViewModels; using Xunit; @@ -10,22 +9,16 @@ namespace Website.Tests.VIewModels { [Fact] public void Constructor_WithContentOver1000Characters_LimitsContentTo1000Chars() { - var state = new BlogPostState { - Post_Content = new string('a', 1001) - }; - var post = new BlogPost(state); + var post = new BlogPost {Content = new string('a', 1001)}; var vm = new BlogPostSnippetViewModel(post); vm.Content.Length.Should().Be(1000); } [Fact] public void Constructor_WithContentUnder1000Characters_ContentIsIdenticalLength() { - var state = new BlogPostState { - Post_Content = new string('a', 900) - }; - var post = new BlogPost(state); + var post = new BlogPost{Content = new string('a', 900)}; var vm = new BlogPostSnippetViewModel(post); vm.Content.Length.Should().Be(900); } } -} \ No newline at end of file +} diff --git a/Website.Tests/VIewModels/BlogPostViewModelTests.cs b/Website.Tests/VIewModels/BlogPostViewModelTests.cs index ebcc691..0643d70 100644 --- a/Website.Tests/VIewModels/BlogPostViewModelTests.cs +++ b/Website.Tests/VIewModels/BlogPostViewModelTests.cs @@ -1,55 +1,48 @@ -using System; +using System; using FluentAssertions; -using Website.Data.States; using Website.Models; using Website.ViewModels; using Xunit; -namespace Website.Tests.VIewModels -{ - public class BlogPostViewModelTests - { +namespace Website.Tests.VIewModels { + public class BlogPostViewModelTests { [Fact] public void ContentHtml_WithMarkdownContent_ReturnsHtml() { - var state = new BlogPostState { - Post_Content="# header" + var post = new BlogPost { + Content = "# header" }; - var post = new BlogPost(state); var vm = new BlogPostViewModel(post, false); vm.ContentHtml.Should().Be("

header

"); } [Fact] public void Timestamp_OnThe1st_IsFriendlyString() { - var state = new BlogPostState { - Post_Content = "", - Post_Timestamp = new DateTime(2018, 10, 01, 15, 1, 25) + var post = new BlogPost { + Content = "", + Timestamp = new DateTime(2018, 10, 01, 15, 1, 25) }; - var post = new BlogPost(state); var vm = new BlogPostViewModel(post, false); vm.Timestamp.Should().Be("Monday the 1st of October 2018"); } [Fact] public void Timestamp_OnThe2nd_IsFriendlyString() { - var state = new BlogPostState { - Post_Content = "", - Post_Timestamp = new DateTime(2018, 10, 02, 15, 1, 25) + var post = new BlogPost { + Content = "", + Timestamp = new DateTime(2018, 10, 02, 15, 1, 25) }; - var post = new BlogPost(state); var vm = new BlogPostViewModel(post, false); vm.Timestamp.Should().Be("Tuesday the 2nd of October 2018"); } [Fact] public void Timestamp_OnThe3rd_IsFriendlyString() { - var state = new BlogPostState { - Post_Content = "", - Post_Timestamp = new DateTime(2018, 10, 03, 15, 1, 25) + var post = new BlogPost { + Content = "", + Timestamp = new DateTime(2018, 10, 03, 15, 1, 25) }; - var post = new BlogPost(state); var vm = new BlogPostViewModel(post, false); vm.Timestamp.Should().Be("Wednesday the 3rd of October 2018"); } } -} \ No newline at end of file +} diff --git a/Website/Controllers/BlogController.cs b/Website/Controllers/BlogController.cs index 3684a40..c493776 100644 --- a/Website/Controllers/BlogController.cs +++ b/Website/Controllers/BlogController.cs @@ -10,21 +10,21 @@ using Website.ViewModels; namespace Website.Controllers { public class BlogController:Controller { private const int MaxPostsPerPage = 10; - private readonly BlogRepository _repo; + private readonly IBlogApi _api; - public BlogController(BlogRepository repo) => _repo = repo; + public BlogController(IBlogApi api) => _api = api; public async Task Page(int page) { var offset = (page - 1) * MaxPostsPerPage; - var posts = (await _repo.GetLatestPostsAsync(MaxPostsPerPage, offset)).Select(p => new BlogPostSnippetViewModel(p)); - var maxPages = (await _repo.GetCountAsync()) / MaxPostsPerPage; + var posts = (await _api.GetLatestPostsAsync(MaxPostsPerPage, offset)).Select(p => new BlogPostSnippetViewModel(p)); + var maxPages = (await _api.GetCountAsync()) / MaxPostsPerPage; var model = new BlogViewModel(posts, page, maxPages); return View(model); } public async Task Entry(string url, bool preview = false) { try { - var post = await _repo.GetPostByUrlAsync(url); + var post = await _api.GetPostByUrlAsync(url); if (!preview && string.IsNullOrEmpty(post.Content)) return NotFound(); @@ -42,7 +42,7 @@ namespace Website.Controllers { return View(); try { - var post = await _repo.GetPostByIdAsync(id.Value); + var post = await _api.GetPostByIdAsync(id.Value); var model = new BlogPostSubmission { Id = post.Id, Title = post.Title, @@ -57,35 +57,28 @@ namespace Website.Controllers { [Authorize] [HttpPost] public async Task Save(BlogPostSubmission submission) { - var post = submission.Id.HasValue ? await _repo.GetPostByIdAsync(submission.Id.Value) : new BlogPost(); - - post.UpdateTitle(submission.Title); - post.UpdateDraft(submission.Content); - - var savedPost = await _repo.SavePost(post); + var savedPost = await _api.SavePost(submission); return RedirectToAction(nameof(Edit), new { savedPost.Id }); } [Authorize] public async Task Manage() { - var posts = await _repo.GetAllPostsAsync(); + var posts = await _api.GetAllPostsAsync(); var models = posts.OrderByDescending(post => post.Timestamp).Select(post => new BlogPostViewModel(post, false)); return View(models); } [Authorize] public async Task Publish(int id) { - var post = await _repo.GetPostByIdAsync(id); - post.Publish(); - await _repo.SavePost(post); + await _api.PublishPostAsync(id); return RedirectToAction(nameof(Manage)); } [Authorize] public async Task Delete(int id) { - await _repo.DeletePostAsync(id); + await _api.DeletePostAsync(id); return RedirectToAction(nameof(Manage)); } diff --git a/Website/Controllers/HomeController.cs b/Website/Controllers/HomeController.cs index 1454f66..465a2cc 100644 --- a/Website/Controllers/HomeController.cs +++ b/Website/Controllers/HomeController.cs @@ -6,16 +6,16 @@ using System.Linq; namespace Website.Controllers { public class HomeController : Controller { - private readonly BlogRepository _blogRepo; + private readonly IBlogApi _blogApi; private readonly GitServerApi _api; - public HomeController(BlogRepository blogRepo, GitServerApi api) { + public HomeController(IBlogApi blogApi, GitServerApi api) { _api = api; - _blogRepo = blogRepo; + _blogApi = blogApi; } public async Task Index() { - var post = await _blogRepo.GetLatestPostAsync(); + var post = await _blogApi.GetLatestPostAsync(); var repo = (await _api.GetRepositories()).First(); var branch = (await _api.GetBranches(repo.Name)).First(); var commit = await _api.GetCommit(repo.Name, branch.Commit.Id); diff --git a/Website/Data/BlogApi.cs b/Website/Data/BlogApi.cs new file mode 100644 index 0000000..1b32c81 --- /dev/null +++ b/Website/Data/BlogApi.cs @@ -0,0 +1,22 @@ +using System.Threading.Tasks; +using Website.Models; +using System.Collections.Generic; +using System.Net.Http; + +namespace Website.Data +{ + public class BlogApi : ApiClient, IBlogApi { + public BlogApi(HttpClient client) : base(client) { + } + + public async Task GetPostByUrlAsync(string url) => await Get("/get/" + url); + public async Task> GetLatestPostsAsync(int count = 0, int offset = 0) => await Get>("/getlatestposts", new{count, offset}); + public async Task GetLatestPostAsync() => await Get("/getlatestpost"); + public async Task GetCountAsync() => await Get("/getcount"); + public async Task GetPostByIdAsync(int id) => await Get("/get/" + id); + public async Task SavePost(BlogPostSubmission post) => await Post("/savepost", post); + public async Task> GetAllPostsAsync() => await Get>("/getallposts"); + public async Task DeletePostAsync(int id) => await Post("/deletepost", id); + public async Task PublishPostAsync(int id) => await Post("/publishpost", id); + } +} diff --git a/Website/Data/BlogRepository.cs b/Website/Data/BlogRepository.cs deleted file mode 100644 index 2d23650..0000000 --- a/Website/Data/BlogRepository.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System.Threading.Tasks; -using System.Linq; -using Dapper; -using Website.Models; -using Website.Data.States; -using System.Collections.Generic; - -namespace Website.Data -{ - public class BlogRepository - { - private readonly IDatabaseProvider _dbProvider; - - public BlogRepository(IDatabaseProvider dbProvider) => _dbProvider = dbProvider; - - public async Task GetPostByUrlAsync(string url) { - const string query = "SELECT * FROM blog_posts WHERE post_url=@url AND post_deleted=0"; - using (var connection = _dbProvider.NewConnection()) { - connection.Open(); - var result = await connection.QueryAsync(query, new{url}); - return new BlogPost(result.First()); - } - } - - public async Task> GetLatestPostsAsync(int limit, int offset = 0) { - const string query = "SELECT * FROM blog_posts WHERE post_content<>'' AND post_deleted=0 ORDER BY post_timestamp DESC LIMIT @offset,@limit"; - using (var connection = _dbProvider.NewConnection()) { - connection.Open(); - var results = await connection.QueryAsync(query, new{limit, offset}); - return results.Select(result => new BlogPost(result)); - } - } - - public async Task GetLatestPostAsync() => (await GetLatestPostsAsync(1)).First(); - - public async Task GetCountAsync() - { - var query="SELECT COUNT(*) FROM blog_posts WHERE post_content<>'' AND post_deleted=0"; - using(var connection = _dbProvider.NewConnection()) { - connection.Open(); - var result = await connection.QueryAsync(query); - return result.First(); - } - } - - public async Task GetPostByIdAsync(int id) { - const string query = "SELECT * FROM blog_posts WHERE post_id=@id AND post_deleted=0"; - using (var connection = _dbProvider.NewConnection()) { - connection.Open(); - var result = await connection.QueryAsync(query, new {id}); - return new BlogPost(result.First()); - } - } - - public async Task SavePost(BlogPost post) { - const string newPostQuery = "INSERT INTO blog_posts (post_title, post_content, post_draft, post_url) VALUES (@title, @content, @draft, @url); SELECT CAST(LAST_INSERT_ID() as int)"; - const string updatePostQuery = "UPDATE blog_posts SET post_id = @id, post_title = @title, post_content = @content, post_draft = @draft, post_url = @url WHERE post_id = @id "; - - var newPost = post.Id == 0; - - using (var connection = _dbProvider.NewConnection()) { - connection.Open(); - var result = await connection.QueryAsync(newPost ? newPostQuery : updatePostQuery, post); - return newPost ? await GetPostByIdAsync(result.Single()) : post; - } - } - - public async Task> GetAllPostsAsync() { - const string query = "SELECT * FROM blog_posts WHERE post_deleted=0"; - - using (var connection = _dbProvider.NewConnection()) { - connection.Open(); - var result = await connection.QueryAsync(query); - return result.Select(state => new BlogPost(state)); - } - } - - public async Task DeletePostAsync(int id) { - const string query = "UPDATE blog_posts SET post_deleted=1 WHERE post_id=@id"; - - using (var connection = _dbProvider.NewConnection()) { - connection.Open(); - await connection.ExecuteAsync(query, new {id}); - } - } - } -} \ No newline at end of file diff --git a/Website/Data/IBlogApi.cs b/Website/Data/IBlogApi.cs new file mode 100644 index 0000000..c30702d --- /dev/null +++ b/Website/Data/IBlogApi.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Website.Models; + +namespace Website.Data { + public interface IBlogApi { + Task GetPostByUrlAsync(string url); + Task> GetLatestPostsAsync(int count = 0, int offset = 0); + Task GetLatestPostAsync(); + Task GetCountAsync(); + Task GetPostByIdAsync(int id); + Task SavePost(BlogPostSubmission post); + Task> GetAllPostsAsync(); + Task DeletePostAsync(int id); + Task PublishPostAsync(int id); + } +} diff --git a/Website/Data/States/BlogPostState.cs b/Website/Data/States/BlogPostState.cs deleted file mode 100644 index 3412f8c..0000000 --- a/Website/Data/States/BlogPostState.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using Dapper.Contrib.Extensions; - -namespace Website.Data.States -{ - [Table("blog_posts")] - public class BlogPostState - { - [Key] - public int Post_Id { get; set; } - public string Post_Title { get; set; } - public string Post_Content { get; set; } - public DateTime Post_Timestamp { get; set; } - public string Post_Draft { get; set; } - public string Post_Url { get; set; } - public bool Post_Deleted{ get; set; } - public int User_Id { get; set; } - } -} \ No newline at end of file diff --git a/Website/Models/BlogPost.cs b/Website/Models/BlogPost.cs index a90abdb..9ecf064 100644 --- a/Website/Models/BlogPost.cs +++ b/Website/Models/BlogPost.cs @@ -1,44 +1,15 @@ -using System; -using System.Text.RegularExpressions; -using Website.Data.States; +using System; namespace Website.Models { public class BlogPost { - public BlogPost() { - - } - - public BlogPost(BlogPostState state) - { - Id = state.Post_Id; - Title = state.Post_Title; - Content = state.Post_Content; - Timestamp = state.Post_Timestamp; - Draft = state.Post_Draft; - Url = state.Post_Url; - UserId = state.User_Id; - } - - public int Id { get; private 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; } - - private void GenerateUrl() { - Url = Regex.Replace(Title, @"[^a-zA-Z0-9\.]+", "-").ToLower(); - } - - public void UpdateTitle(string title) { - Title = title; - GenerateUrl(); - } - - public void UpdateDraft(string content) => Draft = content; - public void Publish() => Content = Draft; + public int Id { get; set; } + public string Title { get; set; } + public string Content { get; set; } + public DateTime Timestamp { get; set; } + public string Draft { get; set; } + public string Url { get; set; } + public int UserId { get; set; } } -} \ No newline at end of file +} diff --git a/Website/Startup.cs b/Website/Startup.cs index 1b87410..dcf6d4e 100644 --- a/Website/Startup.cs +++ b/Website/Startup.cs @@ -1,4 +1,5 @@ -using System.Net.Http; +using System; +using System.Net.Http; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -43,9 +44,10 @@ namespace Website private void RegisterRepositories(IServiceCollection services) => services.AddSingleton() - .AddSingleton() + .AddSingleton() .AddSingleton() - .AddSingleton(new GitServerApi(new HttpClient(), Configuration["gitDomain"], Configuration["gitToken"])); + .AddSingleton(new GitServerApi(new HttpClient(), Configuration["gitDomain"], Configuration["gitToken"])) + .AddHttpClient(client => client.BaseAddress = new Uri(Configuration["blogApiEndpoint"])); // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) diff --git a/Website/ViewComponents/BlogNavigationViewComponent.cs b/Website/ViewComponents/BlogNavigationViewComponent.cs index fcec7c2..783bfa7 100644 --- a/Website/ViewComponents/BlogNavigationViewComponent.cs +++ b/Website/ViewComponents/BlogNavigationViewComponent.cs @@ -4,9 +4,9 @@ using Website.Data; namespace Website.ViewComponents { public class BlogNavigationViewComponent:ViewComponent { - private readonly BlogRepository _repo; + private readonly IBlogApi _repo; - public BlogNavigationViewComponent(BlogRepository repo) { + public BlogNavigationViewComponent(IBlogApi repo) { _repo = repo; } @@ -15,4 +15,4 @@ namespace Website.ViewComponents { return View(posts); } } -} \ No newline at end of file +} diff --git a/Website/appsettings.Development.json b/Website/appsettings.Development.json index 59498ee..996fc81 100644 --- a/Website/appsettings.Development.json +++ b/Website/appsettings.Development.json @@ -1,14 +1,15 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Debug", - "System": "Information", - "Microsoft": "Information" - } - }, - "ConnectionStrings": { - "database": "Server=localhost;User ID=user;Password=pass;Database=db" - }, - "gitDomain": "", - "gitToken": "" +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + }, + "ConnectionStrings": { + "database": "Server=localhost;User ID=user;Password=pass;Database=db" + }, + "gitDomain": "", + "gitToken": "", + "blogApiEndpoint": "" } \ No newline at end of file diff --git a/Website/appsettings.json b/Website/appsettings.json index 9f8945e..3751044 100644 --- a/Website/appsettings.json +++ b/Website/appsettings.json @@ -9,6 +9,7 @@ }, "gitDomain": "", "gitToken": "", + "blogApiEndpoint": "", "AllowedHosts": "*", "Kestrel": { "EndPoints": {