diff options
-rw-r--r-- | Timeline.Tests/IntegratedTests/IntegratedTestBase.cs | 15 | ||||
-rw-r--r-- | Timeline.Tests/IntegratedTests/PersonalTimelineTest.cs | 8 | ||||
-rw-r--r-- | Timeline.Tests/IntegratedTests/TokenTest.cs | 4 | ||||
-rw-r--r-- | Timeline.Tests/IntegratedTests/UserTest.cs | 20 | ||||
-rw-r--r-- | Timeline/Controllers/TokenController.cs | 4 | ||||
-rw-r--r-- | Timeline/Controllers/UserController.cs | 22 | ||||
-rw-r--r-- | Timeline/Models/Http/TokenController.cs | 4 | ||||
-rw-r--r-- | Timeline/Models/Http/UserInfo.cs | 45 |
8 files changed, 54 insertions, 68 deletions
diff --git a/Timeline.Tests/IntegratedTests/IntegratedTestBase.cs b/Timeline.Tests/IntegratedTests/IntegratedTestBase.cs index 242e96cd..59af5eab 100644 --- a/Timeline.Tests/IntegratedTests/IntegratedTestBase.cs +++ b/Timeline.Tests/IntegratedTests/IntegratedTestBase.cs @@ -15,6 +15,12 @@ namespace Timeline.Tests.IntegratedTests public abstract class IntegratedTestBase : IClassFixture<WebApplicationFactory<Startup>>, IDisposable { + static IntegratedTestBase()
+ {
+ FluentAssertions.AssertionOptions.AssertEquivalencyUsing(options =>
+ options.Excluding(m => m.RuntimeType == typeof(UserInfo) && m.SelectedMemberPath == "_links"));
+ } + protected TestApplication TestApp { get; } protected WebApplicationFactory<Startup> Factory => TestApp.Factory; @@ -56,7 +62,6 @@ namespace Timeline.Tests.IntegratedTests }
var userInfoList = new List<UserInfo>();
- var userInfoForAdminList = new List<UserInfoForAdmin>();
var userService = scope.ServiceProvider.GetRequiredService<IUserService>();
var mapper = scope.ServiceProvider.GetRequiredService<IMapper>();
@@ -65,11 +70,9 @@ namespace Timeline.Tests.IntegratedTests {
userService.CreateUser(user).Wait();
userInfoList.Add(mapper.Map<UserInfo>(user));
- userInfoForAdminList.Add(mapper.Map<UserInfoForAdmin>(user));
}
- UserInfoList = userInfoList;
- UserInfoForAdminList = userInfoForAdminList;
+ UserInfos = userInfoList;
}
} @@ -83,9 +86,7 @@ namespace Timeline.Tests.IntegratedTests TestApp.Dispose(); } - public IReadOnlyList<UserInfo> UserInfoList { get; } - - public IReadOnlyList<UserInfoForAdmin> UserInfoForAdminList { get; } + public IReadOnlyList<UserInfo> UserInfos { get; } public Task<HttpClient> CreateDefaultClient() { diff --git a/Timeline.Tests/IntegratedTests/PersonalTimelineTest.cs b/Timeline.Tests/IntegratedTests/PersonalTimelineTest.cs index dacfea62..f3d6b172 100644 --- a/Timeline.Tests/IntegratedTests/PersonalTimelineTest.cs +++ b/Timeline.Tests/IntegratedTests/PersonalTimelineTest.cs @@ -26,7 +26,7 @@ namespace Timeline.Tests.IntegratedTests var res = await client.GetAsync("users/user1/timeline"); var body = res.Should().HaveStatusCode(200) .And.HaveJsonBody<BaseTimelineInfo>().Which; - body.Owner.Should().BeEquivalentTo(UserInfoList[1]); + body.Owner.Should().BeEquivalentTo(UserInfos[1]); body.Visibility.Should().Be(TimelineVisibility.Register); body.Description.Should().Be(""); body.Members.Should().NotBeNull().And.BeEmpty(); @@ -169,7 +169,7 @@ namespace Timeline.Tests.IntegratedTests var res = await client.PutAsync("/users/user1/timeline/members/user2", null); res.Should().HaveStatusCode(200); } - await AssertMembers(new List<UserInfo> { UserInfoList[2] }); + await AssertMembers(new List<UserInfo> { UserInfos[2] }); { var res = await client.DeleteAsync("/users/user1/timeline/members/user2"); res.Should().BeDelete(true); @@ -452,7 +452,7 @@ namespace Timeline.Tests.IntegratedTests .Which; body.Should().NotBeNull(); body.Content.Should().Be(mockContent); - body.Author.Should().BeEquivalentTo(UserInfoList[1]); + body.Author.Should().BeEquivalentTo(UserInfos[1]); createRes = body; } { @@ -472,7 +472,7 @@ namespace Timeline.Tests.IntegratedTests .Which; body.Should().NotBeNull();
body.Content.Should().Be(mockContent2); - body.Author.Should().BeEquivalentTo(UserInfoList[1]); + body.Author.Should().BeEquivalentTo(UserInfos[1]); body.Time.Should().BeCloseTo(mockTime2, 1000); createRes2 = body; } diff --git a/Timeline.Tests/IntegratedTests/TokenTest.cs b/Timeline.Tests/IntegratedTests/TokenTest.cs index ec7514ff..928d546c 100644 --- a/Timeline.Tests/IntegratedTests/TokenTest.cs +++ b/Timeline.Tests/IntegratedTests/TokenTest.cs @@ -77,7 +77,7 @@ namespace Timeline.Tests.IntegratedTests var body = response.Should().HaveStatusCode(200)
.And.HaveJsonBody<CreateTokenResponse>().Which;
body.Token.Should().NotBeNullOrWhiteSpace();
- body.User.Should().BeEquivalentTo(UserInfoForAdminList[1]);
+ body.User.Should().BeEquivalentTo(UserInfos[1]);
}
[Fact]
@@ -165,7 +165,7 @@ namespace Timeline.Tests.IntegratedTests new VerifyTokenRequest { Token = createTokenResult.Token });
response.Should().HaveStatusCode(200)
.And.HaveJsonBody<VerifyTokenResponse>()
- .Which.User.Should().BeEquivalentTo(UserInfoForAdminList[1]);
+ .Which.User.Should().BeEquivalentTo(UserInfos[1]);
}
}
}
diff --git a/Timeline.Tests/IntegratedTests/UserTest.cs b/Timeline.Tests/IntegratedTests/UserTest.cs index 1b9733ff..bbeb6ad6 100644 --- a/Timeline.Tests/IntegratedTests/UserTest.cs +++ b/Timeline.Tests/IntegratedTests/UserTest.cs @@ -25,7 +25,7 @@ namespace Timeline.Tests.IntegratedTests var res = await client.GetAsync("/users");
res.Should().HaveStatusCode(200)
.And.HaveJsonBody<UserInfo[]>()
- .Which.Should().BeEquivalentTo(UserInfoList);
+ .Which.Should().BeEquivalentTo(UserInfos);
}
[Fact]
@@ -35,7 +35,7 @@ namespace Timeline.Tests.IntegratedTests var res = await client.GetAsync("/users");
res.Should().HaveStatusCode(200)
.And.HaveJsonBody<UserInfo[]>()
- .Which.Should().BeEquivalentTo(UserInfoList);
+ .Which.Should().BeEquivalentTo(UserInfos);
}
[Fact]
@@ -44,8 +44,8 @@ namespace Timeline.Tests.IntegratedTests using var client = await CreateClientAsAdministrator();
var res = await client.GetAsync("/users");
res.Should().HaveStatusCode(200)
- .And.HaveJsonBody<UserInfoForAdmin[]>()
- .Which.Should().BeEquivalentTo(UserInfoForAdminList);
+ .And.HaveJsonBody<UserInfo[]>()
+ .Which.Should().BeEquivalentTo(UserInfos);
}
[Fact]
@@ -55,7 +55,7 @@ namespace Timeline.Tests.IntegratedTests var res = await client.GetAsync($"/users/admin");
res.Should().HaveStatusCode(200)
.And.HaveJsonBody<UserInfo>()
- .Which.Should().BeEquivalentTo(UserInfoList[0]);
+ .Which.Should().BeEquivalentTo(UserInfos[0]);
}
[Fact]
@@ -65,7 +65,7 @@ namespace Timeline.Tests.IntegratedTests var res = await client.GetAsync($"/users/admin");
res.Should().HaveStatusCode(200)
.And.HaveJsonBody<UserInfo>()
- .Which.Should().BeEquivalentTo(UserInfoList[0]);
+ .Which.Should().BeEquivalentTo(UserInfos[0]);
}
[Fact]
@@ -74,8 +74,8 @@ namespace Timeline.Tests.IntegratedTests using var client = await CreateClientAsAdministrator();
var res = await client.GetAsync($"/users/user1");
res.Should().HaveStatusCode(200)
- .And.HaveJsonBody<UserInfoForAdmin>()
- .Which.Should().BeEquivalentTo(UserInfoForAdminList[1]);
+ .And.HaveJsonBody<UserInfo>()
+ .Which.Should().BeEquivalentTo(UserInfos[1]);
}
[Fact]
@@ -134,7 +134,7 @@ namespace Timeline.Tests.IntegratedTests {
var res = await client.GetAsync("/users/newuser");
var body = res.Should().HaveStatusCode(200)
- .And.HaveJsonBody<UserInfoForAdmin>()
+ .And.HaveJsonBody<UserInfo>()
.Which;
body.Administrator.Should().Be(true);
body.Nickname.Should().Be("aaa");
@@ -301,7 +301,7 @@ namespace Timeline.Tests.IntegratedTests {
var res = await client.GetAsync("users/aaa");
var body = res.Should().HaveStatusCode(200)
- .And.HaveJsonBody<UserInfoForAdmin>().Which;
+ .And.HaveJsonBody<UserInfo>().Which;
body.Username.Should().Be("aaa");
body.Nickname.Should().Be("ccc");
body.Administrator.Should().BeTrue();
diff --git a/Timeline/Controllers/TokenController.cs b/Timeline/Controllers/TokenController.cs index a7f5fbde..1fb0b17a 100644 --- a/Timeline/Controllers/TokenController.cs +++ b/Timeline/Controllers/TokenController.cs @@ -59,7 +59,7 @@ namespace Timeline.Controllers return Ok(new CreateTokenResponse
{
Token = result.Token,
- User = _mapper.Map<UserInfoForAdmin>(result.User)
+ User = _mapper.Map<UserInfo>(result.User)
});
}
catch (UserNotExistException e)
@@ -94,7 +94,7 @@ namespace Timeline.Controllers ("Username", result.Username), ("Token", request.Token)));
return Ok(new VerifyTokenResponse
{
- User = _mapper.Map<UserInfoForAdmin>(result)
+ User = _mapper.Map<UserInfo>(result)
});
}
catch (UserTokenTimeExpireException e)
diff --git a/Timeline/Controllers/UserController.cs b/Timeline/Controllers/UserController.cs index fa73c6f9..4572296b 100644 --- a/Timeline/Controllers/UserController.cs +++ b/Timeline/Controllers/UserController.cs @@ -29,35 +29,23 @@ namespace Timeline.Controllers _mapper = mapper;
}
- private IUserInfo ConvertToUserInfo(User user, bool administrator)
- {
- if (administrator)
- return _mapper.Map<UserInfoForAdmin>(user);
- else
- return _mapper.Map<UserInfo>(user);
- }
+ private UserInfo ConvertToUserInfo(User user) => _mapper.Map<UserInfo>(user);
[HttpGet("users")]
- public async Task<ActionResult<IUserInfo[]>> List()
+ public async Task<ActionResult<UserInfo[]>> List()
{
var users = await _userService.GetUsers();
- var administrator = this.IsAdministrator();
- // Note: the (object) explicit conversion. If not convert,
- // then result is a IUserInfo array and JsonSerializer will
- // treat all element as IUserInfo and deserialize only properties
- // in IUserInfo. So we convert it to object to make an object
- // array so that JsonSerializer use the runtime type.
- var result = users.Select(u => (object)ConvertToUserInfo(u, administrator)).ToArray();
+ var result = users.Select(u => ConvertToUserInfo(u)).ToArray();
return Ok(result);
}
[HttpGet("users/{username}")]
- public async Task<ActionResult<IUserInfo>> Get([FromRoute][Username] string username)
+ public async Task<ActionResult<UserInfo>> Get([FromRoute][Username] string username)
{
try
{
var user = await _userService.GetUserByUsername(username);
- return Ok(ConvertToUserInfo(user, this.IsAdministrator()));
+ return Ok(ConvertToUserInfo(user));
}
catch (UserNotExistException e)
{
diff --git a/Timeline/Models/Http/TokenController.cs b/Timeline/Models/Http/TokenController.cs index 383b2965..ea8b59ed 100644 --- a/Timeline/Models/Http/TokenController.cs +++ b/Timeline/Models/Http/TokenController.cs @@ -16,7 +16,7 @@ namespace Timeline.Models.Http public class CreateTokenResponse
{
public string Token { get; set; } = default!;
- public UserInfoForAdmin User { get; set; } = default!;
+ public UserInfo User { get; set; } = default!;
}
public class VerifyTokenRequest
@@ -27,6 +27,6 @@ namespace Timeline.Models.Http public class VerifyTokenResponse
{
- public UserInfoForAdmin User { get; set; } = default!;
+ public UserInfo User { get; set; } = default!;
}
}
diff --git a/Timeline/Models/Http/UserInfo.cs b/Timeline/Models/Http/UserInfo.cs index 07ac0aad..5890a7a6 100644 --- a/Timeline/Models/Http/UserInfo.cs +++ b/Timeline/Models/Http/UserInfo.cs @@ -7,54 +7,52 @@ using Timeline.Services; namespace Timeline.Models.Http
{
- public interface IUserInfo
- {
- string Username { get; set; }
- string Nickname { get; set; }
- string AvatarUrl { get; set; }
- }
-
- public class UserInfo : IUserInfo
+ public class UserInfo
{
public string Username { get; set; } = default!;
public string Nickname { get; set; } = default!;
- public string AvatarUrl { get; set; } = default!;
+ public bool? Administrator { get; set; } = default!;
+#pragma warning disable CA1707
+ public UserInfoLinks? _links { get; set; }
+#pragma warning restore CA1707
}
- public class UserInfoForAdmin : IUserInfo
+ public class UserInfoLinks
{
- public string Username { get; set; } = default!;
- public string Nickname { get; set; } = default!;
- public string AvatarUrl { get; set; } = default!;
- public bool Administrator { get; set; }
+ public string Avatar { get; set; } = default!;
+ public string Timeline { get; set; } = default!;
}
- public class UserInfoAvatarUrlValueResolver : IValueResolver<User, IUserInfo, string>
+ public class UserInfoLinksValueResolver : IValueResolver<User, UserInfo, UserInfoLinks?>
{
private readonly IActionContextAccessor? _actionContextAccessor;
private readonly IUrlHelperFactory? _urlHelperFactory;
- public UserInfoAvatarUrlValueResolver()
+ public UserInfoLinksValueResolver()
{
_actionContextAccessor = null;
_urlHelperFactory = null;
}
- public UserInfoAvatarUrlValueResolver(IActionContextAccessor actionContextAccessor, IUrlHelperFactory urlHelperFactory)
+ public UserInfoLinksValueResolver(IActionContextAccessor actionContextAccessor, IUrlHelperFactory urlHelperFactory)
{
_actionContextAccessor = actionContextAccessor;
_urlHelperFactory = urlHelperFactory;
}
- public string Resolve(User source, IUserInfo destination, string destMember, ResolutionContext context)
+ public UserInfoLinks? Resolve(User source, UserInfo destination, UserInfoLinks? destMember, ResolutionContext context)
{
- if (_actionContextAccessor == null)
+ if (_actionContextAccessor == null || _urlHelperFactory == null)
{
- return $"/users/{destination.Username}/avatar";
+ return null;
}
- var urlHelper = _urlHelperFactory!.GetUrlHelper(_actionContextAccessor.ActionContext);
- return urlHelper.ActionLink(nameof(UserAvatarController.Get), nameof(UserAvatarController), new { destination.Username });
+ var urlHelper = _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext);
+ return new UserInfoLinks
+ {
+ Avatar = urlHelper.ActionLink(nameof(UserAvatarController.Get), nameof(UserAvatarController), new { destination.Username }),
+ Timeline = urlHelper.ActionLink(nameof(PersonalTimelineController.TimelineGet), nameof(PersonalTimelineController), new { destination.Username })
+ };
}
}
@@ -62,8 +60,7 @@ namespace Timeline.Models.Http {
public UserInfoAutoMapperProfile()
{
- CreateMap<User, UserInfo>().ForMember(u => u.AvatarUrl, opt => opt.MapFrom<UserInfoAvatarUrlValueResolver>());
- CreateMap<User, UserInfoForAdmin>().ForMember(u => u.AvatarUrl, opt => opt.MapFrom<UserInfoAvatarUrlValueResolver>());
+ CreateMap<User, UserInfo>().ForMember(u => u._links, opt => opt.MapFrom<UserInfoLinksValueResolver>());
}
}
}
|