From 98dabdd9462384353389a6834b9e71a3e40330a2 Mon Sep 17 00:00:00 2001 From: crupest Date: Fri, 21 Feb 2020 22:50:23 +0800 Subject: Add visibility query filter in get timelines api. --- Timeline.Tests/IntegratedTests/TimelineTest.cs | 178 +++++++++++++++++++++---- Timeline/Controllers/TimelineController.cs | 34 ++++- Timeline/Resources/Messages.Designer.cs | 9 ++ Timeline/Resources/Messages.resx | 3 + Timeline/Services/TimelineService.cs | 22 ++- Timeline/Services/UserTokenManager.cs | 3 +- 6 files changed, 217 insertions(+), 32 deletions(-) diff --git a/Timeline.Tests/IntegratedTests/TimelineTest.cs b/Timeline.Tests/IntegratedTests/TimelineTest.cs index 2500a0c3..51653b0a 100644 --- a/Timeline.Tests/IntegratedTests/TimelineTest.cs +++ b/Timeline.Tests/IntegratedTests/TimelineTest.cs @@ -63,24 +63,39 @@ namespace Timeline.Tests.IntegratedTests } [Fact] - public async Task TimelineList_WithRelate() + public async Task TimelineList_WithQuery() { await CreateTestTimelines(); + var testResultRelate = new List(); var testResultOwn = new List(); var testResultJoin = new List(); - var testResultAll = new List(); + var testResultOwnPrivate = new List(); + var testResultRelatePublic = new List(); + var testResultRelateRegister = new List(); + var testResultJoinPrivate = new List(); + var testResultPublic = new List(); { var client = await CreateClientAsUser(); { - var res = await client.PutAsync("/users/user1/timeline/members/user2", null); + var res = await client.PutAsync("/users/user1/timeline/members/user3", null); res.Should().HaveStatusCode(200); } { - var res = await client.PutAsync("/timelines/t1/members/user2", null); + var res = await client.PutAsync("/timelines/t1/members/user3", null); + res.Should().HaveStatusCode(200); + } + + { + var res = await client.PatchAsJsonAsync("/users/user1/timeline", new TimelinePatchRequest { Visibility = TimelineVisibility.Public }); + res.Should().HaveStatusCode(200); + } + + { + var res = await client.PatchAsJsonAsync("/timelines/t1", new TimelinePatchRequest { Visibility = TimelineVisibility.Register }); res.Should().HaveStatusCode(200); } @@ -88,44 +103,156 @@ namespace Timeline.Tests.IntegratedTests var res = await client.GetAsync("/users/user1/timeline"); var timeline = res.Should().HaveStatusCode(200) .And.HaveJsonBody().Which; - testResultAll.Add(timeline); + testResultRelate.Add(timeline); testResultJoin.Add(timeline); + testResultRelatePublic.Add(timeline); + testResultPublic.Add(timeline); } { var res = await client.GetAsync("/timelines/t1"); var timeline = res.Should().HaveStatusCode(200) .And.HaveJsonBody().Which; - testResultAll.Add(timeline); + testResultRelate.Add(timeline); testResultJoin.Add(timeline); + testResultRelateRegister.Add(timeline); } } - testResultAll.Add(_testTimelines[2]); - testResultOwn.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(testResultAll); + + { + var res = await client.PutAsync("/users/user2/timeline/members/user3", null); + res.Should().HaveStatusCode(200); + } + + { + var res = await client.PutAsync("/timelines/t2/members/user3", null); + res.Should().HaveStatusCode(200); + } + + { + var res = await client.PatchAsJsonAsync("/users/user2/timeline", new TimelinePatchRequest { Visibility = TimelineVisibility.Register }); + res.Should().HaveStatusCode(200); + } + + { + var res = await client.PatchAsJsonAsync("/timelines/t2", new TimelinePatchRequest { Visibility = TimelineVisibility.Private }); + res.Should().HaveStatusCode(200); + } + + { + var res = await client.GetAsync("/users/user2/timeline"); + var timeline = res.Should().HaveStatusCode(200) + .And.HaveJsonBody().Which; + testResultRelate.Add(timeline); + testResultJoin.Add(timeline); + testResultRelateRegister.Add(timeline); + } + + { + var res = await client.GetAsync("/timelines/t2"); + var timeline = res.Should().HaveStatusCode(200) + .And.HaveJsonBody().Which; + testResultRelate.Add(timeline); + testResultJoin.Add(timeline); + testResultJoinPrivate.Add(timeline); + } } { - var client = await CreateClientAs(2); - var res = await client.GetAsync("/timelines?relate=user2&relateType=own"); - res.Should().HaveStatusCode(200) - .And.HaveJsonBody>() - .Which.Should().BeEquivalentTo(testResultOwn); + var client = await CreateClientAs(3); + + { + var res = await client.PatchAsJsonAsync("/users/user3/timeline", new TimelinePatchRequest { Visibility = TimelineVisibility.Private }); + res.Should().HaveStatusCode(200); + } + + { + var res = await client.PatchAsJsonAsync("/timelines/t3", new TimelinePatchRequest { Visibility = TimelineVisibility.Register }); + res.Should().HaveStatusCode(200); + } + + { + var res = await client.GetAsync("/users/user3/timeline"); + var timeline = res.Should().HaveStatusCode(200) + .And.HaveJsonBody().Which; + testResultRelate.Add(timeline); + testResultOwn.Add(timeline); + testResultOwnPrivate.Add(timeline); + } + + { + var res = await client.GetAsync("/timelines/t3"); + var timeline = res.Should().HaveStatusCode(200) + .And.HaveJsonBody().Which; + testResultRelate.Add(timeline); + testResultOwn.Add(timeline); + testResultRelateRegister.Add(timeline); + } } { - var client = await CreateClientAs(2); - var res = await client.GetAsync("/timelines?relate=user2&relateType=join"); - res.Should().HaveStatusCode(200) - .And.HaveJsonBody>() - .Which.Should().BeEquivalentTo(testResultJoin); + var client = await CreateClientAs(3); + { + var res = await client.GetAsync("/timelines?relate=user3"); + var body = res.Should().HaveStatusCode(200) + .And.HaveJsonBody>() + .Which; + body.Should().BeEquivalentTo(testResultRelate); + } + + { + var res = await client.GetAsync("/timelines?relate=user3&relateType=own"); + var body = res.Should().HaveStatusCode(200) + .And.HaveJsonBody>() + .Which; + body.Should().BeEquivalentTo(testResultOwn); + } + + { + var res = await client.GetAsync("/timelines?relate=user3&visibility=public"); + var body = res.Should().HaveStatusCode(200) + .And.HaveJsonBody>() + .Which; + body.Should().BeEquivalentTo(testResultRelatePublic); + } + + { + var res = await client.GetAsync("/timelines?relate=user3&visibility=register"); + var body = res.Should().HaveStatusCode(200) + .And.HaveJsonBody>() + .Which; + body.Should().BeEquivalentTo(testResultRelateRegister); + } + + { + var res = await client.GetAsync("/timelines?relate=user3&relateType=join&visibility=private"); + var body = res.Should().HaveStatusCode(200) + .And.HaveJsonBody>() + .Which; + body.Should().BeEquivalentTo(testResultJoinPrivate); + } + + { + var res = await client.GetAsync("/timelines?relate=user3&relateType=own&visibility=private"); + var body = res.Should().HaveStatusCode(200) + .And.HaveJsonBody>() + .Which; + body.Should().BeEquivalentTo(testResultOwnPrivate); + } + } + + { + var client = await CreateDefaultClient(); + { + var res = await client.GetAsync("/timelines?visibility=public"); + var body = res.Should().HaveStatusCode(200) + .And.HaveJsonBody>() + .Which; + body.Should().BeEquivalentTo(testResultPublic); + } } } @@ -143,6 +270,11 @@ namespace Timeline.Tests.IntegratedTests var res = await client.GetAsync("/timelines?relateType=aaa"); res.Should().BeInvalidModel(); } + + { + var res = await client.GetAsync("/timelines?visibility=aaa"); + res.Should().BeInvalidModel(); + } } [Fact] diff --git a/Timeline/Controllers/TimelineController.cs b/Timeline/Controllers/TimelineController.cs index 47b3696c..9ada16e0 100644 --- a/Timeline/Controllers/TimelineController.cs +++ b/Timeline/Controllers/TimelineController.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; @@ -29,8 +30,37 @@ namespace Timeline.Controllers } [HttpGet("timelines")] - public async Task>> TimelineList([FromQuery][Username] string? relate, [FromQuery][RegularExpression("(own)|(join)")] string? relateType) + public async Task>> TimelineList([FromQuery][Username] string? relate, [FromQuery][RegularExpression("(own)|(join)")] string? relateType, [FromQuery] string? visibility) { + List? visibilityFilter = null; + if (visibility != null) + { + visibilityFilter = new List(); + var items = visibility.Split('|'); + foreach (var item in items) + { + if (item.Equals(nameof(TimelineVisibility.Private), StringComparison.OrdinalIgnoreCase)) + { + if (!visibilityFilter.Contains(TimelineVisibility.Private)) + visibilityFilter.Add(TimelineVisibility.Private); + } + else if (item.Equals(nameof(TimelineVisibility.Register), StringComparison.OrdinalIgnoreCase)) + { + if (!visibilityFilter.Contains(TimelineVisibility.Register)) + visibilityFilter.Add(TimelineVisibility.Register); + } + else if (item.Equals(nameof(TimelineVisibility.Public), StringComparison.OrdinalIgnoreCase)) + { + if (!visibilityFilter.Contains(TimelineVisibility.Public)) + visibilityFilter.Add(TimelineVisibility.Public); + } + else + { + return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_QueryVisibilityUnknown, item)); + } + } + } + TimelineUserRelationship? relationship = null; if (relate != null) { @@ -51,7 +81,7 @@ namespace Timeline.Controllers } } - var result = await _service.GetTimelines(relationship); + var result = await _service.GetTimelines(relationship, visibilityFilter); result.ForEach(t => t.FillLinks(Url)); return Ok(result); } diff --git a/Timeline/Resources/Messages.Designer.cs b/Timeline/Resources/Messages.Designer.cs index 727df046..4123cb8b 100644 --- a/Timeline/Resources/Messages.Designer.cs +++ b/Timeline/Resources/Messages.Designer.cs @@ -186,6 +186,15 @@ namespace Timeline.Resources { } } + /// + /// Looks up a localized string similar to '{0}' is an unkown visibility in the query parameter 'visibility'. . + /// + internal static string TimelineController_QueryVisibilityUnknown { + get { + return ResourceManager.GetString("TimelineController_QueryVisibilityUnknown", 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 c42657d3..865db524 100644 --- a/Timeline/Resources/Messages.resx +++ b/Timeline/Resources/Messages.resx @@ -159,6 +159,9 @@ The user specified by query param "relate" does not exist. + + '{0}' is an unkown visibility in the query parameter 'visibility'. + Username or password is invalid. diff --git a/Timeline/Services/TimelineService.cs b/Timeline/Services/TimelineService.cs index 7afc0512..76acc7d7 100644 --- a/Timeline/Services/TimelineService.cs +++ b/Timeline/Services/TimelineService.cs @@ -251,11 +251,12 @@ namespace Timeline.Services /// Get all timelines including personal timelines. /// /// Filter timelines related (own or is a member) to specific user. + /// Filter timelines with given visibility. If null or empty, all visibilities are returned. Duplicate value are ignored. /// The list of timelines. /// /// If user with related user id does not exist, empty list will be returned. /// - Task> GetTimelines(TimelineUserRelationship? relate = null); + Task> GetTimelines(TimelineUserRelationship? relate = null, List? visibility = null); /// /// Create a timeline. @@ -647,13 +648,24 @@ namespace Timeline.Services } } - public async Task> GetTimelines(TimelineUserRelationship? relate = null) + public async Task> GetTimelines(TimelineUserRelationship? relate = null, List? visibility = null) { List entities; + IQueryable ApplyTimelineVisibilityFilter(IQueryable query) + { + if (visibility != null && visibility.Count != 0) + { + return query.Where(t => visibility.Contains(t.Visibility)); + } + return query; + } + + bool allVisibilities = visibility == null || visibility.Count == 0; + if (relate == null) { - entities = await Database.Timelines.Include(t => t.Members).ToListAsync(); + entities = await ApplyTimelineVisibilityFilter(Database.Timelines).Include(t => t.Members).ToListAsync(); } else { @@ -661,12 +673,12 @@ namespace Timeline.Services if ((relate.Type & TimelineUserRelationshipType.Own) != 0) { - entities.AddRange(await Database.Timelines.Where(t => t.OwnerId == relate.UserId && t.Name != null).Include(t => t.Members).ToListAsync()); + entities.AddRange(await ApplyTimelineVisibilityFilter(Database.Timelines.Where(t => t.OwnerId == relate.UserId)).Include(t => t.Members).ToListAsync()); } if ((relate.Type & TimelineUserRelationshipType.Join) != 0) { - entities.AddRange(await Database.TimelineMembers.Where(m => m.UserId == relate.UserId).Include(m => m.Timeline).ThenInclude(t => t.Members).Select(m => m.Timeline).ToListAsync()); + entities.AddRange(await ApplyTimelineVisibilityFilter(Database.TimelineMembers.Where(m => m.UserId == relate.UserId).Include(m => m.Timeline).ThenInclude(t => t.Members).Select(m => m.Timeline)).ToListAsync()); } } diff --git a/Timeline/Services/UserTokenManager.cs b/Timeline/Services/UserTokenManager.cs index 3e9ef3d4..4e54c4cd 100644 --- a/Timeline/Services/UserTokenManager.cs +++ b/Timeline/Services/UserTokenManager.cs @@ -1,7 +1,6 @@ using Microsoft.Extensions.Logging; using System; using System.Threading.Tasks; -using Timeline.Models; namespace Timeline.Services { @@ -21,7 +20,7 @@ namespace Timeline.Services /// The expire time of the token. /// The created token and the user info. /// Thrown when or is null. - /// Thrown when is of bad format. + /// Thrown when is of bad format. /// Thrown when the user with does not exist. /// Thrown when is wrong. public Task CreateToken(string username, string password, DateTime? expireAt = null); -- cgit v1.2.3