Use new API for blog

This commit is contained in:
Robert Marshall 2020-04-11 13:37:14 +01:00
parent e389b2404a
commit 25c320bf6b
17 changed files with 356 additions and 314 deletions

View file

@ -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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> Delete(int id) {
await _repo.DeletePostAsync(id);
await _api.DeletePostAsync(id);
return RedirectToAction(nameof(Manage));
}

View file

@ -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<IActionResult> 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);

22
Website/Data/BlogApi.cs Normal file
View file

@ -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<BlogPost> GetPostByUrlAsync(string url) => await Get<BlogPost>("/get/" + url);
public async Task<IEnumerable<BlogPost>> GetLatestPostsAsync(int count = 0, int offset = 0) => await Get<IEnumerable<BlogPost>>("/getlatestposts", new{count, offset});
public async Task<BlogPost> GetLatestPostAsync() => await Get<BlogPost>("/getlatestpost");
public async Task<int> GetCountAsync() => await Get<int>("/getcount");
public async Task<BlogPost> GetPostByIdAsync(int id) => await Get<BlogPost>("/get/" + id);
public async Task<BlogPost> SavePost(BlogPostSubmission post) => await Post<BlogPost>("/savepost", post);
public async Task<IEnumerable<BlogPost>> GetAllPostsAsync() => await Get<IEnumerable<BlogPost>>("/getallposts");
public async Task DeletePostAsync(int id) => await Post<object>("/deletepost", id);
public async Task PublishPostAsync(int id) => await Post<object>("/publishpost", id);
}
}

View file

@ -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<BlogPost> 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<BlogPostState>(query, new{url});
return new BlogPost(result.First());
}
}
public async Task<IEnumerable<BlogPost>> 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<BlogPostState>(query, new{limit, offset});
return results.Select(result => new BlogPost(result));
}
}
public async Task<BlogPost> GetLatestPostAsync() => (await GetLatestPostsAsync(1)).First();
public async Task<int> 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<int>(query);
return result.First();
}
}
public async Task<BlogPost> 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<BlogPostState>(query, new {id});
return new BlogPost(result.First());
}
}
public async Task<BlogPost> 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<int>(newPost ? newPostQuery : updatePostQuery, post);
return newPost ? await GetPostByIdAsync(result.Single()) : post;
}
}
public async Task<IEnumerable<BlogPost>> GetAllPostsAsync() {
const string query = "SELECT * FROM blog_posts WHERE post_deleted=0";
using (var connection = _dbProvider.NewConnection()) {
connection.Open();
var result = await connection.QueryAsync<BlogPostState>(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});
}
}
}
}

17
Website/Data/IBlogApi.cs Normal file
View file

@ -0,0 +1,17 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Website.Models;
namespace Website.Data {
public interface IBlogApi {
Task<BlogPost> GetPostByUrlAsync(string url);
Task<IEnumerable<BlogPost>> GetLatestPostsAsync(int count = 0, int offset = 0);
Task<BlogPost> GetLatestPostAsync();
Task<int> GetCountAsync();
Task<BlogPost> GetPostByIdAsync(int id);
Task<BlogPost> SavePost(BlogPostSubmission post);
Task<IEnumerable<BlogPost>> GetAllPostsAsync();
Task DeletePostAsync(int id);
Task PublishPostAsync(int id);
}
}

View file

@ -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; }
}
}

View file

@ -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; }
}
}
}

View file

@ -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<IDatabaseProvider, MySQLDatabaseProvider>()
.AddSingleton<BlogRepository, BlogRepository>()
.AddSingleton<IBlogApi, BlogApi>()
.AddSingleton<UserRepository, UserRepository>()
.AddSingleton(new GitServerApi(new HttpClient(), Configuration["gitDomain"], Configuration["gitToken"]));
.AddSingleton(new GitServerApi(new HttpClient(), Configuration["gitDomain"], Configuration["gitToken"]))
.AddHttpClient<IBlogApi, BlogApi>(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)

View file

@ -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);
}
}
}
}

View file

@ -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": ""
}

View file

@ -9,6 +9,7 @@
},
"gitDomain": "<GitDomain>",
"gitToken": "<GitToken>",
"blogApiEndpoint": "<BlogEndpoint>",
"AllowedHosts": "*",
"Kestrel": {
"EndPoints": {