From 52acf41e331ddbd66befed4692c804b754ba7d5c Mon Sep 17 00:00:00 2001 From: crupest Date: Thu, 30 Jan 2020 20:26:52 +0800 Subject: ... --- Timeline/Models/Http/TokenController.cs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 Timeline/Models/Http/TokenController.cs (limited to 'Timeline/Models/Http/TokenController.cs') diff --git a/Timeline/Models/Http/TokenController.cs b/Timeline/Models/Http/TokenController.cs new file mode 100644 index 00000000..383b2965 --- /dev/null +++ b/Timeline/Models/Http/TokenController.cs @@ -0,0 +1,32 @@ +using System.ComponentModel.DataAnnotations; + +namespace Timeline.Models.Http +{ + public class CreateTokenRequest + { + [Required] + public string Username { get; set; } = default!; + [Required] + public string Password { get; set; } = default!; + // in days, optional + [Range(1, 365)] + public int? Expire { get; set; } + } + + public class CreateTokenResponse + { + public string Token { get; set; } = default!; + public UserInfoForAdmin User { get; set; } = default!; + } + + public class VerifyTokenRequest + { + [Required] + public string Token { get; set; } = default!; + } + + public class VerifyTokenResponse + { + public UserInfoForAdmin User { get; set; } = default!; + } +} -- cgit v1.2.3 From 0dc8b59b435f978428c73bc28e2a424bc413c562 Mon Sep 17 00:00:00 2001 From: crupest Date: Fri, 31 Jan 2020 15:21:58 +0800 Subject: Combine two user info types. --- .../IntegratedTests/IntegratedTestBase.cs | 15 ++++---- .../IntegratedTests/PersonalTimelineTest.cs | 8 ++-- Timeline.Tests/IntegratedTests/TokenTest.cs | 4 +- Timeline.Tests/IntegratedTests/UserTest.cs | 20 +++++----- Timeline/Controllers/TokenController.cs | 4 +- Timeline/Controllers/UserController.cs | 22 +++-------- Timeline/Models/Http/TokenController.cs | 4 +- Timeline/Models/Http/UserInfo.cs | 45 ++++++++++------------ 8 files changed, 54 insertions(+), 68 deletions(-) (limited to 'Timeline/Models/Http/TokenController.cs') 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>, IDisposable { + static IntegratedTestBase() + { + FluentAssertions.AssertionOptions.AssertEquivalencyUsing(options => + options.Excluding(m => m.RuntimeType == typeof(UserInfo) && m.SelectedMemberPath == "_links")); + } + protected TestApplication TestApp { get; } protected WebApplicationFactory Factory => TestApp.Factory; @@ -56,7 +62,6 @@ namespace Timeline.Tests.IntegratedTests } var userInfoList = new List(); - var userInfoForAdminList = new List(); var userService = scope.ServiceProvider.GetRequiredService(); var mapper = scope.ServiceProvider.GetRequiredService(); @@ -65,11 +70,9 @@ namespace Timeline.Tests.IntegratedTests { userService.CreateUser(user).Wait(); userInfoList.Add(mapper.Map(user)); - userInfoForAdminList.Add(mapper.Map(user)); } - UserInfoList = userInfoList; - UserInfoForAdminList = userInfoForAdminList; + UserInfos = userInfoList; } } @@ -83,9 +86,7 @@ namespace Timeline.Tests.IntegratedTests TestApp.Dispose(); } - public IReadOnlyList UserInfoList { get; } - - public IReadOnlyList UserInfoForAdminList { get; } + public IReadOnlyList UserInfos { get; } public Task 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().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 { UserInfoList[2] }); + await AssertMembers(new List { 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().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() - .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() - .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() - .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() - .Which.Should().BeEquivalentTo(UserInfoForAdminList); + .And.HaveJsonBody() + .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() - .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() - .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() - .Which.Should().BeEquivalentTo(UserInfoForAdminList[1]); + .And.HaveJsonBody() + .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() + .And.HaveJsonBody() .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().Which; + .And.HaveJsonBody().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(result.User) + User = _mapper.Map(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(result) + User = _mapper.Map(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(user); - else - return _mapper.Map(user); - } + private UserInfo ConvertToUserInfo(User user) => _mapper.Map(user); [HttpGet("users")] - public async Task> List() + public async Task> 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> Get([FromRoute][Username] string username) + public async Task> 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 + public class UserInfoLinksValueResolver : IValueResolver { 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().ForMember(u => u.AvatarUrl, opt => opt.MapFrom()); - CreateMap().ForMember(u => u.AvatarUrl, opt => opt.MapFrom()); + CreateMap().ForMember(u => u._links, opt => opt.MapFrom()); } } } -- cgit v1.2.3