diff options
author | crupest <crupest@outlook.com> | 2020-02-04 18:02:39 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2020-02-04 18:02:39 +0800 |
commit | 03c9d4edc4e321db8c1b70ae621f77fa89664dfc (patch) | |
tree | 458d802973d4768e4bd5d463540608404b86c309 | |
parent | 7cf188134086c5d878cd3230345e89fc10b7e2ca (diff) | |
download | timeline-03c9d4edc4e321db8c1b70ae621f77fa89664dfc.tar.gz timeline-03c9d4edc4e321db8c1b70ae621f77fa89664dfc.tar.bz2 timeline-03c9d4edc4e321db8c1b70ae621f77fa89664dfc.zip |
Add get timeline list feature.
-rw-r--r-- | Timeline.ErrorCodes/ErrorCodes.cs | 5 | ||||
-rw-r--r-- | Timeline.Tests/IntegratedTests/TimelineTest.cs | 77 | ||||
-rw-r--r-- | Timeline/Controllers/PersonalTimelineController.cs | 4 | ||||
-rw-r--r-- | Timeline/Controllers/TimelineController.cs | 31 | ||||
-rw-r--r-- | Timeline/Models/Http/ErrorResponse.cs | 15 | ||||
-rw-r--r-- | Timeline/Models/Http/TimelineCommon.cs | 35 | ||||
-rw-r--r-- | Timeline/Resources/Messages.Designer.cs | 9 | ||||
-rw-r--r-- | Timeline/Resources/Messages.resx | 3 | ||||
-rw-r--r-- | Timeline/Services/TimelineService.cs | 48 |
9 files changed, 201 insertions, 26 deletions
diff --git a/Timeline.ErrorCodes/ErrorCodes.cs b/Timeline.ErrorCodes/ErrorCodes.cs index e07fbd94..eca0e18b 100644 --- a/Timeline.ErrorCodes/ErrorCodes.cs +++ b/Timeline.ErrorCodes/ErrorCodes.cs @@ -62,6 +62,11 @@ public const int NotExist = 1_104_02_01;
public const int MemberPut_NotExist = 1_104_03_01;
}
+
+ public static class TimelineController
+ {
+ public const int QueryRelateNotExist = 1_105_01_01;
+ }
}
}
diff --git a/Timeline.Tests/IntegratedTests/TimelineTest.cs b/Timeline.Tests/IntegratedTests/TimelineTest.cs index 58066d71..253554df 100644 --- a/Timeline.Tests/IntegratedTests/TimelineTest.cs +++ b/Timeline.Tests/IntegratedTests/TimelineTest.cs @@ -36,6 +36,83 @@ namespace Timeline.Tests.IntegratedTests }
[Fact]
+ public async Task TimelineList()
+ {
+ await CreateTestTimelines();
+
+ TimelineInfo user1Timeline;
+
+ var client = await CreateDefaultClient();
+
+ {
+ var res = await client.GetAsync("/users/user1/timeline");
+ user1Timeline = res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelineInfo>().Which;
+ }
+
+ {
+ var testResult = new List<TimelineInfo>();
+ testResult.Add(user1Timeline);
+ testResult.AddRange(_testTimelines);
+
+ var res = await client.GetAsync("/timelines");
+ res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<List<TimelineInfo>>()
+ .Which.Should().BeEquivalentTo(testResult);
+ }
+ }
+
+ [Fact]
+ public async Task TimelineList_WithRelate()
+ {
+ await CreateTestTimelines();
+
+ var testResult = new List<TimelineInfo>();
+
+ {
+ var client = await CreateClientAsUser();
+
+ {
+ var res = await client.PutAsync("/users/user1/timeline/members/user2", null);
+ res.Should().HaveStatusCode(200);
+ }
+
+ {
+ var res = await client.PutAsync("/timelines/t1/members/user2", null);
+ res.Should().HaveStatusCode(200);
+ }
+
+ {
+ var res = await client.GetAsync("/users/user1/timeline");
+ testResult.Add(res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelineInfo>().Which);
+ }
+
+ {
+ var res = await client.GetAsync("/timelines/t1");
+ testResult.Add(res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelineInfo>().Which);
+ }
+
+ {
+ var res = await client.GetAsync("/users/user2/timeline");
+ testResult.Add(res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelineInfo>().Which);
+ }
+ }
+
+ testResult.Add(_testTimelines[2]);
+
+ {
+ var client = await CreateClientAs(2);
+ var res = await client.GetAsync("/timelines?relate=user2");
+ res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<List<TimelineInfo>>()
+ .Which.Should().BeEquivalentTo(testResult);
+ }
+ }
+
+ [Fact]
public async Task TimelineCreate_Should_Work()
{
{
diff --git a/Timeline/Controllers/PersonalTimelineController.cs b/Timeline/Controllers/PersonalTimelineController.cs index b6c213d9..cef04a97 100644 --- a/Timeline/Controllers/PersonalTimelineController.cs +++ b/Timeline/Controllers/PersonalTimelineController.cs @@ -28,7 +28,7 @@ namespace Timeline.Controllers [HttpGet("users/{username}/timeline")]
public async Task<ActionResult<TimelineInfo>> TimelineGet([FromRoute][Username] string username)
{
- return (await _service.GetTimeline(username)).FillLinksForPersonalTimeline(Url);
+ return (await _service.GetTimeline(username)).FillLinks(Url);
}
[HttpGet("users/{username}/timeline/posts")]
@@ -84,7 +84,7 @@ namespace Timeline.Controllers return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
}
await _service.ChangeProperty(username, body);
- var timeline = (await _service.GetTimeline(username)).FillLinksForPersonalTimeline(Url);
+ var timeline = (await _service.GetTimeline(username)).FillLinks(Url);
return Ok(timeline);
}
diff --git a/Timeline/Controllers/TimelineController.cs b/Timeline/Controllers/TimelineController.cs index a514ccd9..811b0426 100644 --- a/Timeline/Controllers/TimelineController.cs +++ b/Timeline/Controllers/TimelineController.cs @@ -17,18 +17,41 @@ namespace Timeline.Controllers {
private readonly ILogger<TimelineController> _logger;
+ private readonly IUserService _userService;
private readonly ITimelineService _service;
- public TimelineController(ILogger<TimelineController> logger, ITimelineService service)
+ public TimelineController(ILogger<TimelineController> logger, IUserService userService, ITimelineService service)
{
_logger = logger;
+ _userService = userService;
_service = service;
}
+ [HttpGet("timelines")]
+ public async Task<ActionResult<List<TimelineInfo>>> TimelineList([FromQuery][Username] string? relate)
+ {
+ long? relatedUserId = null;
+ if (relate != null)
+ {
+ try
+ {
+ relatedUserId = await _userService.GetUserIdByUsername(relate);
+ }
+ catch (UserNotExistException)
+ {
+ return BadRequest(ErrorResponse.TimelineController.QueryRelateNotExist());
+ }
+ }
+
+ var result = await _service.GetTimelines(relatedUserId);
+ result.ForEach(t => t.FillLinks(Url));
+ return Ok(result);
+ }
+
[HttpGet("timelines/{name}")]
public async Task<ActionResult<TimelineInfo>> TimelineGet([FromRoute][TimelineName] string name)
{
- var result = (await _service.GetTimeline(name)).FillLinksForNormalTimeline(Url);
+ var result = (await _service.GetTimeline(name)).FillLinks(Url);
return Ok(result);
}
@@ -85,7 +108,7 @@ namespace Timeline.Controllers return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
}
await _service.ChangeProperty(name, body);
- var timeline = (await _service.GetTimeline(name)).FillLinksForNormalTimeline(Url);
+ var timeline = (await _service.GetTimeline(name)).FillLinks(Url);
return Ok(timeline);
}
@@ -137,7 +160,7 @@ namespace Timeline.Controllers try
{
- var timelineInfo = (await _service.CreateTimeline(body.Name, userId)).FillLinksForNormalTimeline(Url);
+ var timelineInfo = (await _service.CreateTimeline(body.Name, userId)).FillLinks(Url);
return Ok(timelineInfo);
}
catch (ConflictException)
diff --git a/Timeline/Models/Http/ErrorResponse.cs b/Timeline/Models/Http/ErrorResponse.cs index 0d23fe59..9f7e70e1 100644 --- a/Timeline/Models/Http/ErrorResponse.cs +++ b/Timeline/Models/Http/ErrorResponse.cs @@ -277,6 +277,21 @@ namespace Timeline.Models.Http }
+ public static class TimelineController
+ {
+
+ public static CommonResponse QueryRelateNotExist(params object?[] formatArgs)
+ {
+ return new CommonResponse(ErrorCodes.TimelineController.QueryRelateNotExist, string.Format(TimelineController_QueryRelateNotExist, formatArgs));
+ }
+
+ public static CommonResponse CustomMessage_QueryRelateNotExist(string message, params object?[] formatArgs)
+ {
+ return new CommonResponse(ErrorCodes.TimelineController.QueryRelateNotExist, string.Format(message, formatArgs));
+ }
+
+ }
+
}
}
diff --git a/Timeline/Models/Http/TimelineCommon.cs b/Timeline/Models/Http/TimelineCommon.cs index 6b857862..d0dfd837 100644 --- a/Timeline/Models/Http/TimelineCommon.cs +++ b/Timeline/Models/Http/TimelineCommon.cs @@ -53,34 +53,29 @@ namespace Timeline.Models.Http public static class TimelineInfoExtensions
{
- public static TimelineInfo FillLinksForPersonalTimeline(this TimelineInfo info, IUrlHelper urlHelper)
+ 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
+ if (string.IsNullOrEmpty(info.Name))
{
- Self = urlHelper.ActionLink(nameof(PersonalTimelineController.TimelineGet), nameof(PersonalTimelineController)[0..^nameof(Controller).Length], new { info.Owner.Username }),
- Posts = urlHelper.ActionLink(nameof(PersonalTimelineController.PostListGet), nameof(PersonalTimelineController)[0..^nameof(Controller).Length], new { info.Owner.Username })
- };
-
- return info;
- }
-
- public static TimelineInfo FillLinksForNormalTimeline(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
+ info._links = new TimelineInfoLinks
+ {
+ Self = urlHelper.ActionLink(nameof(PersonalTimelineController.TimelineGet), nameof(PersonalTimelineController)[0..^nameof(Controller).Length], new { info.Owner.Username }),
+ Posts = urlHelper.ActionLink(nameof(PersonalTimelineController.PostListGet), nameof(PersonalTimelineController)[0..^nameof(Controller).Length], new { info.Owner.Username })
+ };
+ }
+ else
{
- Self = urlHelper.ActionLink(nameof(TimelineController.TimelineGet), nameof(TimelineController)[0..^nameof(Controller).Length], new { info.Name }),
- Posts = urlHelper.ActionLink(nameof(TimelineController.PostListGet), nameof(TimelineController)[0..^nameof(Controller).Length], new { info.Name })
- };
+ info._links = new TimelineInfoLinks
+ {
+ Self = urlHelper.ActionLink(nameof(TimelineController.TimelineGet), nameof(TimelineController)[0..^nameof(Controller).Length], new { info.Name }),
+ Posts = urlHelper.ActionLink(nameof(TimelineController.PostListGet), nameof(TimelineController)[0..^nameof(Controller).Length], new { info.Name })
+ };
+ }
return info;
}
diff --git a/Timeline/Resources/Messages.Designer.cs b/Timeline/Resources/Messages.Designer.cs index eeb44f10..727df046 100644 --- a/Timeline/Resources/Messages.Designer.cs +++ b/Timeline/Resources/Messages.Designer.cs @@ -178,6 +178,15 @@ namespace Timeline.Resources { }
/// <summary>
+ /// Looks up a localized string similar to The user specified by query param "relate" does not exist..
+ /// </summary>
+ internal static string TimelineController_QueryRelateNotExist {
+ get {
+ return ResourceManager.GetString("TimelineController_QueryRelateNotExist", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to Username or password is invalid..
/// </summary>
internal static string TokenController_Create_BadCredential {
diff --git a/Timeline/Resources/Messages.resx b/Timeline/Resources/Messages.resx index 66a84d5f..c42657d3 100644 --- a/Timeline/Resources/Messages.resx +++ b/Timeline/Resources/Messages.resx @@ -156,6 +156,9 @@ <data name="TimelineCommon_NotExist" xml:space="preserve">
<value>The timeline with given name does not exist.</value>
</data>
+ <data name="TimelineController_QueryRelateNotExist" xml:space="preserve">
+ <value>The user specified by query param "relate" does not exist.</value>
+ </data>
<data name="TokenController_Create_BadCredential" xml:space="preserve">
<value>Username or password is invalid.</value>
</data>
diff --git a/Timeline/Services/TimelineService.cs b/Timeline/Services/TimelineService.cs index 6f9d437e..67a57deb 100644 --- a/Timeline/Services/TimelineService.cs +++ b/Timeline/Services/TimelineService.cs @@ -229,6 +229,16 @@ namespace Timeline.Services public interface ITimelineService : IBaseTimelineService
{
/// <summary>
+ /// Get all timelines including personal timelines.
+ /// </summary>
+ /// <param name="relatedUserId">Filter timelines related (own or is a member) to specific user.</param>
+ /// <returns>The list of timelines.</returns>
+ /// <remarks>
+ /// If user with related user id does not exist, empty list will be returned.
+ /// </remarks>
+ Task<List<TimelineInfo>> GetTimelines(long? relatedUserId = null);
+
+ /// <summary>
/// Create a timeline.
/// </summary>
/// <param name="name">The name of the timeline.</param>
@@ -618,6 +628,44 @@ namespace Timeline.Services }
}
+ public async Task<List<TimelineInfo>> GetTimelines(long? relatedUserId = null)
+ {
+ List<TimelineEntity> entities;
+
+ if (relatedUserId == null)
+ {
+ entities = await Database.Timelines.Include(t => t.Members).ToListAsync();
+ }
+ else
+ {
+ var timelineAsMemberIds = await Database.TimelineMembers.Where(m => m.UserId == relatedUserId).Select(m => m.TimelineId).ToListAsync();
+ entities = await Database.Timelines.Where(t => t.OwnerId == relatedUserId || timelineAsMemberIds.Contains(t.Id)).Include(t => t.Members).ToListAsync();
+ }
+
+ var result = new List<TimelineInfo>();
+
+ foreach (var entity in entities)
+ {
+ var timeline = new TimelineInfo
+ {
+ Name = entity.Name,
+ Description = entity.Description ?? "",
+ Owner = Mapper.Map<UserInfo>(await UserService.GetUserById(entity.OwnerId)),
+ Visibility = entity.Visibility,
+ Members = new List<UserInfo>()
+ };
+
+ foreach (var m in entity.Members)
+ {
+ timeline.Members.Add(Mapper.Map<UserInfo>(await UserService.GetUserById(m.UserId)));
+ }
+
+ result.Add(timeline);
+ }
+
+ return result;
+ }
+
public async Task<TimelineInfo> CreateTimeline(string name, long owner)
{
if (name == null)
|