aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author杨宇千 <crupest@outlook.com>2019-08-22 14:32:37 +0800
committer杨宇千 <crupest@outlook.com>2019-08-22 14:32:37 +0800
commit693367c1ba22f9eff8554c987dc61c0283386caa (patch)
treeb7a0af4752333db84c668dc03ba0abea43ed7d61
parent1a998040268282086549e67ae81f8e059ee885a9 (diff)
downloadtimeline-693367c1ba22f9eff8554c987dc61c0283386caa.tar.gz
timeline-693367c1ba22f9eff8554c987dc61c0283386caa.tar.bz2
timeline-693367c1ba22f9eff8554c987dc61c0283386caa.zip
Add user detail controller.
-rw-r--r--Timeline.Tests/IntegratedTests/UserDetailTest.cs123
-rw-r--r--Timeline/Controllers/UserDetailController.cs75
-rw-r--r--Timeline/Services/UserDetailService.cs9
-rw-r--r--Timeline/Startup.cs1
4 files changed, 208 insertions, 0 deletions
diff --git a/Timeline.Tests/IntegratedTests/UserDetailTest.cs b/Timeline.Tests/IntegratedTests/UserDetailTest.cs
new file mode 100644
index 00000000..571f200f
--- /dev/null
+++ b/Timeline.Tests/IntegratedTests/UserDetailTest.cs
@@ -0,0 +1,123 @@
+using FluentAssertions;
+using Microsoft.AspNetCore.Mvc.Testing;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Threading.Tasks;
+using Timeline.Controllers;
+using Timeline.Models;
+using Timeline.Models.Http;
+using Timeline.Tests.Helpers;
+using Timeline.Tests.Helpers.Authentication;
+using Timeline.Tests.Mock.Data;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Timeline.Tests.IntegratedTests
+{
+ public class UserDetailTest : IClassFixture<MyWebApplicationFactory<Startup>>, IDisposable
+ {
+ private readonly WebApplicationFactory<Startup> _factory;
+ private readonly Action _disposeAction;
+
+ public UserDetailTest(MyWebApplicationFactory<Startup> factory, ITestOutputHelper outputHelper)
+ {
+ _factory = factory.WithTestConfig(outputHelper, out _disposeAction);
+ }
+
+ public void Dispose()
+ {
+ _disposeAction();
+ }
+
+ [Fact]
+ public async Task TestAsUser()
+ {
+ using (var client = await _factory.CreateClientAsUser())
+ {
+ {
+ var res = await client.GetAsync($"users/usernotexist/details");
+ res.Should().HaveStatusCodeNotFound()
+ .And.Should().HaveBodyAsCommonResponseWithCode(UserDetailController.ErrorCodes.Get_UserNotExist);
+ }
+
+ async Task GetAndTest(UserDetail d)
+ {
+ var res = await client.GetAsync($"users/{MockUsers.UserUsername}/details");
+ res.Should().HaveStatusCodeOk()
+ .And.Should().HaveBodyAsJson<UserDetail>()
+ .Which.Should().BeEquivalentTo(d);
+ }
+
+ await GetAndTest(new UserDetail());
+
+ {
+ var res = await client.PatchAsJsonAsync($"users/{MockUsers.AdminUsername}/details", new UserDetail());
+ res.Should().HaveStatusCode(HttpStatusCode.Forbidden)
+ .And.Should().HaveBodyAsCommonResponseWithCode(UserDetailController.ErrorCodes.Patch_Forbid);
+ }
+
+ {
+ var res = await client.PatchAsJsonAsync($"users/{MockUsers.UserUsername}/details", new UserDetail
+ {
+ QQ = "aaaaaaa",
+ EMail = "aaaaaa"
+ });
+ res.Should().HaveStatusCode(HttpStatusCode.BadRequest)
+ .And.Should().HaveBodyAsCommonResponseWithCode(CommonResponse.ErrorCodes.InvalidModel);
+ }
+
+ var detail = new UserDetail
+ {
+ QQ = "1234567",
+ EMail = "aaaa@aaa.net",
+ Description = "aaaaaaaaa"
+ };
+
+ {
+ var res = await client.PatchAsJsonAsync($"users/{MockUsers.UserUsername}/details", detail);
+ res.Should().HaveStatusCodeOk();
+ await GetAndTest(detail);
+ }
+
+ var detail2 = new UserDetail
+ {
+ QQ = "",
+ PhoneNumber = "12345678910",
+ Description = "bbbbbbbb"
+ };
+
+ {
+ var res = await client.PatchAsJsonAsync($"users/{MockUsers.UserUsername}/details", detail2);
+ res.Should().HaveStatusCodeOk();
+ await GetAndTest(new UserDetail
+ {
+ QQ = null,
+ EMail = detail.EMail,
+ PhoneNumber = detail2.PhoneNumber,
+ Description = detail2.Description
+ });
+ }
+ }
+ }
+
+ [Fact]
+ public async Task TestAsAdmin()
+ {
+ using (var client = await _factory.CreateClientAsAdmin())
+ {
+ {
+ var res = await client.PatchAsJsonAsync($"users/{MockUsers.UserUsername}/details", new UserDetail());
+ res.Should().HaveStatusCodeOk();
+ }
+
+ {
+ var res = await client.PatchAsJsonAsync($"users/usernotexist/details", new UserDetail());
+ res.Should().HaveStatusCodeNotFound()
+ .And.Should().HaveBodyAsCommonResponseWithCode(UserDetailController.ErrorCodes.Patch_UserNotExist);
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Timeline/Controllers/UserDetailController.cs b/Timeline/Controllers/UserDetailController.cs
new file mode 100644
index 00000000..9e1d5483
--- /dev/null
+++ b/Timeline/Controllers/UserDetailController.cs
@@ -0,0 +1,75 @@
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
+using System.Threading.Tasks;
+using Timeline.Authenticate;
+using Timeline.Models;
+using Timeline.Models.Http;
+using Timeline.Services;
+
+namespace Timeline.Controllers
+{
+ [Route("users/{username}/details")]
+ [ProducesErrorResponseType(typeof(CommonResponse))]
+ [ApiController]
+ public class UserDetailController : Controller
+ {
+ public static class ErrorCodes
+ {
+ public const int Get_UserNotExist = -1001;
+
+ public const int Patch_Forbid = -2001;
+ public const int Patch_UserNotExist = -2002;
+
+ }
+
+ private readonly ILogger<UserDetailController> _logger;
+ private readonly IUserDetailService _service;
+
+ public UserDetailController(ILogger<UserDetailController> logger, IUserDetailService service)
+ {
+ _logger = logger;
+ _service = service;
+ }
+
+ [HttpGet()]
+ [UserAuthorize]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UserDetail))]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ public async Task<IActionResult> Get([FromRoute] string username)
+ {
+ try
+ {
+ var detail = await _service.GetUserDetail(username);
+ return Ok(detail);
+ }
+ catch (UserNotExistException)
+ {
+ return NotFound(new CommonResponse(ErrorCodes.Get_UserNotExist, "The user does not exist."));
+ }
+ }
+
+ [HttpPatch()]
+ [Authorize]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(void))]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status403Forbidden)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ public async Task<IActionResult> Patch([FromRoute] string username, [FromBody] UserDetail detail)
+ {
+ if (!User.IsAdmin() && User.Identity.Name != username)
+ return StatusCode(StatusCodes.Status403Forbidden, new CommonResponse(ErrorCodes.Patch_Forbid, "You can't change other's details unless you are admin."));
+
+ try
+ {
+ await _service.UpdateUserDetail(username, detail);
+ return Ok();
+ }
+ catch (UserNotExistException)
+ {
+ return NotFound(new CommonResponse(ErrorCodes.Patch_UserNotExist, "The user does not exist."));
+ }
+ }
+ }
+}
diff --git a/Timeline/Services/UserDetailService.cs b/Timeline/Services/UserDetailService.cs
index d1fdc040..0bb745f3 100644
--- a/Timeline/Services/UserDetailService.cs
+++ b/Timeline/Services/UserDetailService.cs
@@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
@@ -89,4 +90,12 @@ namespace Timeline.Services
_logger.LogInformation("An entity is updated in user_details.");
}
}
+
+ public static class UserDetailServiceCollectionExtensions
+ {
+ public static void AddUserDetailService(this IServiceCollection services)
+ {
+ services.AddScoped<IUserDetailService, UserDetailService>();
+ }
+ }
}
diff --git a/Timeline/Startup.cs b/Timeline/Startup.cs
index 66f648c3..b5a5106b 100644
--- a/Timeline/Startup.cs
+++ b/Timeline/Startup.cs
@@ -46,6 +46,7 @@ namespace Timeline
services.AddTransient<IClock, Clock>();
services.AddUserAvatarService();
+ services.AddUserDetailService();
var databaseConfig = Configuration.GetSection(nameof(DatabaseConfig)).Get<DatabaseConfig>();