aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2020-03-10 19:37:58 +0800
committercrupest <crupest@outlook.com>2020-03-10 19:37:58 +0800
commit95be9f58b7cf76f1c585791447e8393d61862e1c (patch)
treeb9165a233b00c4ab5be47f0ce786b64c178a9fdf
parent88232f85e69a5e4b390e73344b31372746d4adca (diff)
downloadtimeline-95be9f58b7cf76f1c585791447e8393d61862e1c.tar.gz
timeline-95be9f58b7cf76f1c585791447e8393d61862e1c.tar.bz2
timeline-95be9f58b7cf76f1c585791447e8393d61862e1c.zip
...
-rw-r--r--Timeline.Tests/IntegratedTests/IntegratedTestBase.cs29
-rw-r--r--Timeline.Tests/IntegratedTests/PersonalTimelineTest.cs153
-rw-r--r--Timeline.Tests/IntegratedTests/TimelineTest.cs72
-rw-r--r--Timeline.Tests/IntegratedTests/TokenTest.cs1
-rw-r--r--Timeline/Controllers/TimelineController.cs14
-rw-r--r--Timeline/Controllers/UserController.cs1
-rw-r--r--Timeline/Entities/TimelineEntity.cs2
-rw-r--r--Timeline/Models/Http/Timeline.cs24
-rw-r--r--Timeline/Models/Http/UserInfo.cs2
-rw-r--r--Timeline/Models/Timeline.cs17
-rw-r--r--Timeline/Services/TimelineService.cs79
-rw-r--r--Timeline/Startup.cs3
12 files changed, 242 insertions, 155 deletions
diff --git a/Timeline.Tests/IntegratedTests/IntegratedTestBase.cs b/Timeline.Tests/IntegratedTests/IntegratedTestBase.cs
index dfde2ea5..66904629 100644
--- a/Timeline.Tests/IntegratedTests/IntegratedTestBase.cs
+++ b/Timeline.Tests/IntegratedTests/IntegratedTestBase.cs
@@ -1,10 +1,13 @@
-using AutoMapper;
-using Microsoft.AspNetCore.Mvc.Testing;
+using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Net.Http;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using System.Threading.Tasks;
+using Timeline.Models;
+using Timeline.Models.Converters;
using Timeline.Models.Http;
using Timeline.Services;
using Timeline.Tests.Helpers;
@@ -14,12 +17,6 @@ namespace Timeline.Tests.IntegratedTests
{
public abstract class IntegratedTestBase : IClassFixture<WebApplicationFactory<Startup>>, IDisposable
{
- static IntegratedTestBase()
- {
- FluentAssertions.AssertionOptions.AssertEquivalencyUsing(options =>
- options.Excluding(m => m.RuntimeType == typeof(UserInfoLinks)));
- }
-
protected TestApplication TestApp { get; }
protected WebApplicationFactory<Startup> Factory => TestApp.Factory;
@@ -63,12 +60,22 @@ namespace Timeline.Tests.IntegratedTests
var userInfoList = new List<UserInfo>();
var userService = scope.ServiceProvider.GetRequiredService<IUserService>();
- var mapper = scope.ServiceProvider.GetRequiredService<IMapper>();
-
foreach (var user in users)
{
userService.CreateUser(user).Wait();
- userInfoList.Add(mapper.Map<UserInfo>(user));
+ }
+
+ using var client = CreateDefaultClient().Result;
+ var options = new JsonSerializerOptions
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase
+ };
+ options.Converters.Add(new JsonStringEnumConverter());
+ options.Converters.Add(new JsonDateTimeConverter());
+ foreach (var user in users)
+ {
+ var s = client.GetStringAsync($"/users/{user.Username}").Result;
+ userInfoList.Add(JsonSerializer.Deserialize<UserInfo>(s, options));
}
UserInfos = userInfoList;
diff --git a/Timeline.Tests/IntegratedTests/PersonalTimelineTest.cs b/Timeline.Tests/IntegratedTests/PersonalTimelineTest.cs
index 7d0a68e8..aa37e898 100644
--- a/Timeline.Tests/IntegratedTests/PersonalTimelineTest.cs
+++ b/Timeline.Tests/IntegratedTests/PersonalTimelineTest.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
+using Timeline.Models;
using Timeline.Models.Http;
using Timeline.Tests.Helpers;
using Xunit;
@@ -23,7 +24,7 @@ namespace Timeline.Tests.IntegratedTests
public async Task TimelineGet_Should_Work()
{
using var client = await CreateDefaultClient();
- var res = await client.GetAsync("users/user1/timeline");
+ var res = await client.GetAsync("timelines/@user1");
var body = res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelineInfo>().Which;
body.Owner.Should().BeEquivalentTo(UserInfos[1]);
@@ -37,31 +38,31 @@ namespace Timeline.Tests.IntegratedTests
{
using var client = await CreateClientAsAdministrator();
{
- var res = await client.GetAsync("users/user!!!/timeline");
+ var res = await client.GetAsync("timelines/@user!!!");
res.Should().BeInvalidModel();
}
{
- var res = await client.PatchAsJsonAsync("users/user!!!/timeline", new TimelinePatchRequest { });
+ var res = await client.PatchAsJsonAsync("timelines/@user!!!", new TimelinePatchRequest { });
res.Should().BeInvalidModel();
}
{
- var res = await client.PutAsync("users/user!!!/timeline/members/user1", null);
+ var res = await client.PutAsync("timelines/@user!!!/members/user1", null);
res.Should().BeInvalidModel();
}
{
- var res = await client.DeleteAsync("users/user!!!/timeline/members/user1");
+ var res = await client.DeleteAsync("timelines/@user!!!/members/user1");
res.Should().BeInvalidModel();
}
{
- var res = await client.GetAsync("users/user!!!/timeline/posts");
+ var res = await client.GetAsync("timelines/@user!!!/posts");
res.Should().BeInvalidModel();
}
{
- var res = await client.PostAsJsonAsync("users/user!!!/timeline/posts", new TimelinePostCreateRequest { Content = "aaa" });
+ var res = await client.PostAsJsonAsync("timelines/@user!!!/posts", TimelineHelper.TextPostCreateRequest("aaa"));
res.Should().BeInvalidModel();
}
{
- var res = await client.DeleteAsync("users/user!!!/timeline/posts/123");
+ var res = await client.DeleteAsync("timelines/@user!!!/posts/123");
res.Should().BeInvalidModel();
}
}
@@ -71,31 +72,31 @@ namespace Timeline.Tests.IntegratedTests
{
using var client = await CreateClientAsAdministrator();
{
- var res = await client.GetAsync("users/usernotexist/timeline");
+ var res = await client.GetAsync("timelines/@usernotexist");
res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.UserCommon.NotExist);
}
{
- var res = await client.PatchAsJsonAsync("users/usernotexist/timeline", new TimelinePatchRequest { });
+ var res = await client.PatchAsJsonAsync("timelines/@usernotexist", new TimelinePatchRequest { });
res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.UserCommon.NotExist);
}
{
- var res = await client.PutAsync("users/usernotexist/timeline/members/user1", null);
+ var res = await client.PutAsync("timelines/@usernotexist/members/user1", null);
res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.UserCommon.NotExist);
}
{
- var res = await client.DeleteAsync("users/usernotexist/timeline/members/user1");
+ var res = await client.DeleteAsync("timelines/@usernotexist/members/user1");
res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.UserCommon.NotExist);
}
{
- var res = await client.GetAsync("users/usernotexist/timeline/posts");
+ var res = await client.GetAsync("timelines/@usernotexist/posts");
res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.UserCommon.NotExist);
}
{
- var res = await client.PostAsJsonAsync("users/usernotexist/timeline/posts", new TimelinePostCreateRequest { Content = "aaa" });
+ var res = await client.PostAsJsonAsync("timelines/@usernotexist/posts", TimelineHelper.TextPostCreateRequest("aaa"));
res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.UserCommon.NotExist);
}
{
- var res = await client.DeleteAsync("users/usernotexist/timeline/posts/123");
+ var res = await client.DeleteAsync("timelines/@usernotexist/posts/123");
res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.UserCommon.NotExist);
}
}
@@ -107,7 +108,7 @@ namespace Timeline.Tests.IntegratedTests
async Task AssertDescription(string description)
{
- var res = await client.GetAsync("users/user1/timeline");
+ var res = await client.GetAsync("timelines/@user1");
var body = res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelineInfo>()
.Which.Description.Should().Be(description);
@@ -117,21 +118,21 @@ namespace Timeline.Tests.IntegratedTests
await AssertDescription("");
{
- var res = await client.PatchAsJsonAsync("users/user1/timeline",
+ var res = await client.PatchAsJsonAsync("timelines/@user1",
new TimelinePatchRequest { Description = mockDescription });
res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelineInfo>().Which.Description.Should().Be(mockDescription);
await AssertDescription(mockDescription);
}
{
- var res = await client.PatchAsJsonAsync("users/user1/timeline",
+ var res = await client.PatchAsJsonAsync("timelines/@user1",
new TimelinePatchRequest { Description = null });
res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelineInfo>().Which.Description.Should().Be(mockDescription);
await AssertDescription(mockDescription);
}
{
- var res = await client.PatchAsJsonAsync("users/user1/timeline",
+ var res = await client.PatchAsJsonAsync("timelines/@user1",
new TimelinePatchRequest { Description = "" });
res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelineInfo>().Which.Description.Should().Be("");
@@ -142,7 +143,7 @@ namespace Timeline.Tests.IntegratedTests
[Fact]
public async Task Member_Should_Work()
{
- const string getUrl = "users/user1/timeline";
+ const string getUrl = "timelines/@user1";
using var client = await CreateClientAsUser();
async Task AssertMembers(IList<UserInfo> members)
@@ -163,23 +164,23 @@ namespace Timeline.Tests.IntegratedTests
await AssertEmptyMembers();
{
- var res = await client.PutAsync("/users/user1/timeline/members/usernotexist", null);
+ var res = await client.PutAsync("/timelines/@user1/members/usernotexist", null);
res.Should().HaveStatusCode(400)
.And.HaveCommonBody(ErrorCodes.TimelineCommon.MemberPut_NotExist);
}
await AssertEmptyMembers();
{
- var res = await client.PutAsync("/users/user1/timeline/members/user2", null);
+ var res = await client.PutAsync("/timelines/@user1/members/user2", null);
res.Should().HaveStatusCode(200);
}
await AssertMembers(new List<UserInfo> { UserInfos[2] });
{
- var res = await client.DeleteAsync("/users/user1/timeline/members/user2");
+ var res = await client.DeleteAsync("/timelines/@user1/members/user2");
res.Should().BeDelete(true);
}
await AssertEmptyMembers();
{
- var res = await client.DeleteAsync("/users/user1/timeline/members/users2");
+ var res = await client.DeleteAsync("/timelines/@user1/members/users2");
res.Should().BeDelete(false);
}
await AssertEmptyMembers();
@@ -193,37 +194,37 @@ namespace Timeline.Tests.IntegratedTests
{
using var client = await CreateClientAs(userNumber);
{
- var res = await client.GetAsync("users/user1/timeline");
+ var res = await client.GetAsync("timelines/@user1");
res.Should().HaveStatusCode(get);
}
{
- var res = await client.PatchAsJsonAsync("users/user1/timeline", new TimelinePatchRequest { Description = "hahaha" });
+ var res = await client.PatchAsJsonAsync("timelines/@user1", new TimelinePatchRequest { Description = "hahaha" });
res.Should().HaveStatusCode(opPatchUser);
}
{
- var res = await client.PatchAsJsonAsync("users/admin/timeline", new TimelinePatchRequest { Description = "hahaha" });
+ var res = await client.PatchAsJsonAsync("timelines/@admin", new TimelinePatchRequest { Description = "hahaha" });
res.Should().HaveStatusCode(opPatchAdmin);
}
{
- var res = await client.PutAsync("users/user1/timeline/members/user2", null);
+ var res = await client.PutAsync("timelines/@user1/members/user2", null);
res.Should().HaveStatusCode(opMemberUser);
}
{
- var res = await client.DeleteAsync("users/user1/timeline/members/user2");
+ var res = await client.DeleteAsync("timelines/@user1/members/user2");
res.Should().HaveStatusCode(opMemberUser);
}
{
- var res = await client.PutAsync("users/admin/timeline/members/user2", null);
+ var res = await client.PutAsync("timelines/@admin/members/user2", null);
res.Should().HaveStatusCode(opMemberAdmin);
}
{
- var res = await client.DeleteAsync("users/admin/timeline/members/user2");
+ var res = await client.DeleteAsync("timelines/@admin/members/user2");
res.Should().HaveStatusCode(opMemberAdmin);
}
}
@@ -231,13 +232,13 @@ namespace Timeline.Tests.IntegratedTests
[Fact]
public async Task Visibility_Test()
{
- const string userUrl = "users/user1/timeline/posts";
- const string adminUrl = "users/admin/timeline/posts";
+ const string userUrl = "timelines/@user1/posts";
+ const string adminUrl = "timelines/@admin/posts";
{
using var client = await CreateClientAsUser();
using var content = new StringContent(@"{""visibility"":""abcdefg""}", System.Text.Encoding.UTF8, System.Net.Mime.MediaTypeNames.Application.Json);
- var res = await client.PatchAsync("users/user1/timeline", content);
+ var res = await client.PatchAsync("timelines/@user1", content);
res.Should().BeInvalidModel();
}
{ // default visibility is registered
@@ -257,7 +258,7 @@ namespace Timeline.Tests.IntegratedTests
{ // change visibility to public
{
using var client = await CreateClientAsUser();
- var res = await client.PatchAsJsonAsync("users/user1/timeline",
+ var res = await client.PatchAsJsonAsync("timelines/@user1",
new TimelinePatchRequest { Visibility = TimelineVisibility.Public });
res.Should().HaveStatusCode(200);
}
@@ -272,12 +273,12 @@ namespace Timeline.Tests.IntegratedTests
{
using var client = await CreateClientAsAdministrator();
{
- var res = await client.PatchAsJsonAsync("users/user1/timeline",
+ var res = await client.PatchAsJsonAsync("timelines/@user1",
new TimelinePatchRequest { Visibility = TimelineVisibility.Private });
res.Should().HaveStatusCode(200);
}
{
- var res = await client.PatchAsJsonAsync("users/admin/timeline",
+ var res = await client.PatchAsJsonAsync("timelines/@admin",
new TimelinePatchRequest { Visibility = TimelineVisibility.Private });
res.Should().HaveStatusCode(200);
}
@@ -299,7 +300,7 @@ namespace Timeline.Tests.IntegratedTests
}
{ // add member
using var client = await CreateClientAsAdministrator();
- var res = await client.PutAsync("/users/admin/timeline/members/user1", null);
+ var res = await client.PutAsync("/timelines/@admin/members/user1", null);
res.Should().HaveStatusCode(200);
}
{ // now user can read admin's
@@ -316,15 +317,15 @@ namespace Timeline.Tests.IntegratedTests
{
using (var client = await CreateClientAsUser())
{
- var res = await client.PutAsync("users/user1/timeline/members/user2", null);
+ var res = await client.PutAsync("timelines/@user1/members/user2", null);
res.Should().HaveStatusCode(200);
}
using (var client = await CreateDefaultClient())
{
{ // no auth should get 401
- var res = await client.PostAsJsonAsync("users/user1/timeline/posts",
- new TimelinePostCreateRequest { Content = "aaa" });
+ var res = await client.PostAsJsonAsync("timelines/@user1/posts",
+ TimelineHelper.TextPostCreateRequest("aaa"));
res.Should().HaveStatusCode(401);
}
}
@@ -332,13 +333,13 @@ namespace Timeline.Tests.IntegratedTests
using (var client = await CreateClientAsUser())
{
{ // post self's
- var res = await client.PostAsJsonAsync("users/user1/timeline/posts",
- new TimelinePostCreateRequest { Content = "aaa" });
+ var res = await client.PostAsJsonAsync("timelines/@user1/posts",
+ TimelineHelper.TextPostCreateRequest("aaa"));
res.Should().HaveStatusCode(200);
}
{ // post other not as a member should get 403
- var res = await client.PostAsJsonAsync("users/admin/timeline/posts",
- new TimelinePostCreateRequest { Content = "aaa" });
+ var res = await client.PostAsJsonAsync("timelines/@admin/posts",
+ TimelineHelper.TextPostCreateRequest("aaa"));
res.Should().HaveStatusCode(403);
}
}
@@ -346,8 +347,8 @@ namespace Timeline.Tests.IntegratedTests
using (var client = await CreateClientAsAdministrator())
{
{ // post as admin
- var res = await client.PostAsJsonAsync("users/user1/timeline/posts",
- new TimelinePostCreateRequest { Content = "aaa" });
+ var res = await client.PostAsJsonAsync("timelines/@user1/posts",
+ TimelineHelper.TextPostCreateRequest("aaa"));
res.Should().HaveStatusCode(200);
}
}
@@ -355,8 +356,8 @@ namespace Timeline.Tests.IntegratedTests
using (var client = await CreateClientAs(2))
{
{ // post as member
- var res = await client.PostAsJsonAsync("users/user1/timeline/posts",
- new TimelinePostCreateRequest { Content = "aaa" });
+ var res = await client.PostAsJsonAsync("timelines/@user1/posts",
+ TimelineHelper.TextPostCreateRequest("aaa"));
res.Should().HaveStatusCode(200);
}
}
@@ -368,8 +369,8 @@ namespace Timeline.Tests.IntegratedTests
async Task<long> CreatePost(int userNumber)
{
using var client = await CreateClientAs(userNumber);
- var res = await client.PostAsJsonAsync($"users/user1/timeline/posts",
- new TimelinePostCreateRequest { Content = "aaa" });
+ var res = await client.PostAsJsonAsync($"timelines/@user1/posts",
+ TimelineHelper.TextPostCreateRequest("aaa"));
return res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelinePostInfo>()
.Which.Id;
@@ -378,53 +379,53 @@ namespace Timeline.Tests.IntegratedTests
using (var client = await CreateClientAsUser())
{
{
- var res = await client.PutAsync("users/user1/timeline/members/user2", null);
+ var res = await client.PutAsync("timelines/@user1/members/user2", null);
res.Should().HaveStatusCode(200);
}
{
- var res = await client.PutAsync("users/user1/timeline/members/user3", null);
+ var res = await client.PutAsync("timelines/@user1/members/user3", null);
res.Should().HaveStatusCode(200);
}
}
{ // no auth should get 401
using var client = await CreateDefaultClient();
- var res = await client.DeleteAsync("users/user1/timeline/posts/12");
+ var res = await client.DeleteAsync("timelines/@user1/posts/12");
res.Should().HaveStatusCode(401);
}
{ // self can delete self
var postId = await CreatePost(1);
using var client = await CreateClientAsUser();
- var res = await client.DeleteAsync($"users/user1/timeline/posts/{postId}");
+ var res = await client.DeleteAsync($"timelines/@user1/posts/{postId}");
res.Should().HaveStatusCode(200);
}
{ // admin can delete any
var postId = await CreatePost(1);
using var client = await CreateClientAsAdministrator();
- var res = await client.DeleteAsync($"users/user1/timeline/posts/{postId}");
+ var res = await client.DeleteAsync($"timelines/@user1/posts/{postId}");
res.Should().HaveStatusCode(200);
}
{ // owner can delete other
var postId = await CreatePost(2);
using var client = await CreateClientAsUser();
- var res = await client.DeleteAsync($"users/user1/timeline/posts/{postId}");
+ var res = await client.DeleteAsync($"timelines/@user1/posts/{postId}");
res.Should().HaveStatusCode(200);
}
{ // author can delete self
var postId = await CreatePost(2);
using var client = await CreateClientAs(2);
- var res = await client.DeleteAsync($"users/user1/timeline/posts/{postId}");
+ var res = await client.DeleteAsync($"timelines/@user1/posts/{postId}");
res.Should().HaveStatusCode(200);
}
{ // otherwise is forbidden
var postId = await CreatePost(2);
using var client = await CreateClientAs(3);
- var res = await client.DeleteAsync($"users/user1/timeline/posts/{postId}");
+ var res = await client.DeleteAsync($"timelines/@user1/posts/{postId}");
res.Should().HaveStatusCode(403);
}
}
@@ -435,31 +436,31 @@ namespace Timeline.Tests.IntegratedTests
{
using var client = await CreateClientAsUser();
{
- var res = await client.GetAsync("users/user1/timeline/posts");
+ var res = await client.GetAsync("timelines/@user1/posts");
res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelinePostInfo[]>()
.Which.Should().NotBeNull().And.BeEmpty();
}
{
- var res = await client.PostAsJsonAsync("users/user1/timeline/posts",
- new TimelinePostCreateRequest { Content = null });
+ var res = await client.PostAsJsonAsync("timelines/@user1/posts",
+ TimelineHelper.TextPostCreateRequest(null));
res.Should().BeInvalidModel();
}
const string mockContent = "aaa";
TimelinePostInfo createRes;
{
- var res = await client.PostAsJsonAsync("users/user1/timeline/posts",
- new TimelinePostCreateRequest { Content = mockContent });
+ var res = await client.PostAsJsonAsync("timelines/@user1/posts",
+ TimelineHelper.TextPostCreateRequest(mockContent));
var body = res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelinePostInfo>()
.Which;
body.Should().NotBeNull();
- body.Content.Should().Be(mockContent);
+ body.Content.Should().BeEquivalentTo(TimelineHelper.TextPostContent(mockContent));
body.Author.Should().BeEquivalentTo(UserInfos[1]);
createRes = body;
}
{
- var res = await client.GetAsync("users/user1/timeline/posts");
+ var res = await client.GetAsync("timelines/@user1/posts");
res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelinePostInfo[]>()
.Which.Should().NotBeNull().And.BeEquivalentTo(createRes);
@@ -468,33 +469,33 @@ namespace Timeline.Tests.IntegratedTests
var mockTime2 = DateTime.Now.AddDays(-1);
TimelinePostInfo createRes2;
{
- var res = await client.PostAsJsonAsync("users/user1/timeline/posts",
- new TimelinePostCreateRequest { Content = mockContent2, Time = mockTime2 });
+ var res = await client.PostAsJsonAsync("timelines/@user1/posts",
+ TimelineHelper.TextPostCreateRequest(mockContent2, mockTime2));
var body = res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelinePostInfo>()
.Which;
body.Should().NotBeNull();
- body.Content.Should().Be(mockContent2);
+ body.Content.Should().BeEquivalentTo(TimelineHelper.TextPostContent(mockContent2));
body.Author.Should().BeEquivalentTo(UserInfos[1]);
body.Time.Should().BeCloseTo(mockTime2, 1000);
createRes2 = body;
}
{
- var res = await client.GetAsync("users/user1/timeline/posts");
+ var res = await client.GetAsync("timelines/@user1/posts");
res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelinePostInfo[]>()
.Which.Should().NotBeNull().And.BeEquivalentTo(createRes, createRes2);
}
{
- var res = await client.DeleteAsync($"users/user1/timeline/posts/{createRes.Id}");
+ var res = await client.DeleteAsync($"timelines/@user1/posts/{createRes.Id}");
res.Should().BeDelete(true);
}
{
- var res = await client.DeleteAsync("users/user1/timeline/posts/30000");
+ var res = await client.DeleteAsync("timelines/@user1/posts/30000");
res.Should().BeDelete(false);
}
{
- var res = await client.GetAsync("users/user1/timeline/posts");
+ var res = await client.GetAsync("timelines/@user1/posts");
res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelinePostInfo[]>()
.Which.Should().NotBeNull().And.BeEquivalentTo(createRes2);
@@ -509,8 +510,8 @@ namespace Timeline.Tests.IntegratedTests
async Task<long> CreatePost(DateTime time)
{
- var res = await client.PostAsJsonAsync("users/user1/timeline/posts",
- new TimelinePostCreateRequest { Content = "aaa", Time = time });
+ var res = await client.PostAsJsonAsync("timelines/@user1/posts",
+ TimelineHelper.TextPostCreateRequest("aaa", time));
return res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelinePostInfo>()
.Which.Id;
@@ -522,7 +523,7 @@ namespace Timeline.Tests.IntegratedTests
var id2 = await CreatePost(now);
{
- var res = await client.GetAsync("users/user1/timeline/posts");
+ var res = await client.GetAsync("timelines/@user1/posts");
res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelinePostInfo[]>()
.Which.Select(p => p.Id).Should().Equal(id1, id2, id0);
diff --git a/Timeline.Tests/IntegratedTests/TimelineTest.cs b/Timeline.Tests/IntegratedTests/TimelineTest.cs
index 14a0a59e..3fceb1d5 100644
--- a/Timeline.Tests/IntegratedTests/TimelineTest.cs
+++ b/Timeline.Tests/IntegratedTests/TimelineTest.cs
@@ -6,12 +6,38 @@ using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
+using Timeline.Models;
using Timeline.Models.Http;
using Timeline.Tests.Helpers;
using Xunit;
namespace Timeline.Tests.IntegratedTests
{
+ public static class TimelineHelper
+ {
+ public static TimelinePostContentInfo TextPostContent(string text)
+ {
+ return new TimelinePostContentInfo
+ {
+ Type = "text",
+ Text = text
+ };
+ }
+
+ public static TimelinePostCreateRequest TextPostCreateRequest(string text, DateTime? time = null)
+ {
+ return new TimelinePostCreateRequest
+ {
+ Content = new TimelinePostCreateRequestContent
+ {
+ Type = "text",
+ Text = text
+ },
+ Time = time
+ };
+ }
+ }
+
public class TimelineTest : IntegratedTestBase
{
public TimelineTest(WebApplicationFactory<Startup> factory)
@@ -45,7 +71,7 @@ namespace Timeline.Tests.IntegratedTests
var client = await CreateDefaultClient();
{
- var res = await client.GetAsync("/users/user1/timeline");
+ var res = await client.GetAsync("/timelines/@user1");
user1Timeline = res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelineInfo>().Which;
}
@@ -80,7 +106,7 @@ namespace Timeline.Tests.IntegratedTests
var client = await CreateClientAsUser();
{
- var res = await client.PutAsync("/users/user1/timeline/members/user3", null);
+ var res = await client.PutAsync("/timelines/@user1/members/user3", null);
res.Should().HaveStatusCode(200);
}
@@ -90,7 +116,7 @@ namespace Timeline.Tests.IntegratedTests
}
{
- var res = await client.PatchAsJsonAsync("/users/user1/timeline", new TimelinePatchRequest { Visibility = TimelineVisibility.Public });
+ var res = await client.PatchAsJsonAsync("/timelines/@user1", new TimelinePatchRequest { Visibility = TimelineVisibility.Public });
res.Should().HaveStatusCode(200);
}
@@ -100,7 +126,7 @@ namespace Timeline.Tests.IntegratedTests
}
{
- var res = await client.GetAsync("/users/user1/timeline");
+ var res = await client.GetAsync("/timelines/@user1");
var timeline = res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelineInfo>().Which;
testResultRelate.Add(timeline);
@@ -123,7 +149,7 @@ namespace Timeline.Tests.IntegratedTests
var client = await CreateClientAs(2);
{
- var res = await client.PutAsync("/users/user2/timeline/members/user3", null);
+ var res = await client.PutAsync("/timelines/@user2/members/user3", null);
res.Should().HaveStatusCode(200);
}
@@ -133,7 +159,7 @@ namespace Timeline.Tests.IntegratedTests
}
{
- var res = await client.PatchAsJsonAsync("/users/user2/timeline", new TimelinePatchRequest { Visibility = TimelineVisibility.Register });
+ var res = await client.PatchAsJsonAsync("/timelines/@user2", new TimelinePatchRequest { Visibility = TimelineVisibility.Register });
res.Should().HaveStatusCode(200);
}
@@ -143,7 +169,7 @@ namespace Timeline.Tests.IntegratedTests
}
{
- var res = await client.GetAsync("/users/user2/timeline");
+ var res = await client.GetAsync("/timelines/@user2");
var timeline = res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelineInfo>().Which;
testResultRelate.Add(timeline);
@@ -165,7 +191,7 @@ namespace Timeline.Tests.IntegratedTests
var client = await CreateClientAs(3);
{
- var res = await client.PatchAsJsonAsync("/users/user3/timeline", new TimelinePatchRequest { Visibility = TimelineVisibility.Private });
+ var res = await client.PatchAsJsonAsync("/timelines/@user3", new TimelinePatchRequest { Visibility = TimelineVisibility.Private });
res.Should().HaveStatusCode(200);
}
@@ -175,7 +201,7 @@ namespace Timeline.Tests.IntegratedTests
}
{
- var res = await client.GetAsync("/users/user3/timeline");
+ var res = await client.GetAsync("/timelines/@user3");
var timeline = res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelineInfo>().Which;
testResultRelate.Add(timeline);
@@ -396,7 +422,7 @@ namespace Timeline.Tests.IntegratedTests
res.Should().BeInvalidModel();
}
{
- var res = await client.PostAsJsonAsync("timelines/aaa!!!/posts", new TimelinePostCreateRequest { Content = "aaa" });
+ var res = await client.PostAsJsonAsync("timelines/aaa!!!/posts", TimelineHelper.TextPostCreateRequest("aaa"));
res.Should().BeInvalidModel();
}
{
@@ -430,7 +456,7 @@ namespace Timeline.Tests.IntegratedTests
res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.TimelineCommon.NotExist);
}
{
- var res = await client.PostAsJsonAsync("timelines/notexist/posts", new TimelinePostCreateRequest { Content = "aaa" });
+ var res = await client.PostAsJsonAsync("timelines/notexist/posts", TimelineHelper.TextPostCreateRequest("aaa"));
res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.TimelineCommon.NotExist);
}
{
@@ -673,7 +699,7 @@ namespace Timeline.Tests.IntegratedTests
{
{ // no auth should get 401
var res = await client.PostAsJsonAsync("timelines/t1/posts",
- new TimelinePostCreateRequest { Content = "aaa" });
+ TimelineHelper.TextPostCreateRequest("aaa"));
res.Should().HaveStatusCode(401);
}
}
@@ -682,12 +708,12 @@ namespace Timeline.Tests.IntegratedTests
{
{ // post self's
var res = await client.PostAsJsonAsync("timelines/t1/posts",
- new TimelinePostCreateRequest { Content = "aaa" });
+ TimelineHelper.TextPostCreateRequest("aaa"));
res.Should().HaveStatusCode(200);
}
{ // post other not as a member should get 403
var res = await client.PostAsJsonAsync("timelines/t0/posts",
- new TimelinePostCreateRequest { Content = "aaa" });
+ TimelineHelper.TextPostCreateRequest("aaa"));
res.Should().HaveStatusCode(403);
}
}
@@ -696,7 +722,7 @@ namespace Timeline.Tests.IntegratedTests
{
{ // post as admin
var res = await client.PostAsJsonAsync("timelines/t1/posts",
- new TimelinePostCreateRequest { Content = "aaa" });
+ TimelineHelper.TextPostCreateRequest("aaa"));
res.Should().HaveStatusCode(200);
}
}
@@ -705,7 +731,7 @@ namespace Timeline.Tests.IntegratedTests
{
{ // post as member
var res = await client.PostAsJsonAsync("timelines/t1/posts",
- new TimelinePostCreateRequest { Content = "aaa" });
+ TimelineHelper.TextPostCreateRequest("aaa"));
res.Should().HaveStatusCode(200);
}
}
@@ -720,7 +746,7 @@ namespace Timeline.Tests.IntegratedTests
{
using var client = await CreateClientAs(userNumber);
var res = await client.PostAsJsonAsync($"timelines/t1/posts",
- new TimelinePostCreateRequest { Content = "aaa" });
+ TimelineHelper.TextPostCreateRequest("aaa"));
return res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelinePostInfo>()
.Which.Id;
@@ -795,19 +821,19 @@ namespace Timeline.Tests.IntegratedTests
}
{
var res = await client.PostAsJsonAsync("timelines/t1/posts",
- new TimelinePostCreateRequest { Content = null });
+ TimelineHelper.TextPostCreateRequest(null));
res.Should().BeInvalidModel();
}
const string mockContent = "aaa";
TimelinePostInfo createRes;
{
var res = await client.PostAsJsonAsync("timelines/t1/posts",
- new TimelinePostCreateRequest { Content = mockContent });
+ TimelineHelper.TextPostCreateRequest(mockContent));
var body = res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelinePostInfo>()
.Which;
body.Should().NotBeNull();
- body.Content.Should().Be(mockContent);
+ body.Content.Should().BeEquivalentTo(TimelineHelper.TextPostContent(mockContent));
body.Author.Should().BeEquivalentTo(UserInfos[1]);
createRes = body;
}
@@ -822,12 +848,12 @@ namespace Timeline.Tests.IntegratedTests
TimelinePostInfo createRes2;
{
var res = await client.PostAsJsonAsync("timelines/t1/posts",
- new TimelinePostCreateRequest { Content = mockContent2, Time = mockTime2 });
+ TimelineHelper.TextPostCreateRequest(mockContent2, mockTime2));
var body = res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelinePostInfo>()
.Which;
body.Should().NotBeNull();
- body.Content.Should().Be(mockContent2);
+ body.Content.Should().BeEquivalentTo(TimelineHelper.TextPostContent(mockContent2));
body.Author.Should().BeEquivalentTo(UserInfos[1]);
body.Time.Should().BeCloseTo(mockTime2, 1000);
createRes2 = body;
@@ -865,7 +891,7 @@ namespace Timeline.Tests.IntegratedTests
async Task<long> CreatePost(DateTime time)
{
var res = await client.PostAsJsonAsync("timelines/t1/posts",
- new TimelinePostCreateRequest { Content = "aaa", Time = time });
+ TimelineHelper.TextPostCreateRequest("aaa", time));
return res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelinePostInfo>()
.Which.Id;
diff --git a/Timeline.Tests/IntegratedTests/TokenTest.cs b/Timeline.Tests/IntegratedTests/TokenTest.cs
index 928d546c..7b28746f 100644
--- a/Timeline.Tests/IntegratedTests/TokenTest.cs
+++ b/Timeline.Tests/IntegratedTests/TokenTest.cs
@@ -4,6 +4,7 @@ using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
+using Timeline.Models;
using Timeline.Models.Http;
using Timeline.Services;
using Timeline.Tests.Helpers;
diff --git a/Timeline/Controllers/TimelineController.cs b/Timeline/Controllers/TimelineController.cs
index 38fe7475..440b0d19 100644
--- a/Timeline/Controllers/TimelineController.cs
+++ b/Timeline/Controllers/TimelineController.cs
@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
+using Microsoft.Net.Http.Headers;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
@@ -113,6 +114,19 @@ namespace Timeline.Controllers
return result;
}
+ // TODO: Make cache available.
+ [HttpGet("timelines/{name}/posts/{id}/data")]
+ public async Task<ActionResult<List<TimelinePostInfo>>> PostDataGet([FromRoute][GeneralTimelineName] string name, [FromRoute] long id)
+ {
+ if (!this.IsAdministrator() && !await _service.HasReadPermission(name, this.GetOptionalUserId()))
+ {
+ return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
+ }
+
+ var data = await _service.GetPostData(name, id);
+ return File(data.Data, data.Type, data.LastModified, new EntityTagHeaderValue(data.ETag));
+ }
+
[HttpPost("timelines/{name}/posts")]
[Authorize]
public async Task<ActionResult<TimelinePostInfo>> PostPost([FromRoute][GeneralTimelineName] string name, [FromBody] TimelinePostCreateRequest body)
diff --git a/Timeline/Controllers/UserController.cs b/Timeline/Controllers/UserController.cs
index a3e8d816..0bc8bcda 100644
--- a/Timeline/Controllers/UserController.cs
+++ b/Timeline/Controllers/UserController.cs
@@ -7,6 +7,7 @@ using System.Linq;
using System.Threading.Tasks;
using Timeline.Auth;
using Timeline.Helpers;
+using Timeline.Models;
using Timeline.Models.Http;
using Timeline.Models.Validation;
using Timeline.Services;
diff --git a/Timeline/Entities/TimelineEntity.cs b/Timeline/Entities/TimelineEntity.cs
index 56b36d4e..3149d4c2 100644
--- a/Timeline/Entities/TimelineEntity.cs
+++ b/Timeline/Entities/TimelineEntity.cs
@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
-using Timeline.Models.Http;
+using Timeline.Models;
namespace Timeline.Entities
{
diff --git a/Timeline/Models/Http/Timeline.cs b/Timeline/Models/Http/Timeline.cs
index 55c3a3bf..9e2aefd0 100644
--- a/Timeline/Models/Http/Timeline.cs
+++ b/Timeline/Models/Http/Timeline.cs
@@ -72,25 +72,27 @@ namespace Timeline.Models.Http
}
}
- public class TimelinePostConverter : ITypeConverter<ITimelinePostContent, TimelinePostContentInfo>
+ public class TimelinePostContentResolver : IValueResolver<TimelinePost, TimelinePostInfo, TimelinePostContentInfo>
{
private readonly IActionContextAccessor _actionContextAccessor;
private readonly IUrlHelperFactory _urlHelperFactory;
- public TimelinePostConverter(IActionContextAccessor actionContextAccessor, IUrlHelperFactory urlHelperFactory)
+ public TimelinePostContentResolver(IActionContextAccessor actionContextAccessor, IUrlHelperFactory urlHelperFactory)
{
_actionContextAccessor = actionContextAccessor;
_urlHelperFactory = urlHelperFactory;
}
- public TimelinePostContentInfo Convert(ITimelinePostContent source, TimelinePostContentInfo destination, ResolutionContext context)
+ public TimelinePostContentInfo Resolve(TimelinePost source, TimelinePostInfo destination, TimelinePostContentInfo destMember, ResolutionContext context)
{
if (_actionContextAccessor.ActionContext == null)
throw new InvalidOperationException("No action context, can't fill urls.");
var urlHelper = _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext);
- if (source is TextTimelinePostContent textContent)
+ var sourceContent = source.Content;
+
+ if (sourceContent is TextTimelinePostContent textContent)
{
return new TimelinePostContentInfo
{
@@ -98,14 +100,21 @@ namespace Timeline.Models.Http
Text = textContent.Text
};
}
- else if (source is ImageTimelinePostContent imageContent)
+ else if (sourceContent is ImageTimelinePostContent imageContent)
{
return new TimelinePostContentInfo
{
Type = TimelinePostContentTypes.Image,
- Url = urlHelper.ActionLink(action: "PostDataGet", nameof(TimelineController)[0..^nameof(Controller).Length], new { source.Name })
+ Url = urlHelper.ActionLink(
+ action: nameof(TimelineController.PostDataGet),
+ controller: nameof(TimelineController)[0..^nameof(Controller).Length],
+ values: new { Name = source.TimelineName, Id = source.Id })
};
}
+ else
+ {
+ throw new InvalidOperationException("Unknown content type.");
+ }
}
}
@@ -114,8 +123,7 @@ namespace Timeline.Models.Http
public TimelineInfoAutoMapperProfile()
{
CreateMap<Timeline, TimelineInfo>().ForMember(u => u._links, opt => opt.MapFrom<TimelineInfoLinksValueResolver>());
- CreateMap<TimelinePost, TimelinePostInfo>();
- CreateMap<ITimelinePostContent, TimelinePostContentInfo>().ConvertUsing<TimelinePostConverter>();
+ CreateMap<TimelinePost, TimelinePostInfo>().ForMember(p => p.Content, opt => opt.MapFrom<TimelinePostContentResolver>());
CreateMap<TimelinePatchRequest, TimelineChangePropertyRequest>();
}
}
diff --git a/Timeline/Models/Http/UserInfo.cs b/Timeline/Models/Http/UserInfo.cs
index 4f887549..b4bf14c1 100644
--- a/Timeline/Models/Http/UserInfo.cs
+++ b/Timeline/Models/Http/UserInfo.cs
@@ -45,7 +45,7 @@ namespace Timeline.Models.Http
{
Self = urlHelper.ActionLink(nameof(UserController.Get), nameof(UserController)[0..^nameof(Controller).Length], new { destination.Username }),
Avatar = urlHelper.ActionLink(nameof(UserAvatarController.Get), nameof(UserAvatarController)[0..^nameof(Controller).Length], new { destination.Username }),
- Timeline = urlHelper.ActionLink(nameof(PersonalTimelineController.TimelineGet), nameof(PersonalTimelineController)[0..^nameof(Controller).Length], new { destination.Username })
+ Timeline = urlHelper.ActionLink(nameof(TimelineController.TimelineGet), nameof(TimelineController)[0..^nameof(Controller).Length], new { Name = "@" + destination.Username })
};
return result;
}
diff --git a/Timeline/Models/Timeline.cs b/Timeline/Models/Timeline.cs
index 6d4c924d..803a5c5c 100644
--- a/Timeline/Models/Timeline.cs
+++ b/Timeline/Models/Timeline.cs
@@ -48,11 +48,22 @@ namespace Timeline.Models
public class TimelinePost
{
+ public TimelinePost(long id, ITimelinePostContent content, DateTime time, User author, DateTime lastUpdated, string timelineName)
+ {
+ Id = id;
+ Content = content;
+ Time = time;
+ Author = author;
+ LastUpdated = lastUpdated;
+ TimelineName = timelineName;
+ }
+
public long Id { get; set; }
- public ITimelinePostContent Content { get; set; } = default!;
+ public ITimelinePostContent Content { get; set; }
public DateTime Time { get; set; }
- public User Author { get; set; } = default!;
- public DateTime LastUpdated { get; set; } = default!;
+ public User Author { get; set; }
+ public DateTime LastUpdated { get; set; }
+ public string TimelineName { get; set; }
}
#pragma warning disable CA1724 // Type names should not match namespaces
diff --git a/Timeline/Services/TimelineService.cs b/Timeline/Services/TimelineService.cs
index 1bccb855..3a5825ae 100644
--- a/Timeline/Services/TimelineService.cs
+++ b/Timeline/Services/TimelineService.cs
@@ -32,12 +32,14 @@ namespace Timeline.Services
public long UserId { get; set; }
}
- public class DataWithType
+ public class PostData
{
#pragma warning disable CA1819 // Properties should not return arrays
public byte[] Data { get; set; } = default!;
#pragma warning restore CA1819 // Properties should not return arrays
public string Type { get; set; } = default!;
+ public string ETag { get; set; } = default!;
+ public DateTime LastModified { get; set; } = default!;
}
/// <summary>
@@ -103,7 +105,7 @@ namespace Timeline.Services
/// <remarks>
/// Use this method to retrieve the image of image post.
/// </remarks>
- Task<DataWithType> GetPostData(string name, long postId);
+ Task<PostData> GetPostData(string name, long postId);
/// <summary>
/// Create a new text post in timeline.
@@ -334,6 +336,8 @@ namespace Timeline.Services
/// </remarks>
protected abstract Task<long> FindTimelineId(string name);
+ protected abstract string GenerateName(string name);
+
public async Task<Models.Timeline> GetTimeline(string name)
{
if (name == null)
@@ -355,7 +359,7 @@ namespace Timeline.Services
return new Models.Timeline
{
- Name = timelineEntity.Name ?? ("@" + owner.Username),
+ Name = GenerateName(name),
Description = timelineEntity.Description ?? "",
Owner = owner,
Visibility = timelineEntity.Visibility,
@@ -387,19 +391,19 @@ namespace Timeline.Services
_ => throw new DatabaseCorruptedException(string.Format(CultureInfo.InvariantCulture, ExceptionDatabaseUnknownContentType, type))
};
- posts.Add(new TimelinePost
- {
- Id = entity.LocalId,
- Content = content,
- Author = author,
- Time = entity.Time,
- LastUpdated = entity.LastUpdated
- });
+ posts.Add(new TimelinePost(
+ id: entity.LocalId,
+ content: content,
+ time: entity.Time,
+ author: author,
+ lastUpdated: entity.LastUpdated,
+ timelineName: GenerateName(name)
+ ));
}
}
return posts;
}
- public async Task<DataWithType> GetPostData(string name, long postId)
+ public async Task<PostData> GetPostData(string name, long postId)
{
if (name == null)
throw new ArgumentNullException(nameof(name));
@@ -437,10 +441,12 @@ namespace Timeline.Services
await Database.SaveChangesAsync();
}
- return new DataWithType
+ return new PostData
{
Data = data,
- Type = postEntity.ExtraContent
+ Type = postEntity.ExtraContent,
+ ETag = tag,
+ LastModified = postEntity.LastUpdated
};
}
@@ -474,14 +480,15 @@ namespace Timeline.Services
Database.TimelinePosts.Add(postEntity);
await Database.SaveChangesAsync();
- return new TimelinePost
- {
- Id = postEntity.LocalId,
- Content = new TextTimelinePostContent(text),
- Author = author,
- Time = finalTime,
- LastUpdated = currentTime
- };
+
+ return new TimelinePost(
+ id: postEntity.LocalId,
+ content: new TextTimelinePostContent(text),
+ time: finalTime,
+ author: author,
+ lastUpdated: currentTime,
+ timelineName: GenerateName(name)
+ );
}
public async Task<TimelinePost> CreateImagePost(string name, long authorId, byte[] data, DateTime? time)
@@ -521,14 +528,14 @@ namespace Timeline.Services
Database.TimelinePosts.Add(postEntity);
await Database.SaveChangesAsync();
- return new TimelinePost
- {
- Id = postEntity.LocalId,
- Content = new ImageTimelinePostContent(tag),
- Author = author,
- Time = finalTime,
- LastUpdated = currentTime
- };
+ return new TimelinePost(
+ id: postEntity.LocalId,
+ content: new ImageTimelinePostContent(tag),
+ time: finalTime,
+ author: author,
+ lastUpdated: currentTime,
+ timelineName: GenerateName(name)
+ );
}
public async Task DeletePost(string name, long id)
@@ -767,6 +774,11 @@ namespace Timeline.Services
return timelineEntity.Id;
}
}
+
+ protected override string GenerateName(string name)
+ {
+ return name;
+ }
}
public class PersonalTimelineService : BaseTimelineManager, IPersonalTimelineService
@@ -818,6 +830,11 @@ namespace Timeline.Services
return newTimelineEntity.Id;
}
}
+
+ protected override string GenerateName(string name)
+ {
+ return "@" + name;
+ }
}
public class TimelineService : ITimelineService
@@ -996,7 +1013,7 @@ namespace Timeline.Services
return s.GetPosts(realName);
}
- public Task<DataWithType> GetPostData(string name, long postId)
+ public Task<PostData> GetPostData(string name, long postId)
{
var s = BranchName(name, out var realName);
return s.GetPostData(realName, postId);
diff --git a/Timeline/Startup.cs b/Timeline/Startup.cs
index 85822a14..d2fd22bd 100644
--- a/Timeline/Startup.cs
+++ b/Timeline/Startup.cs
@@ -101,7 +101,8 @@ namespace Timeline
services.AddUserAvatarService();
- services.AddScoped<ITimelineService, TimelineManager>();
+ services.AddScoped<ITimelineService, TimelineService>();
+ services.AddScoped<IOrdinaryTimelineService, OrdinaryTimelineService>();
services.AddScoped<IPersonalTimelineService, PersonalTimelineService>();
services.TryAddSingleton<IActionContextAccessor, ActionContextAccessor>();