Skip to content

Commit

Permalink
Show validation for server claim field validation (#2826)
Browse files Browse the repository at this point in the history
* ClaimServiceImpl to primary ctor

* Add some logging for claims

* Show validation for server claim field validation
  • Loading branch information
leotsarev authored Oct 14, 2024
1 parent cdb64a8 commit ca3c14a
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 35 deletions.
2 changes: 1 addition & 1 deletion src/JoinRpg.Domain.Test/FieldSaveHelperTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ public void TryToSkipMandatoryField()

var claim = mock.CreateApprovedClaim(mock.Character, mock.Player);

var exception = Should.Throw<FieldRequiredException>(() =>
var exception = Should.Throw<CharacterFieldRequiredException>(() =>
InitFieldSaveHelper().SaveCharacterFields(
mock.Player.UserId,
claim,
Expand Down
2 changes: 1 addition & 1 deletion src/JoinRpg.Domain/CharacterFields/FieldSaveHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ private static void AssignValues(IReadOnlyDictionary<int, string?> newFieldValue

if (normalizedValue is null && field.Field.MandatoryStatus == MandatoryStatus.Required)
{
throw new FieldRequiredException(field.Field.Name);
throw new CharacterFieldRequiredException(field.Field.Name, field.Field.Id);
}

_ = strategy.AssignFieldValue(field, normalizedValue);
Expand Down
10 changes: 7 additions & 3 deletions src/JoinRpg.Domain/Exceptions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Linq.Expressions;
using JoinRpg.DataModel;
using JoinRpg.Helpers;
using JoinRpg.PrimitiveTypes;

namespace JoinRpg.Domain;

Expand Down Expand Up @@ -233,11 +234,14 @@ public NoAccessToProjectException(IProjectEntity entity, int? userId)
: base(entity, $"No access to entity of {entity.Project.ProjectName} for user {userId}") => UserId = userId;
}

public class FieldRequiredException : JoinRpgBaseException
public class FieldRequiredException(string fieldName) : JoinRpgBaseException($"Field {fieldName} is required")
{
public string FieldName { get; }
public string FieldName { get; } = fieldName;
}

public FieldRequiredException(string fieldName) : base($"{fieldName}") => FieldName = fieldName;
public class CharacterFieldRequiredException(string fieldName, ProjectFieldIdentification fieldId) : FieldRequiredException(fieldName)
{
public ProjectFieldIdentification FieldId { get; } = fieldId;
}

public class ValueAlreadySetException : JoinRpgBaseException
Expand Down
2 changes: 1 addition & 1 deletion src/JoinRpg.Portal/Controllers/ClaimController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ await claimService.AddClaimFromUser(viewModel.ProjectId, viewModel.CharacterGrou
catch (Exception exception)
{
ModelState.AddException(exception);
var source = await ProjectRepository.GetClaimSource(viewModel.ProjectId, viewModel.CharacterGroupId, viewModel.CharacterId).ConfigureAwait(false);
var source = await ProjectRepository.GetClaimSource(viewModel.ProjectId, viewModel.CharacterGroupId, viewModel.CharacterId);
var projectInfo = await projectMetadataRepository.GetProjectMetadata(new ProjectIdentification(viewModel.ProjectId));
viewModel.Fill(source, CurrentUserId, projectInfo, Request.GetDynamicValuesFromPost(FieldValueViewModel.HtmlIdPrefix));
return base.View(viewModel);
Expand Down
15 changes: 11 additions & 4 deletions src/JoinRpg.Portal/Infrastructure/ModelStateExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public static void AddException(this ModelStateDictionary dict, Exception except
case DbEntityValidationException validation:
var dbValidationErrors = validation.EntityValidationErrors
.SelectMany(eve => eve.ValidationErrors).ToList();
if (dbValidationErrors.Any())
if (dbValidationErrors.Count != 0)
{
foreach (var error in dbValidationErrors)
{
Expand All @@ -30,10 +30,15 @@ public static void AddException(this ModelStateDictionary dict, Exception except

dict.AddModelError("", exception.ToString());
return;

case CharacterFieldRequiredException required:
var errorMessage = GetErrorMessage(required);
dict.AddModelError("", errorMessage);
dict.AddModelError(Web.Models.FieldValueViewModel.HtmlIdPrefix + required.FieldId.ProjectFieldId, errorMessage);
return;
case FieldRequiredException required:
dict.AddModelError("", required.FieldName + " is required");
dict.AddModelError(required.FieldName, " required");
var errorMessage1 = GetErrorMessage(required);
dict.AddModelError("", errorMessage1);
dict.AddModelError(required.FieldName, errorMessage1);
return;
case ClaimWrongStatusException _:
case ProjectDeactivatedException _:
Expand All @@ -54,4 +59,6 @@ public static void AddException(this ModelStateDictionary dict, Exception except
break;
}
}

private static string GetErrorMessage(FieldRequiredException required) => $"{required.FieldName} — обязательное поле";
}
2 changes: 2 additions & 0 deletions src/JoinRpg.Portal/Views/Claim/Add.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@
{
ViewBag.HideCharacterClaimFieldsIcon = true;

@Html.ValidationSummary(true, "", new { @class = "text-danger" })

@await Html.PartialAsync("_EditFieldsPartial", Model.Fields)

if (Model.Fields.Fields.Any(f => f.HasPrice))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,22 @@
default:
throw new InvalidEnumArgumentException("Unknown field type name");
}
@{
var tryGetModelStateResult = ViewData.ModelState.TryGetValue(Model.FieldClientId, out var entry);
var modelErrors = tryGetModelStateResult ? entry?.Errors : null;
Microsoft.AspNetCore.Mvc.ModelBinding.ModelError modelError = null;
if (modelErrors != null && modelErrors.Count != 0)
{
modelError = modelErrors.FirstOrDefault(m => !string.IsNullOrEmpty(m.ErrorMessage)) ?? modelErrors[0];
}
if (modelError is not null)
{
<span class="text-danger field-validation-error" data-valmsg-for="@Model.FieldClientId" data-valmsg-replace="true">
<span id="@(Model.FieldClientId)-error" class="">@modelError.ErrorMessage</span>
</span>
}
}

<span class="field-validation-valid text-danger" data-valmsg-for="@Model.FieldClientId" data-valmsg-replace="true"></span>
@if (Model.HasMasterAccess)
{
Expand Down Expand Up @@ -221,7 +237,6 @@
</div>
}
}
@Html.ValidationMessageFor(model => model.Value, "", new { @class = "text-danger" })
</div>

<div class="col-md-2 price-table">
Expand Down
48 changes: 24 additions & 24 deletions src/JoinRpg.Services.Impl/ClaimServiceImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,20 @@
using JoinRpg.PrimitiveTypes.ProjectMetadata;
using JoinRpg.Services.Interfaces;
using JoinRpg.Services.Interfaces.Notification;
using Microsoft.Extensions.Logging;

namespace JoinRpg.Services.Impl;

internal class ClaimServiceImpl : ClaimImplBase, IClaimService
internal class ClaimServiceImpl(
IUnitOfWork unitOfWork,
IEmailService emailService,
FieldSaveHelper fieldSaveHelper,
IAccommodationInviteService accommodationInviteService,
ICurrentUserAccessor currentUserAccessor,
IProjectMetadataRepository projectMetadataRepository,
IProblemValidator<Claim> claimValidator,
ILogger<CharacterServiceImpl> logger)
: ClaimImplBase(unitOfWork, emailService, currentUserAccessor, projectMetadataRepository), IClaimService
{

public async Task SubscribeClaimToUser(int projectId, int claimId)
Expand Down Expand Up @@ -170,6 +180,14 @@ public async Task AddClaimFromUser(int projectId,
string claimText,
IReadOnlyDictionary<int, string?> fields)
{
if (characterId is not null)
{
logger.LogDebug("About to add claim to character {characterId}", characterId);
}
if (characterGroupId is not null)
{
logger.LogDebug("About to add claim to character {characterGroupId}", characterGroupId);
}
var source = await ProjectRepository.GetClaimSource(projectId, characterGroupId, characterId);
var projectInfo = await ProjectMetadataRepository.GetProjectMetadata(new(projectId));

Expand Down Expand Up @@ -223,14 +241,15 @@ public async Task AddClaimFromUser(int projectId,

if (claim.Project.Details.AutoAcceptClaims)
{
var userId = claim.ResponsibleMasterUserId;
StartImpersonate(userId);
StartImpersonate(claim.ResponsibleMasterUserId);
//TODO[Localize]
await ApproveByMaster(projectId,
claim.ClaimId,
"Ваша заявка была принята автоматически");
ResetImpersonation();
}

logger.LogInformation("Claim ({claimId}) was successfully send", claim.ClaimId);
}

public async Task AddComment(int projectId, int claimId, int? parentCommentId, bool isVisibleToPlayer, string commentText, FinanceOperationAction financeAction)
Expand Down Expand Up @@ -485,7 +504,7 @@ public async Task DeclineByMaster(int projectId, int claimId, Claim.DenialStatus
DeleteCharacter(claim.Character, CurrentUserId);
}

await _accommodationInviteService.DeclineAllClaimInvites(claimId).ConfigureAwait(false);
await accommodationInviteService.DeclineAllClaimInvites(claimId).ConfigureAwait(false);

var email =
await
Expand Down Expand Up @@ -687,7 +706,7 @@ public async Task DeclineByPlayer(int projectId, int claimId, string commentText
claim.ClaimStatus = Claim.Status.DeclinedByUser;


await _accommodationInviteService.DeclineAllClaimInvites(claimId).ConfigureAwait(false);
await accommodationInviteService.DeclineAllClaimInvites(claimId).ConfigureAwait(false);

var roomEmail = await CommonClaimDecline(claim);

Expand Down Expand Up @@ -913,25 +932,6 @@ private void MarkCharacterChangedIfApproved(Claim claim)
}
}

private readonly FieldSaveHelper fieldSaveHelper;
private readonly IAccommodationInviteService _accommodationInviteService;
private readonly IPlotService plotService;
private readonly IProblemValidator<Claim> claimValidator;

public ClaimServiceImpl(IUnitOfWork unitOfWork, IEmailService emailService,
FieldSaveHelper fieldSaveHelper,
IAccommodationInviteService accommodationInviteService,
ICurrentUserAccessor currentUserAccessor,
IPlotService plotService,
IProjectMetadataRepository projectMetadataRepository,
IProblemValidator<Claim> claimValidator) : base(unitOfWork, emailService, currentUserAccessor, projectMetadataRepository)
{
this.fieldSaveHelper = fieldSaveHelper;
_accommodationInviteService = accommodationInviteService;
this.plotService = plotService;
this.claimValidator = claimValidator;
}

private void SetDiscussed(Claim claim, bool isVisibleToPlayer)
{
claim.LastUpdateDateTime = Now;
Expand Down

0 comments on commit ca3c14a

Please sign in to comment.