From dc1ab11cea249f4ca967f86b115147a63f7c93a5 Mon Sep 17 00:00:00 2001 From: 杨宇千 Date: Mon, 5 Aug 2019 18:58:56 +0800 Subject: 3 things. 1. Exchange Models and Entities namespace. 2. Fix the bug that input with missing field leads to 500. 3. Write unit tests. --- Timeline/Models/DatabaseContext.cs | 42 -------------------------- Timeline/Models/Http/Common.cs | 51 ++++++++++++++++++++++++++++++++ Timeline/Models/Http/Token.cs | 32 ++++++++++++++++++++ Timeline/Models/Http/User.cs | 26 +++++++++++++++++ Timeline/Models/PutResult.cs | 17 +++++++++++ Timeline/Models/UserInfo.cs | 23 +++++++++++++++ Timeline/Models/UserUtility.cs | 60 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 209 insertions(+), 42 deletions(-) delete mode 100644 Timeline/Models/DatabaseContext.cs create mode 100644 Timeline/Models/Http/Common.cs create mode 100644 Timeline/Models/Http/Token.cs create mode 100644 Timeline/Models/Http/User.cs create mode 100644 Timeline/Models/PutResult.cs create mode 100644 Timeline/Models/UserInfo.cs create mode 100644 Timeline/Models/UserUtility.cs (limited to 'Timeline/Models') diff --git a/Timeline/Models/DatabaseContext.cs b/Timeline/Models/DatabaseContext.cs deleted file mode 100644 index afd5a333..00000000 --- a/Timeline/Models/DatabaseContext.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; - -namespace Timeline.Models -{ - public static class UserRoles - { - public const string Admin = "admin"; - public const string User = "user"; - } - - [Table("user")] - public class User - { - [Column("id"), Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public long Id { get; set; } - - [Column("name"), Required] - public string Name { get; set; } - - [Column("password"), Required] - public string EncryptedPassword { get; set; } - - [Column("roles"), Required] - public string RoleString { get; set; } - - [Column("version"), Required] - public long Version { get; set; } - } - - public class DatabaseContext : DbContext - { - public DatabaseContext(DbContextOptions options) - : base(options) - { - - } - - public DbSet Users { get; set; } - } -} diff --git a/Timeline/Models/Http/Common.cs b/Timeline/Models/Http/Common.cs new file mode 100644 index 00000000..74959088 --- /dev/null +++ b/Timeline/Models/Http/Common.cs @@ -0,0 +1,51 @@ +namespace Timeline.Models.Http +{ + public class CommonResponse + { + public static class ErrorCodes + { + /// + /// Used when the model is invaid. + /// For example a required field is null. + /// + public const int InvalidModel = -100; + } + + public static CommonResponse InvalidModel(string message) + { + return new CommonResponse(ErrorCodes.InvalidModel, message); + } + + public CommonResponse() + { + + } + + public CommonResponse(int code, string message) + { + Code = code; + Message = message; + } + + public int Code { get; set; } + public string Message { get; set; } + } + + public static class CommonPutResponse + { + public const int CreatedCode = 0; + public const int ModifiedCode = 1; + + public static CommonResponse Created { get; } = new CommonResponse(CreatedCode, "A new item is created."); + public static CommonResponse Modified { get; } = new CommonResponse(ModifiedCode, "An existent item is modified."); + } + + public static class CommonDeleteResponse + { + public const int DeletedCode = 0; + public const int NotExistsCode = 1; + + public static CommonResponse Deleted { get; } = new CommonResponse(DeletedCode, "An existent item is deleted."); + public static CommonResponse NotExists { get; } = new CommonResponse(NotExistsCode, "The item does not exist."); + } +} diff --git a/Timeline/Models/Http/Token.cs b/Timeline/Models/Http/Token.cs new file mode 100644 index 00000000..cef679ef --- /dev/null +++ b/Timeline/Models/Http/Token.cs @@ -0,0 +1,32 @@ +using System.ComponentModel.DataAnnotations; + +namespace Timeline.Models.Http +{ + public class CreateTokenRequest + { + [Required] + public string Username { get; set; } + [Required] + public string Password { get; set; } + // in days, optional + [Range(1, 365)] + public int? ExpireOffset { get; set; } + } + + public class CreateTokenResponse + { + public string Token { get; set; } + public UserInfo User { get; set; } + } + + public class VerifyTokenRequest + { + [Required] + public string Token { get; set; } + } + + public class VerifyTokenResponse + { + public UserInfo User { get; set; } + } +} diff --git a/Timeline/Models/Http/User.cs b/Timeline/Models/Http/User.cs new file mode 100644 index 00000000..1de7fae2 --- /dev/null +++ b/Timeline/Models/Http/User.cs @@ -0,0 +1,26 @@ +using System.ComponentModel.DataAnnotations; + +namespace Timeline.Models.Http +{ + public class UserPutRequest + { + [Required] + public string Password { get; set; } + [Required] + public bool Administrator { get; set; } + } + + public class UserPatchRequest + { + public string Password { get; set; } + public bool? Administrator { get; set; } + } + + public class ChangePasswordRequest + { + [Required] + public string OldPassword { get; set; } + [Required] + public string NewPassword { get; set; } + } +} diff --git a/Timeline/Models/PutResult.cs b/Timeline/Models/PutResult.cs new file mode 100644 index 00000000..f11ac138 --- /dev/null +++ b/Timeline/Models/PutResult.cs @@ -0,0 +1,17 @@ +namespace Timeline.Models +{ + /// + /// Represents the result of a "put" operation. + /// + public enum PutResult + { + /// + /// Indicates the item did not exist and now is created. + /// + Created, + /// + /// Indicates the item exists already and is modified. + /// + Modified + } +} diff --git a/Timeline/Models/UserInfo.cs b/Timeline/Models/UserInfo.cs new file mode 100644 index 00000000..b5cb1e7f --- /dev/null +++ b/Timeline/Models/UserInfo.cs @@ -0,0 +1,23 @@ +namespace Timeline.Models +{ + public sealed class UserInfo + { + public UserInfo() + { + } + + public UserInfo(string username, bool administrator) + { + Username = username; + Administrator = administrator; + } + + public string Username { get; set; } + public bool Administrator { get; set; } + + public override string ToString() + { + return $"Username: {Username} ; Administrator: {Administrator}"; + } + } +} diff --git a/Timeline/Models/UserUtility.cs b/Timeline/Models/UserUtility.cs new file mode 100644 index 00000000..711e321a --- /dev/null +++ b/Timeline/Models/UserUtility.cs @@ -0,0 +1,60 @@ +using System; +using System.Linq; +using Timeline.Entities; +using Timeline.Services; + +namespace Timeline.Models +{ + public static class UserUtility + { + public const string UserRole = UserRoles.User; + public const string AdminRole = UserRoles.Admin; + + public static string[] UserRoleArray { get; } = new string[] { UserRole }; + public static string[] AdminRoleArray { get; } = new string[] { UserRole, AdminRole }; + + public static string[] IsAdminToRoleArray(bool isAdmin) + { + return isAdmin ? AdminRoleArray : UserRoleArray; + } + + public static bool RoleArrayToIsAdmin(string[] roles) + { + return roles.Contains(AdminRole); + } + + public static string[] RoleStringToRoleArray(string roleString) + { + return roleString.Split(',').ToArray(); + } + + public static string RoleArrayToRoleString(string[] roles) + { + return string.Join(',', roles); + } + + public static string IsAdminToRoleString(bool isAdmin) + { + return RoleArrayToRoleString(IsAdminToRoleArray(isAdmin)); + } + + public static bool RoleStringToIsAdmin(string roleString) + { + return RoleArrayToIsAdmin(RoleStringToRoleArray(roleString)); + } + + public static UserInfo CreateUserInfo(User user) + { + if (user == null) + throw new ArgumentNullException(nameof(user)); + return new UserInfo(user.Name, RoleStringToIsAdmin(user.RoleString)); + } + + internal static UserCache CreateUserCache(User user) + { + if (user == null) + throw new ArgumentNullException(nameof(user)); + return new UserCache { Username = user.Name, Administrator = RoleStringToIsAdmin(user.RoleString), Version = user.Version }; + } + } +} -- cgit v1.2.3 From 063321c90b8509249e65b49f39cf7d4f375305f6 Mon Sep 17 00:00:00 2001 From: 杨宇千 Date: Thu, 8 Aug 2019 17:13:14 +0800 Subject: 2 things. 1. Make Administrator in UserPutRequest nullable. 2. Remove default route. --- Timeline/Controllers/UserController.cs | 10 +--------- Timeline/Models/Http/User.cs | 2 +- Timeline/Startup.cs | 2 +- 3 files changed, 3 insertions(+), 11 deletions(-) (limited to 'Timeline/Models') diff --git a/Timeline/Controllers/UserController.cs b/Timeline/Controllers/UserController.cs index 0992946c..28d9523a 100644 --- a/Timeline/Controllers/UserController.cs +++ b/Timeline/Controllers/UserController.cs @@ -18,8 +18,6 @@ namespace Timeline.Controllers { public const int Get_NotExists = -1001; - public const int Put_NoPassword = -2001; - public const int Patch_NotExists = -3001; public const int ChangePassword_BadOldPassword = -4001; @@ -55,13 +53,7 @@ namespace Timeline.Controllers [HttpPut("user/{username}"), AdminAuthorize] public async Task Put([FromBody] UserPutRequest request, [FromRoute] string username) { - if (request.Password == null) // This place will be refactored. - { - _logger.LogInformation("Attempt to put a user without a password. Username: {} .", username); - return BadRequest(); - } - - var result = await _userService.PutUser(username, request.Password, request.Administrator); + var result = await _userService.PutUser(username, request.Password, request.Administrator.Value); switch (result) { case PutResult.Created: diff --git a/Timeline/Models/Http/User.cs b/Timeline/Models/Http/User.cs index 3259a448..d45543fb 100644 --- a/Timeline/Models/Http/User.cs +++ b/Timeline/Models/Http/User.cs @@ -7,7 +7,7 @@ namespace Timeline.Models.Http [Required] public string Password { get; set; } [Required] - public bool Administrator { get; set; } + public bool? Administrator { get; set; } } public class UserPatchRequest diff --git a/Timeline/Startup.cs b/Timeline/Startup.cs index a28899f4..414bc705 100644 --- a/Timeline/Startup.cs +++ b/Timeline/Startup.cs @@ -89,7 +89,7 @@ namespace Timeline app.UseAuthentication(); - app.UseMvcWithDefaultRoute(); + app.UseMvc(); } } } -- cgit v1.2.3