From 3cbb2e7ef4bb2ccbbaadd18e49e2de392d6db2e1 Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 4 Feb 2020 18:02:39 +0800 Subject: Add get timeline list feature. --- Timeline.ErrorCodes/ErrorCodes.cs | 5 ++ Timeline.Tests/IntegratedTests/TimelineTest.cs | 77 ++++++++++++++++++++++ Timeline/Controllers/PersonalTimelineController.cs | 4 +- Timeline/Controllers/TimelineController.cs | 31 +++++++-- Timeline/Models/Http/ErrorResponse.cs | 15 +++++ Timeline/Models/Http/TimelineCommon.cs | 35 +++++----- Timeline/Resources/Messages.Designer.cs | 9 +++ Timeline/Resources/Messages.resx | 3 + 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 @@ -35,6 +35,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().Which; + } + + { + var testResult = new List(); + testResult.Add(user1Timeline); + testResult.AddRange(_testTimelines); + + var res = await client.GetAsync("/timelines"); + res.Should().HaveStatusCode(200) + .And.HaveJsonBody>() + .Which.Should().BeEquivalentTo(testResult); + } + } + + [Fact] + public async Task TimelineList_WithRelate() + { + await CreateTestTimelines(); + + var testResult = new List(); + + { + 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().Which); + } + + { + var res = await client.GetAsync("/timelines/t1"); + testResult.Add(res.Should().HaveStatusCode(200) + .And.HaveJsonBody().Which); + } + + { + var res = await client.GetAsync("/users/user2/timeline"); + testResult.Add(res.Should().HaveStatusCode(200) + .And.HaveJsonBody().Which); + } + } + + testResult.Add(_testTimelines[2]); + + { + var client = await CreateClientAs(2); + var res = await client.GetAsync("/timelines?relate=user2"); + res.Should().HaveStatusCode(200) + .And.HaveJsonBody>() + .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> 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 _logger; + private readonly IUserService _userService; private readonly ITimelineService _service; - public TimelineController(ILogger logger, ITimelineService service) + public TimelineController(ILogger logger, IUserService userService, ITimelineService service) { _logger = logger; + _userService = userService; _service = service; } + [HttpGet("timelines")] + public async Task>> 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> 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 @@ -177,6 +177,15 @@ namespace Timeline.Resources { } } + /// + /// Looks up a localized string similar to The user specified by query param "relate" does not exist.. + /// + internal static string TimelineController_QueryRelateNotExist { + get { + return ResourceManager.GetString("TimelineController_QueryRelateNotExist", resourceCulture); + } + } + /// /// Looks up a localized string similar to Username or password is invalid.. /// 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 @@ The timeline with given name does not exist. + + The user specified by query param "relate" does not exist. + Username or password is invalid. 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 @@ -228,6 +228,16 @@ namespace Timeline.Services /// public interface ITimelineService : IBaseTimelineService { + /// + /// Get all timelines including personal timelines. + /// + /// Filter timelines related (own or is a member) to specific user. + /// The list of timelines. + /// + /// If user with related user id does not exist, empty list will be returned. + /// + Task> GetTimelines(long? relatedUserId = null); + /// /// Create a timeline. /// @@ -618,6 +628,44 @@ namespace Timeline.Services } } + public async Task> GetTimelines(long? relatedUserId = null) + { + List 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(); + + foreach (var entity in entities) + { + var timeline = new TimelineInfo + { + Name = entity.Name, + Description = entity.Description ?? "", + Owner = Mapper.Map(await UserService.GetUserById(entity.OwnerId)), + Visibility = entity.Visibility, + Members = new List() + }; + + foreach (var m in entity.Members) + { + timeline.Members.Add(Mapper.Map(await UserService.GetUserById(m.UserId))); + } + + result.Add(timeline); + } + + return result; + } + public async Task CreateTimeline(string name, long owner) { if (name == null) -- cgit v1.2.3