diff options
-rw-r--r-- | Timeline.Tests/IntegratedTests/TimelineTest.cs | 178 | ||||
-rw-r--r-- | Timeline/Controllers/TimelineController.cs | 34 | ||||
-rw-r--r-- | Timeline/Resources/Messages.Designer.cs | 9 | ||||
-rw-r--r-- | Timeline/Resources/Messages.resx | 3 | ||||
-rw-r--r-- | Timeline/Services/TimelineService.cs | 22 | ||||
-rw-r--r-- | 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<TimelineInfo>();
var testResultOwn = new List<TimelineInfo>();
var testResultJoin = new List<TimelineInfo>();
- var testResultAll = new List<TimelineInfo>();
+ var testResultOwnPrivate = new List<TimelineInfo>();
+ var testResultRelatePublic = new List<TimelineInfo>();
+ var testResultRelateRegister = new List<TimelineInfo>();
+ var testResultJoinPrivate = new List<TimelineInfo>();
+ var testResultPublic = new List<TimelineInfo>();
{
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<TimelineInfo>().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<TimelineInfo>().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<List<TimelineInfo>>()
- .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<TimelineInfo>().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<TimelineInfo>().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<List<TimelineInfo>>()
- .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<TimelineInfo>().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<TimelineInfo>().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<List<TimelineInfo>>()
- .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<List<TimelineInfo>>()
+ .Which;
+ body.Should().BeEquivalentTo(testResultRelate);
+ }
+
+ {
+ var res = await client.GetAsync("/timelines?relate=user3&relateType=own");
+ var body = res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<List<TimelineInfo>>()
+ .Which;
+ body.Should().BeEquivalentTo(testResultOwn);
+ }
+
+ {
+ var res = await client.GetAsync("/timelines?relate=user3&visibility=public");
+ var body = res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<List<TimelineInfo>>()
+ .Which;
+ body.Should().BeEquivalentTo(testResultRelatePublic);
+ }
+
+ {
+ var res = await client.GetAsync("/timelines?relate=user3&visibility=register");
+ var body = res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<List<TimelineInfo>>()
+ .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<List<TimelineInfo>>()
+ .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<List<TimelineInfo>>()
+ .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<List<TimelineInfo>>()
+ .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<ActionResult<List<TimelineInfo>>> TimelineList([FromQuery][Username] string? relate, [FromQuery][RegularExpression("(own)|(join)")] string? relateType)
+ public async Task<ActionResult<List<TimelineInfo>>> TimelineList([FromQuery][Username] string? relate, [FromQuery][RegularExpression("(own)|(join)")] string? relateType, [FromQuery] string? visibility)
{
+ List<TimelineVisibility>? visibilityFilter = null;
+ if (visibility != null)
+ {
+ visibilityFilter = new List<TimelineVisibility>();
+ 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 @@ -187,6 +187,15 @@ namespace Timeline.Resources { }
/// <summary>
+ /// Looks up a localized string similar to '{0}' is an unkown visibility in the query parameter 'visibility'. .
+ /// </summary>
+ internal static string TimelineController_QueryVisibilityUnknown {
+ get {
+ return ResourceManager.GetString("TimelineController_QueryVisibilityUnknown", 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 c42657d3..865db524 100644 --- a/Timeline/Resources/Messages.resx +++ b/Timeline/Resources/Messages.resx @@ -159,6 +159,9 @@ <data name="TimelineController_QueryRelateNotExist" xml:space="preserve">
<value>The user specified by query param "relate" does not exist.</value>
</data>
+ <data name="TimelineController_QueryVisibilityUnknown" xml:space="preserve">
+ <value>'{0}' is an unkown visibility in the query parameter 'visibility'. </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 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.
/// </summary>
/// <param name="relate">Filter timelines related (own or is a member) to specific user.</param>
+ /// <param name="visibility">Filter timelines with given visibility. If null or empty, all visibilities are returned. Duplicate value are ignored.</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(TimelineUserRelationship? relate = null);
+ Task<List<TimelineInfo>> GetTimelines(TimelineUserRelationship? relate = null, List<TimelineVisibility>? visibility = null);
/// <summary>
/// Create a timeline.
@@ -647,13 +648,24 @@ namespace Timeline.Services }
}
- public async Task<List<TimelineInfo>> GetTimelines(TimelineUserRelationship? relate = null)
+ public async Task<List<TimelineInfo>> GetTimelines(TimelineUserRelationship? relate = null, List<TimelineVisibility>? visibility = null)
{
List<TimelineEntity> entities;
+ IQueryable<TimelineEntity> ApplyTimelineVisibilityFilter(IQueryable<TimelineEntity> 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 /// <param name="expireAt">The expire time of the token.</param>
/// <returns>The created token and the user info.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="username"/> or <paramref name="password"/> is null.</exception>
- /// <exception cref="UsernameBadFormatException">Thrown when <paramref name="username"/> is of bad format.</exception>
+ /// <exception cref="ArgumentException">Thrown when <paramref name="username"/> is of bad format.</exception>
/// <exception cref="UserNotExistException">Thrown when the user with <paramref name="username"/> does not exist.</exception>
/// <exception cref="BadPasswordException">Thrown when <paramref name="password"/> is wrong.</exception>
public Task<UserTokenCreateResult> CreateToken(string username, string password, DateTime? expireAt = null);
|