aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2020-02-02 00:31:33 +0800
committercrupest <crupest@outlook.com>2020-02-02 00:31:33 +0800
commite71cb8e368d081747014f1434c5ee157660783f2 (patch)
tree2d161307f76b29c52c60048cf4f65d354a967df3
parent673fa5f0645308bfa8d17dc4e0e145bbaf239329 (diff)
downloadtimeline-e71cb8e368d081747014f1434c5ee157660783f2.tar.gz
timeline-e71cb8e368d081747014f1434c5ee157660783f2.tar.bz2
timeline-e71cb8e368d081747014f1434c5ee157660783f2.zip
...
-rw-r--r--Timeline.Tests/IntegratedTests/PersonalTimelineTest.cs14
-rw-r--r--Timeline/Controllers/PersonalTimelineController.cs8
-rw-r--r--Timeline/Models/Http/TimelineCommon.cs36
-rw-r--r--Timeline/Services/TimelineService.cs133
4 files changed, 101 insertions, 90 deletions
diff --git a/Timeline.Tests/IntegratedTests/PersonalTimelineTest.cs b/Timeline.Tests/IntegratedTests/PersonalTimelineTest.cs
index 81446fd8..cc170a98 100644
--- a/Timeline.Tests/IntegratedTests/PersonalTimelineTest.cs
+++ b/Timeline.Tests/IntegratedTests/PersonalTimelineTest.cs
@@ -25,7 +25,7 @@ namespace Timeline.Tests.IntegratedTests
using var client = await CreateDefaultClient();
var res = await client.GetAsync("users/user1/timeline");
var body = res.Should().HaveStatusCode(200)
- .And.HaveJsonBody<BaseTimelineInfo>().Which;
+ .And.HaveJsonBody<TimelineInfo>().Which;
body.Owner.Should().BeEquivalentTo(UserInfos[1]);
body.Visibility.Should().Be(TimelineVisibility.Register);
body.Description.Should().Be("");
@@ -109,7 +109,7 @@ namespace Timeline.Tests.IntegratedTests
{
var res = await client.GetAsync("users/user1/timeline");
var body = res.Should().HaveStatusCode(200)
- .And.HaveJsonBody<BaseTimelineInfo>()
+ .And.HaveJsonBody<TimelineInfo>()
.Which.Description.Should().Be(description);
}
@@ -120,21 +120,21 @@ namespace Timeline.Tests.IntegratedTests
var res = await client.PatchAsJsonAsync("users/user1/timeline",
new TimelinePatchRequest { Description = mockDescription });
res.Should().HaveStatusCode(200)
- .And.HaveJsonBody<BaseTimelineInfo>().Which.Description.Should().Be(mockDescription);
+ .And.HaveJsonBody<TimelineInfo>().Which.Description.Should().Be(mockDescription);
await AssertDescription(mockDescription);
}
{
var res = await client.PatchAsJsonAsync("users/user1/timeline",
new TimelinePatchRequest { Description = null });
res.Should().HaveStatusCode(200)
- .And.HaveJsonBody<BaseTimelineInfo>().Which.Description.Should().Be(mockDescription);
+ .And.HaveJsonBody<TimelineInfo>().Which.Description.Should().Be(mockDescription);
await AssertDescription(mockDescription);
}
{
var res = await client.PatchAsJsonAsync("users/user1/timeline",
new TimelinePatchRequest { Description = "" });
res.Should().HaveStatusCode(200)
- .And.HaveJsonBody<BaseTimelineInfo>().Which.Description.Should().Be("");
+ .And.HaveJsonBody<TimelineInfo>().Which.Description.Should().Be("");
await AssertDescription("");
}
}
@@ -149,7 +149,7 @@ namespace Timeline.Tests.IntegratedTests
{
var res = await client.GetAsync(getUrl);
res.Should().HaveStatusCode(200)
- .And.HaveJsonBody<BaseTimelineInfo>()
+ .And.HaveJsonBody<TimelineInfo>()
.Which.Members.Should().NotBeNull().And.BeEquivalentTo(members);
}
@@ -157,7 +157,7 @@ namespace Timeline.Tests.IntegratedTests
{
var res = await client.GetAsync(getUrl);
res.Should().HaveStatusCode(200)
- .And.HaveJsonBody<BaseTimelineInfo>()
+ .And.HaveJsonBody<TimelineInfo>()
.Which.Members.Should().NotBeNull().And.BeEmpty();
}
diff --git a/Timeline/Controllers/PersonalTimelineController.cs b/Timeline/Controllers/PersonalTimelineController.cs
index 11353bb5..842da015 100644
--- a/Timeline/Controllers/PersonalTimelineController.cs
+++ b/Timeline/Controllers/PersonalTimelineController.cs
@@ -26,9 +26,9 @@ namespace Timeline.Controllers
}
[HttpGet("users/{username}/timeline")]
- public async Task<ActionResult<BaseTimelineInfo>> TimelineGet([FromRoute][Username] string username)
+ public async Task<ActionResult<TimelineInfo>> TimelineGet([FromRoute][Username] string username)
{
- return await _service.GetTimeline(username);
+ return (await _service.GetTimeline(username)).FillLinks(Url);
}
[HttpGet("users/{username}/timeline/posts")]
@@ -77,14 +77,14 @@ namespace Timeline.Controllers
[HttpPatch("users/{username}/timeline")]
[Authorize]
- public async Task<ActionResult<BaseTimelineInfo>> TimelinePatch([FromRoute][Username] string username, [FromBody] TimelinePatchRequest body)
+ public async Task<ActionResult<TimelineInfo>> TimelinePatch([FromRoute][Username] string username, [FromBody] TimelinePatchRequest body)
{
if (!this.IsAdministrator() && !(User.Identity.Name == username))
{
return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
}
await _service.ChangeProperty(username, body);
- var timeline = await _service.GetTimeline(username);
+ var timeline = (await _service.GetTimeline(username)).FillLinks(Url);
return Ok(timeline);
}
diff --git a/Timeline/Models/Http/TimelineCommon.cs b/Timeline/Models/Http/TimelineCommon.cs
index febb8186..0b2a714c 100644
--- a/Timeline/Models/Http/TimelineCommon.cs
+++ b/Timeline/Models/Http/TimelineCommon.cs
@@ -1,5 +1,7 @@
-using System;
+using Microsoft.AspNetCore.Mvc;
+using System;
using System.Collections.Generic;
+using Timeline.Controllers;
namespace Timeline.Models.Http
{
@@ -28,17 +30,41 @@ namespace Timeline.Models.Http
public DateTime LastUpdated { get; set; } = default!;
}
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "This is a DTO class.")]
- public class BaseTimelineInfo
+ public class TimelineInfo
{
+ public string? Name { get; set; }
public string Description { get; set; } = default!;
public UserInfo Owner { get; set; } = default!;
public TimelineVisibility Visibility { get; set; }
+#pragma warning disable CA2227 // Collection properties should be read only
public List<UserInfo> Members { get; set; } = default!;
+#pragma warning restore CA2227 // Collection properties should be read only
+
+#pragma warning disable CA1707 // Identifiers should not contain underscores
+ public TimelineInfoLinks? _links { get; set; }
+#pragma warning restore CA1707 // Identifiers should not contain underscores
+ }
+
+ public class TimelineInfoLinks
+ {
+ public string Posts { get; set; } = default!;
}
- public class TimelineInfo : BaseTimelineInfo
+ public static class TimelineInfoExtensions
{
- public string Name { get; set; } = default!;
+ public static TimelineInfo FillLinks(this TimelineInfo info, IUrlHelper urlHelper)
+ {
+ if (info == null)
+ throw new ArgumentNullException(nameof(info));
+ if (urlHelper == null)
+ throw new ArgumentNullException(nameof(urlHelper));
+
+ info._links = new TimelineInfoLinks
+ {
+ Posts = urlHelper.ActionLink(nameof(PersonalTimelineController.PostListGet), nameof(PersonalTimelineController)[0..^nameof(Controller).Length], new { info.Owner.Username })
+ };
+
+ return info;
+ }
}
}
diff --git a/Timeline/Services/TimelineService.cs b/Timeline/Services/TimelineService.cs
index 0ea68265..a16237ca 100644
--- a/Timeline/Services/TimelineService.cs
+++ b/Timeline/Services/TimelineService.cs
@@ -25,6 +25,37 @@ namespace Timeline.Services
public interface IBaseTimelineService
{
/// <summary>
+ /// Get the timeline info.
+ /// </summary>
+ /// <param name="name">Username or the timeline name. See remarks of <see cref="IBaseTimelineService"/>.</param>
+ /// <returns>The timeline info.</returns>
+ /// <exception cref="ArgumentNullException">Thrown when <paramref name="name"/> is null.</exception>
+ /// <exception cref="ArgumentException">Thrown when <paramref name="name"/> is illegal. It is not a valid timeline name (for normal timeline service) or a valid username (for personal timeline service).</exception>
+ /// <exception cref="TimelineNotExistException">
+ /// Thrown when timeline does not exist.
+ /// For normal timeline, it means the name does not exist.
+ /// For personal timeline, it means the user of that username does not exist
+ /// and the inner exception should be a <see cref="UserNotExistException"/>.
+ /// </exception>
+ Task<TimelineInfo> GetTimeline(string name);
+
+ /// <summary>
+ /// Set the properties of a timeline.
+ /// </summary>
+ /// <param name="name">Username or the timeline name. See remarks of <see cref="IBaseTimelineService"/>.</param>
+ /// <param name="newProperties">The new properties. Null member means not to change.</param>
+ /// <returns>The timeline info.</returns>
+ /// <exception cref="ArgumentNullException">Thrown when <paramref name="name"/> is null.</exception>
+ /// <exception cref="ArgumentException">Thrown when <paramref name="name"/> is illegal. It is not a valid timeline name (for normal timeline service) or a valid username (for personal timeline service).</exception>
+ /// <exception cref="TimelineNotExistException">
+ /// Thrown when timeline does not exist.
+ /// For normal timeline, it means the name does not exist.
+ /// For personal timeline, it means the user of that username does not exist
+ /// and the inner exception should be a <see cref="UserNotExistException"/>.
+ /// </exception>
+ Task ChangeProperty(string name, TimelinePatchRequest newProperties);
+
+ /// <summary>
/// Get all the posts in the timeline.
/// </summary>
/// <param name="name">Username or the timeline name. See remarks of <see cref="IBaseTimelineService"/>.</param>
@@ -178,20 +209,6 @@ namespace Timeline.Services
public interface ITimelineService : IBaseTimelineService
{
/// <summary>
- /// Get the timeline info.
- /// </summary>
- /// <param name="name">The name of the timeline.</param>
- /// <returns>The timeline info.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="name"/> is null.</exception>
- /// <exception cref="ArgumentException">
- /// Thrown when timeline name is invalid. Currently it means it is an empty string.
- /// </exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with the name does not exist.
- /// </exception>
- Task<TimelineInfo> GetTimeline(string name);
-
- /// <summary>
/// Create a timeline.
/// </summary>
/// <param name="name">The name of the timeline.</param>
@@ -205,37 +222,6 @@ namespace Timeline.Services
public interface IPersonalTimelineService : IBaseTimelineService
{
- /// <summary>
- /// Get the timeline info.
- /// </summary>
- /// <param name="username">The username of the owner of the personal timeline.</param>
- /// <returns>The timeline info.</returns>
- /// <exception cref="ArgumentNullException">
- /// Thrown when <paramref name="username"/> is null.
- /// </exception>
- /// <exception cref="ArgumentException">
- /// Thrown when <paramref name="username"/> is of bad format.
- /// </exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when the user does not exist. Inner exception MUST be <see cref="UserNotExistException"/>.
- /// </exception>
- Task<BaseTimelineInfo> GetTimeline(string username);
-
- /// <summary>
- /// Set the properties of a timeline.
- /// </summary>
- /// <param name="name">Username or the timeline name. See remarks of <see cref="IBaseTimelineService"/>.</param>
- /// <param name="newProperties">The new properties. Null member means not to change.</param>
- /// <exception cref="ArgumentNullException">
- /// Thrown when <paramref name="username"/> is null.
- /// </exception>
- /// <exception cref="ArgumentException">
- /// Thrown when <paramref name="username"/> is of bad format.
- /// </exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when the user does not exist. Inner exception MUST be <see cref="UserNotExistException"/>.
- /// </exception>
- Task ChangeProperty(string name, TimelinePatchRequest newProperties);
}
@@ -283,6 +269,34 @@ namespace Timeline.Services
/// </remarks>
protected abstract Task<long> FindTimelineId(string name);
+ public async Task<TimelineInfo> GetTimeline(string name)
+ {
+ if (name == null)
+ throw new ArgumentNullException(nameof(name));
+
+ var timelineId = await FindTimelineId(name);
+
+ var timelineEntity = await Database.Timelines.Where(t => t.Id == timelineId).SingleAsync();
+
+ var timelineMemberEntities = await Database.TimelineMembers.Where(m => m.TimelineId == timelineId).Select(m => new { m.UserId }).ToListAsync();
+
+ var owner = Mapper.Map<UserInfo>(await UserService.GetUserById(timelineEntity.OwnerId));
+
+ var members = new List<UserInfo>();
+ foreach (var memberEntity in timelineMemberEntities)
+ {
+ members.Add(Mapper.Map<UserInfo>(await UserService.GetUserById(memberEntity.UserId)));
+ }
+
+ return new TimelineInfo
+ {
+ Description = timelineEntity.Description ?? "",
+ Owner = owner,
+ Visibility = timelineEntity.Visibility,
+ Members = members
+ };
+ }
+
public async Task<List<TimelinePostInfo>> GetPosts(string name)
{
if (name == null)
@@ -569,34 +583,5 @@ namespace Timeline.Services
return newTimelineEntity.Id;
}
}
-
- public async Task<BaseTimelineInfo> GetTimeline(string username)
- {
- if (username == null)
- throw new ArgumentNullException(nameof(username));
-
- var timelineId = await FindTimelineId(username);
-
- var timelineEntity = await Database.Timelines.Where(t => t.Id == timelineId).SingleAsync();
-
- var timelineMemberEntities = await Database.TimelineMembers.Where(m => m.TimelineId == timelineId).Select(m => new { m.UserId }).ToListAsync();
-
- var owner = Mapper.Map<UserInfo>(await UserService.GetUserById(timelineEntity.OwnerId));
-
- var members = new List<UserInfo>();
- foreach (var memberEntity in timelineMemberEntities)
- {
- members.Add(Mapper.Map<UserInfo>(await UserService.GetUserById(memberEntity.UserId)));
- }
-
- return new BaseTimelineInfo
- {
- Description = timelineEntity.Description ?? "",
- Owner = owner,
- Visibility = timelineEntity.Visibility,
- Members = members
- };
- }
-
}
}