From 99b0c09a6bb984d811b63788015cfad1855b5f3c Mon Sep 17 00:00:00 2001 From: ivarlovlie Date: Mon, 14 Nov 2022 11:25:12 +0700 Subject: refactor: Endpoints - Model payloads in relevant route class - Move RequestTimeZoneInfo out of EndpointBase --- .../Internal/Account/CreateAccountPayload.cs | 17 ---------- .../Internal/Account/CreateAccountRoute.cs | 35 ++++++++++++-------- .../Internal/Account/DeleteAccountRoute.cs | 5 --- .../Endpoints/Internal/Account/GetAccountRoute.cs | 26 +++++++++++++++ .../api/src/Endpoints/Internal/Account/GetRoute.cs | 31 ------------------ .../src/Endpoints/Internal/Account/LoginPayload.cs | 18 +---------- .../src/Endpoints/Internal/Account/LoginRoute.cs | 29 +++++++++-------- .../Internal/Account/UpdateAccountPayload.cs | 17 ---------- .../Internal/Account/UpdateAccountRoute.cs | 37 ++++++++++++++-------- 9 files changed, 87 insertions(+), 128 deletions(-) delete mode 100644 code/api/src/Endpoints/Internal/Account/CreateAccountPayload.cs create mode 100644 code/api/src/Endpoints/Internal/Account/GetAccountRoute.cs delete mode 100644 code/api/src/Endpoints/Internal/Account/GetRoute.cs delete mode 100644 code/api/src/Endpoints/Internal/Account/UpdateAccountPayload.cs (limited to 'code/api/src/Endpoints/Internal/Account') diff --git a/code/api/src/Endpoints/Internal/Account/CreateAccountPayload.cs b/code/api/src/Endpoints/Internal/Account/CreateAccountPayload.cs deleted file mode 100644 index 1161af3..0000000 --- a/code/api/src/Endpoints/Internal/Account/CreateAccountPayload.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace IOL.GreatOffice.Api.Endpoints.Internal.Account; - -/// -/// Payload for creating new user accounts. -/// -public class CreateAccountPayload -{ - /// - /// Username for the new account. - /// - public string Username { get; set; } - - /// - /// Password for the new account. - /// - public string Password { get; set; } -} \ No newline at end of file diff --git a/code/api/src/Endpoints/Internal/Account/CreateAccountRoute.cs b/code/api/src/Endpoints/Internal/Account/CreateAccountRoute.cs index f34056d..6b6e7bc 100644 --- a/code/api/src/Endpoints/Internal/Account/CreateAccountRoute.cs +++ b/code/api/src/Endpoints/Internal/Account/CreateAccountRoute.cs @@ -1,35 +1,44 @@ +using Microsoft.Extensions.Localization; + namespace IOL.GreatOffice.Api.Endpoints.Internal.Account; -public class CreateAccountRoute : RouteBaseAsync.WithRequest.WithActionResult +public class CreateAccountRoute : RouteBaseAsync.WithRequest.WithActionResult { private readonly MainAppDatabase _database; private readonly UserService _userService; + private readonly IStringLocalizer _localizer; - public CreateAccountRoute(UserService userService, MainAppDatabase database) { + public CreateAccountRoute(UserService userService, MainAppDatabase database, IStringLocalizer localizer) { _userService = userService; _database = database; + _localizer = localizer; + } + + public class Payload + { + public string Username { get; set; } + public string Password { get; set; } } - /// - /// Create a new user account. - /// - /// - /// - /// [AllowAnonymous] [HttpPost("~/_/account/create")] - public override async Task HandleAsync(CreateAccountPayload request, CancellationToken cancellationToken = default) { + public override async Task HandleAsync(Payload request, CancellationToken cancellationToken = default) { + var errors = new Dictionary(); if (request.Username.IsValidEmailAddress() == false) { - return BadRequest(new KnownProblemModel("Invalid form", request.Username + " does not look like a valid email")); + errors.Add("username", _localizer["{0} does not look like a valid email", request.Username]); } if (request.Password.Length < 6) { - return BadRequest(new KnownProblemModel("Invalid form", "The password requires 6 or more characters.")); + errors.Add("password", _localizer["The password requires 6 or more characters."]); } var username = request.Username.Trim(); - if (_database.Users.Any(c => c.Username == username)) { - return BadRequest(new KnownProblemModel("Username is not available", "There is already a user registered with email: " + username)); + if (errors.All(p => p.Key != "username") && _database.Users.Any(c => c.Username == username)) { + errors.Add("username", _localizer["There is already a user registered with username: {0}", username]); + } + + if (errors.Any()) { + return KnownProblem(_localizer["Invalid form"], _localizer["You have invalid values"], errors); } var user = new User(username); diff --git a/code/api/src/Endpoints/Internal/Account/DeleteAccountRoute.cs b/code/api/src/Endpoints/Internal/Account/DeleteAccountRoute.cs index f0e8362..e9fe40d 100644 --- a/code/api/src/Endpoints/Internal/Account/DeleteAccountRoute.cs +++ b/code/api/src/Endpoints/Internal/Account/DeleteAccountRoute.cs @@ -10,11 +10,6 @@ public class DeleteAccountRoute : RouteBaseAsync.WithoutRequest.WithActionResult _userService = userService; } - /// - /// Delete the logged on user's account. - /// - /// - /// [HttpDelete("~/_/account/delete")] public override async Task HandleAsync(CancellationToken cancellationToken = default) { var user = _database.Users.SingleOrDefault(c => c.Id == LoggedInUser.Id); diff --git a/code/api/src/Endpoints/Internal/Account/GetAccountRoute.cs b/code/api/src/Endpoints/Internal/Account/GetAccountRoute.cs new file mode 100644 index 0000000..121b40f --- /dev/null +++ b/code/api/src/Endpoints/Internal/Account/GetAccountRoute.cs @@ -0,0 +1,26 @@ +namespace IOL.GreatOffice.Api.Endpoints.Internal.Account; + +public class GetAccountRoute : RouteBaseAsync.WithoutRequest.WithActionResult +{ + private readonly MainAppDatabase _database; + + public GetAccountRoute(MainAppDatabase database) { + _database = database; + } + + [HttpGet("~/_/account")] + public override async Task> HandleAsync(CancellationToken cancellationToken = default) { + var user = _database.Users + .Select(x => new {x.Username, x.Id}) + .SingleOrDefault(c => c.Id == LoggedInUser.Id); + if (user != default) { + return Ok(new LoggedInUserModel { + Id = LoggedInUser.Id, + Username = LoggedInUser.Username + }); + } + + await HttpContext.SignOutAsync(); + return Unauthorized(); + } +} \ No newline at end of file diff --git a/code/api/src/Endpoints/Internal/Account/GetRoute.cs b/code/api/src/Endpoints/Internal/Account/GetRoute.cs deleted file mode 100644 index 8d6c50f..0000000 --- a/code/api/src/Endpoints/Internal/Account/GetRoute.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace IOL.GreatOffice.Api.Endpoints.Internal.Account; - -public class GetAccountRoute : RouteBaseAsync.WithoutRequest.WithActionResult -{ - private readonly MainAppDatabase _database; - - public GetAccountRoute(MainAppDatabase database) { - _database = database; - } - - /// - /// Get the logged on user's session data. - /// - /// - /// - [HttpGet("~/_/account")] - public override async Task> HandleAsync(CancellationToken cancellationToken = default) { - var user = _database.Users - .Select(x => new {x.Username, x.Id}) - .SingleOrDefault(c => c.Id == LoggedInUser.Id); - if (user != default) { - return Ok(new LoggedInUserModel { - Id = LoggedInUser.Id, - Username = LoggedInUser.Username - }); - } - - await HttpContext.SignOutAsync(); - return Unauthorized(); - } -} \ No newline at end of file diff --git a/code/api/src/Endpoints/Internal/Account/LoginPayload.cs b/code/api/src/Endpoints/Internal/Account/LoginPayload.cs index 807662c..a5670c6 100644 --- a/code/api/src/Endpoints/Internal/Account/LoginPayload.cs +++ b/code/api/src/Endpoints/Internal/Account/LoginPayload.cs @@ -1,22 +1,6 @@ namespace IOL.GreatOffice.Api.Endpoints.Internal.Account; -/// -/// Payload for logging in a user. -/// public class LoginPayload { - /// - /// Username of the user's account. - /// - public string Username { get; set; } - /// - /// Password of the user's account. - /// - public string Password { get; set; } - - /// - /// Specify that the created session should be long lived and continually refreshed. - /// - public bool Persist { get; set; } -} +} \ No newline at end of file diff --git a/code/api/src/Endpoints/Internal/Account/LoginRoute.cs b/code/api/src/Endpoints/Internal/Account/LoginRoute.cs index 696c3c2..eaebc2a 100644 --- a/code/api/src/Endpoints/Internal/Account/LoginRoute.cs +++ b/code/api/src/Endpoints/Internal/Account/LoginRoute.cs @@ -1,31 +1,32 @@ +using Microsoft.Extensions.Localization; + namespace IOL.GreatOffice.Api.Endpoints.Internal.Account; -public class LoginRoute : RouteBaseAsync.WithRequest.WithActionResult +public class LoginRoute : RouteBaseAsync.WithRequest.WithActionResult { private readonly MainAppDatabase _database; private readonly UserService _userService; + private readonly IStringLocalizer _localizer; - public LoginRoute(MainAppDatabase database, UserService userService) { + public LoginRoute(MainAppDatabase database, UserService userService, IStringLocalizer localizer) { _database = database; _userService = userService; + _localizer = localizer; + } + + public class Payload + { + public string Username { get; set; } + public string Password { get; set; } + public bool Persist { get; set; } } - /// - /// Login a user. - /// - /// - /// - /// [AllowAnonymous] [HttpPost("~/_/account/login")] - public override async Task HandleAsync(LoginPayload request, CancellationToken cancellationToken = default) { - if (!ModelState.IsValid) { - return BadRequest(ModelState); - } - + public override async Task HandleAsync(Payload request, CancellationToken cancellationToken = default) { var user = _database.Users.SingleOrDefault(u => u.Username == request.Username); if (user == default || !user.VerifyPassword(request.Password)) { - return BadRequest(new KnownProblemModel("Invalid username or password")); + return KnownProblem(_localizer["Invalid username or password"]); } await _userService.LogInUser(HttpContext, user, request.Persist); diff --git a/code/api/src/Endpoints/Internal/Account/UpdateAccountPayload.cs b/code/api/src/Endpoints/Internal/Account/UpdateAccountPayload.cs deleted file mode 100644 index 88a3237..0000000 --- a/code/api/src/Endpoints/Internal/Account/UpdateAccountPayload.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace IOL.GreatOffice.Api.Endpoints.Internal.Account; - -/// -/// Payload for updating an account. -/// -public class UpdatePayload -{ - /// - /// Username to set on the logged on user's account. - /// - public string Username { get; set; } - - /// - /// Password to set on the logged on user's account. - /// - public string Password { get; set; } -} diff --git a/code/api/src/Endpoints/Internal/Account/UpdateAccountRoute.cs b/code/api/src/Endpoints/Internal/Account/UpdateAccountRoute.cs index 02dc3f1..c8999e0 100644 --- a/code/api/src/Endpoints/Internal/Account/UpdateAccountRoute.cs +++ b/code/api/src/Endpoints/Internal/Account/UpdateAccountRoute.cs @@ -1,21 +1,26 @@ +using Microsoft.Extensions.Localization; + namespace IOL.GreatOffice.Api.Endpoints.Internal.Account; -public class UpdateAccountRoute : RouteBaseAsync.WithRequest.WithActionResult +public class UpdateAccountRoute : RouteBaseAsync.WithRequest.WithActionResult { private readonly MainAppDatabase _database; + private readonly IStringLocalizer _localizer; - public UpdateAccountRoute(MainAppDatabase database) { + public UpdateAccountRoute(MainAppDatabase database, IStringLocalizer localizer) { _database = database; + _localizer = localizer; + } + + public class Payload + { + public string Username { get; set; } + + public string Password { get; set; } } - /// - /// Update the logged on user's data. - /// - /// - /// - /// [HttpPost("~/_/account/update")] - public override async Task HandleAsync(UpdatePayload request, CancellationToken cancellationToken = default) { + public override async Task HandleAsync(Payload request, CancellationToken cancellationToken = default) { var user = _database.Users.SingleOrDefault(c => c.Id == LoggedInUser.Id); if (user == default) { await HttpContext.SignOutAsync(); @@ -23,12 +28,13 @@ public class UpdateAccountRoute : RouteBaseAsync.WithRequest.With } if (request.Password.IsNullOrWhiteSpace() && request.Username.IsNullOrWhiteSpace()) { - return BadRequest(new KnownProblemModel("Invalid request", "No data was submitted")); + return KnownProblem(_localizer["Invalid request"], _localizer["No data was submitted"]); } + var validationProblems = new Dictionary(); + if (request.Password.HasValue() && request.Password.Length < 6) { - return BadRequest(new KnownProblemModel("Invalid request", - "The new password must contain at least 6 characters")); + validationProblems.Add("password", _localizer["The new password must contain at least 6 characters"]); } if (request.Password.HasValue()) { @@ -36,8 +42,11 @@ public class UpdateAccountRoute : RouteBaseAsync.WithRequest.With } if (request.Username.HasValue() && !request.Username.IsValidEmailAddress()) { - return BadRequest(new KnownProblemModel("Invalid request", - "The new username does not look like a valid email address")); + validationProblems.Add("username", _localizer["The new username does not look like a valid email address"]); + } + + if (validationProblems.Any()) { + return KnownProblem(_localizer["Validation problems"], _localizer["Your form has invalid values"], validationProblems); } if (request.Username.HasValue()) { -- cgit v1.3