aboutsummaryrefslogtreecommitdiffstats
path: root/code/api/src
diff options
context:
space:
mode:
authorivarlovlie <git@ivarlovlie.no>2022-11-17 07:46:38 +0100
committerivarlovlie <git@ivarlovlie.no>2022-11-17 07:46:38 +0100
commit971a70d15e5531b59afd74556dc50214deeeafe9 (patch)
tree67210f7bf53dcbe5b17bf62c7837dca1cd88907c /code/api/src
parent18b8d910b3dfa1e5c2a39b401b007b5e1ab8dea5 (diff)
downloadgreatoffice-971a70d15e5531b59afd74556dc50214deeeafe9.tar.xz
greatoffice-971a70d15e5531b59afd74556dc50214deeeafe9.zip
feat: Update known problem model
Diffstat (limited to 'code/api/src')
-rw-r--r--code/api/src/Data/Models/KnownProblemModel.cs19
-rw-r--r--code/api/src/Endpoints/Internal/Account/CreateAccountRoute.cs16
-rw-r--r--code/api/src/Endpoints/Internal/Account/UpdateAccountRoute.cs12
-rw-r--r--code/api/src/Endpoints/Internal/PasswordResetRequests/CreateResetRequestRoute.cs27
-rw-r--r--code/api/src/Endpoints/Internal/PasswordResetRequests/FulfillResetRequestRoute.cs13
-rw-r--r--code/api/src/Endpoints/V1/Customers/CreateCustomerRoute.cs11
-rw-r--r--code/api/src/Endpoints/V1/Projects/CreateProjectRoute.cs14
7 files changed, 82 insertions, 30 deletions
diff --git a/code/api/src/Data/Models/KnownProblemModel.cs b/code/api/src/Data/Models/KnownProblemModel.cs
index 38b3eba..818a295 100644
--- a/code/api/src/Data/Models/KnownProblemModel.cs
+++ b/code/api/src/Data/Models/KnownProblemModel.cs
@@ -2,7 +2,7 @@ namespace IOL.GreatOffice.Api.Data.Models;
public class KnownProblemModel
{
- public KnownProblemModel(string title = default, string subtitle = default, Dictionary<string, string> errors = default) {
+ public KnownProblemModel(string title = default, string subtitle = default, Dictionary<string, string[]> errors = default) {
Title = title;
Subtitle = subtitle;
Errors = errors;
@@ -10,6 +10,21 @@ public class KnownProblemModel
public string Title { get; set; }
public string Subtitle { get; set; }
- public Dictionary<string, string> Errors { get; set; }
+ public Dictionary<string, string[]> Errors { get; set; }
public string TraceId { get; set; }
+
+ public void AddError(string field, string errorText) {
+ if (Errors == default) {
+ Errors = new Dictionary<string, string[]>();
+ }
+
+ if (!Errors.ContainsKey(field)) {
+ Errors.Add(field, new[] {errorText});
+ } else {
+ var currentErrors = Errors[field];
+ var newErrors = currentErrors.Concat(new[] {errorText});
+ Errors.Remove(field);
+ Errors.Add(field, newErrors.ToArray());
+ }
+ }
} \ 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 6b6e7bc..81c507d 100644
--- a/code/api/src/Endpoints/Internal/Account/CreateAccountRoute.cs
+++ b/code/api/src/Endpoints/Internal/Account/CreateAccountRoute.cs
@@ -23,22 +23,24 @@ public class CreateAccountRoute : RouteBaseAsync.WithRequest<CreateAccountRoute.
[AllowAnonymous]
[HttpPost("~/_/account/create")]
public override async Task<ActionResult> HandleAsync(Payload request, CancellationToken cancellationToken = default) {
- var errors = new Dictionary<string, string>();
+ var problem = new KnownProblemModel();
if (request.Username.IsValidEmailAddress() == false) {
- errors.Add("username", _localizer["{0} does not look like a valid email", request.Username]);
+ problem.AddError("username", _localizer["{0} does not look like a valid email", request.Username]);
}
if (request.Password.Length < 6) {
- errors.Add("password", _localizer["The password requires 6 or more characters."]);
+ problem.AddError("password", _localizer["The password requires 6 or more characters."]);
}
var username = request.Username.Trim();
- 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 (problem.Errors.All(p => p.Key != "username") && _database.Users.Any(c => c.Username == username)) {
+ problem.AddError("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);
+ if (problem.Errors.Any()) {
+ problem.Title = _localizer["Invalid form"];
+ problem.Subtitle = _localizer["One or more fields is invalid"];
+ return KnownProblem(problem);
}
var user = new User(username);
diff --git a/code/api/src/Endpoints/Internal/Account/UpdateAccountRoute.cs b/code/api/src/Endpoints/Internal/Account/UpdateAccountRoute.cs
index c8999e0..c75e750 100644
--- a/code/api/src/Endpoints/Internal/Account/UpdateAccountRoute.cs
+++ b/code/api/src/Endpoints/Internal/Account/UpdateAccountRoute.cs
@@ -31,10 +31,10 @@ public class UpdateAccountRoute : RouteBaseAsync.WithRequest<UpdateAccountRoute.
return KnownProblem(_localizer["Invalid request"], _localizer["No data was submitted"]);
}
- var validationProblems = new Dictionary<string, string>();
+ var problem = new KnownProblemModel();
if (request.Password.HasValue() && request.Password.Length < 6) {
- validationProblems.Add("password", _localizer["The new password must contain at least 6 characters"]);
+ problem.AddError("password", _localizer["The new password must contain at least 6 characters"]);
}
if (request.Password.HasValue()) {
@@ -42,11 +42,13 @@ public class UpdateAccountRoute : RouteBaseAsync.WithRequest<UpdateAccountRoute.
}
if (request.Username.HasValue() && !request.Username.IsValidEmailAddress()) {
- validationProblems.Add("username", _localizer["The new username does not look like a valid email address"]);
+ problem.AddError("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 (problem.Errors.Any()) {
+ problem.Title = _localizer["Invalid form"];
+ problem.Subtitle = _localizer["One or more validation errors occured"];
+ return KnownProblem(problem);
}
if (request.Username.HasValue()) {
diff --git a/code/api/src/Endpoints/Internal/PasswordResetRequests/CreateResetRequestRoute.cs b/code/api/src/Endpoints/Internal/PasswordResetRequests/CreateResetRequestRoute.cs
index edf825e..9a22ab3 100644
--- a/code/api/src/Endpoints/Internal/PasswordResetRequests/CreateResetRequestRoute.cs
+++ b/code/api/src/Endpoints/Internal/PasswordResetRequests/CreateResetRequestRoute.cs
@@ -1,29 +1,42 @@
+using Microsoft.Extensions.Localization;
+
namespace IOL.GreatOffice.Api.Endpoints.Internal.PasswordResetRequests;
-public class CreateResetRequestRoute : RouteBaseAsync.WithRequest<string>.WithActionResult
+public class CreateResetRequestRoute : RouteBaseAsync.WithRequest<CreateResetRequestRoute.Payload>.WithActionResult
{
private readonly ILogger<CreateResetRequestRoute> _logger;
private readonly PasswordResetService _passwordResetService;
private readonly MainAppDatabase _database;
+ private readonly IStringLocalizer<SharedResources> _localizer;
- public CreateResetRequestRoute(ILogger<CreateResetRequestRoute> logger, PasswordResetService passwordResetService, MainAppDatabase database) {
+ public CreateResetRequestRoute(ILogger<CreateResetRequestRoute> logger, PasswordResetService passwordResetService, MainAppDatabase database, IStringLocalizer<SharedResources> localizer) {
_logger = logger;
_passwordResetService = passwordResetService;
_database = database;
+ _localizer = localizer;
+ }
+
+ public class Payload
+ {
+ public string Email { get; set; }
}
[AllowAnonymous]
[HttpPost("~/_/password-reset-request/create")]
- public override async Task<ActionResult> HandleAsync([FromQuery(Name = "for_user")] string username, CancellationToken cancellationToken = default) {
+ public override async Task<ActionResult> HandleAsync(Payload payload, CancellationToken cancellationToken = default) {
+ if (payload.Email.IsNullOrWhiteSpace()) {
+ return KnownProblem(_localizer["Invalid form"],
+ _localizer["One or more fields is invalid"],
+ new() {{"email", new string[] {_localizer["Email is a required field"]}}}
+ );
+ }
+
var tz = GetRequestTimeZone(_logger);
_logger.LogInformation("Creating forgot password request with local date time: " + tz.LocalDateTime.ToString("u"));
-
- var user = _database.Users.FirstOrDefault(c => c.Username.Equals(username));
+ var user = _database.Users.FirstOrDefault(c => c.Username.Equals(payload.Email));
// Don't inform the caller that the user does not exist.
if (user == default) return Ok();
-
await _passwordResetService.AddRequestAsync(user, tz.TimeZoneInfo, cancellationToken);
-
return Ok();
}
} \ No newline at end of file
diff --git a/code/api/src/Endpoints/Internal/PasswordResetRequests/FulfillResetRequestRoute.cs b/code/api/src/Endpoints/Internal/PasswordResetRequests/FulfillResetRequestRoute.cs
index c831470..8c7ce03 100644
--- a/code/api/src/Endpoints/Internal/PasswordResetRequests/FulfillResetRequestRoute.cs
+++ b/code/api/src/Endpoints/Internal/PasswordResetRequests/FulfillResetRequestRoute.cs
@@ -1,11 +1,15 @@
+using Microsoft.Extensions.Localization;
+
namespace IOL.GreatOffice.Api.Endpoints.Internal.PasswordResetRequests;
public class FulfillResetRequestRoute : RouteBaseAsync.WithRequest<FulfillResetRequestRoute.Payload>.WithActionResult
{
+ private readonly IStringLocalizer<SharedResources> _localizer;
private readonly PasswordResetService _passwordResetService;
- public FulfillResetRequestRoute(PasswordResetService passwordResetService) {
+ public FulfillResetRequestRoute(PasswordResetService passwordResetService, IStringLocalizer<SharedResources> localizer) {
_passwordResetService = passwordResetService;
+ _localizer = localizer;
}
public class Payload
@@ -17,6 +21,13 @@ public class FulfillResetRequestRoute : RouteBaseAsync.WithRequest<FulfillResetR
[AllowAnonymous]
[HttpPost("~/_/password-reset-request/fulfill")]
public override async Task<ActionResult> HandleAsync(Payload request, CancellationToken cancellationToken = default) {
+ if (request.NewPassword.Length < 6) {
+ return KnownProblem(_localizer["Invalid form"],
+ _localizer["One or more fields is invalid"],
+ new() {{"newPassword", new string[] {_localizer["The new password needs to be atleast 6 characters"]}}}
+ );
+ }
+
return await _passwordResetService.FulfillRequestAsync(request.Id, request.NewPassword, cancellationToken) switch {
FulfillPasswordResetRequestResult.REQUEST_NOT_FOUND => NotFound(),
FulfillPasswordResetRequestResult.USER_NOT_FOUND => NotFound(),
diff --git a/code/api/src/Endpoints/V1/Customers/CreateCustomerRoute.cs b/code/api/src/Endpoints/V1/Customers/CreateCustomerRoute.cs
index eb69f7f..b20b404 100644
--- a/code/api/src/Endpoints/V1/Customers/CreateCustomerRoute.cs
+++ b/code/api/src/Endpoints/V1/Customers/CreateCustomerRoute.cs
@@ -16,9 +16,14 @@ public class CreateCustomerRoute : RouteBaseAsync.WithRequest<CreateCustomerPayl
[HttpPost("~/v{version:apiVersion}/customers/create")]
public override async Task<ActionResult> HandleAsync(CreateCustomerPayload request, CancellationToken cancellationToken = default) {
- var errors = new Dictionary<string, string>();
- if (request.Name.Trim().IsNullOrEmpty()) errors.Add("name", _localizer["Name is a required field"]);
- if (errors.Any()) return KnownProblem(_localizer["Invalid form"], _localizer["One or more fields is invalid"], errors);
+ var problem = new KnownProblemModel();
+ if (request.Name.Trim().IsNullOrEmpty()) problem.AddError("name", _localizer["Name is a required field"]);
+ if (problem.Errors.Any()) {
+ problem.Title = _localizer["Invalid form"];
+ problem.Subtitle = _localizer["One or more validation errors occured"];
+ return KnownProblem(problem);
+ }
+
var customer = new Customer(LoggedInUser) {
CustomerNumber = request.CustomerNumber,
Name = request.Name,
diff --git a/code/api/src/Endpoints/V1/Projects/CreateProjectRoute.cs b/code/api/src/Endpoints/V1/Projects/CreateProjectRoute.cs
index 5c78e27..04a3a9a 100644
--- a/code/api/src/Endpoints/V1/Projects/CreateProjectRoute.cs
+++ b/code/api/src/Endpoints/V1/Projects/CreateProjectRoute.cs
@@ -14,10 +14,10 @@ public class CreateProjectRoute : RouteBaseAsync.WithRequest<CreateProjectPayloa
[HttpPost("~/v{version:apiVersion}/projects/create")]
public override async Task<ActionResult> HandleAsync(CreateProjectPayload request, CancellationToken cancellationToken = default) {
- var errors = new Dictionary<string, string>();
+ var problem = new KnownProblemModel();
if (request.Name.IsNullOrEmpty()) {
- errors.Add("name", _localizer["Name is a required field"]);
+ problem.AddError("name", _localizer["Name is a required field"]);
}
var project = new Project(LoggedInUser) {
@@ -31,7 +31,7 @@ public class CreateProjectRoute : RouteBaseAsync.WithRequest<CreateProjectPayloa
foreach (var customerId in request.CustomerIds) {
var customer = _database.Customers.FirstOrDefault(c => c.Id == customerId);
if (customer == default) {
- errors.Add("customer_" + customerId, _localizer["Customer not found"]);
+ problem.AddError("customer_" + customerId, _localizer["Customer not found"]);
continue;
}
@@ -41,7 +41,7 @@ public class CreateProjectRoute : RouteBaseAsync.WithRequest<CreateProjectPayloa
foreach (var member in request.Members) {
var user = _database.Users.FirstOrDefault(c => c.Id == member.UserId);
if (user == default) {
- errors.Add("members_" + member.UserId, _localizer["User not found"]);
+ problem.AddError("members_" + member.UserId, _localizer["User not found"]);
continue;
}
@@ -52,7 +52,11 @@ public class CreateProjectRoute : RouteBaseAsync.WithRequest<CreateProjectPayloa
});
}
- if (errors.Any()) return KnownProblem(_localizer["Invalid form"], _localizer["One or more fields is invalid"], errors);
+ if (problem.Errors.Any()) {
+ problem.Title = _localizer["Invalid form"];
+ problem.Subtitle = _localizer["One or more validation errors occured"];
+ return KnownProblem(problem);
+ }
_database.Projects.Add(project);
await _database.SaveChangesAsync(cancellationToken);