Validation fixes, finished application creation, added required char (*) for required fields

This commit is contained in:
2024-09-08 14:13:08 +03:00
parent 142148368f
commit c128349f3b
34 changed files with 372 additions and 100 deletions

View File

@@ -0,0 +1,13 @@
using System.Text.RegularExpressions;
namespace ApplicationLayer
{
public static class Constants
{
public readonly static Regex EnglishWordRegex = new("^[a-zA-Z]*$");
public readonly static Regex EnglishPhraseRegex = new(@"^[a-zA-Z№0-9?><;,{}[\]\-_+=!@#$%\^&*|']*$");
public readonly static Regex PhoneNumRegex = new(@"^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$");
}
}

View File

@@ -10,16 +10,22 @@ public class NameModelValidator : AbstractValidator<NameModel>
RuleFor(m => m.FirstName) RuleFor(m => m.FirstName)
.NotEmpty() .NotEmpty()
.WithMessage("First Name can not be empty") .WithMessage("First Name can not be empty")
.Matches(Constants.EnglishWordRegex)
.WithMessage("First name must be in english characters")
.MaximumLength(ConfigurationConstraints.NameLength) .MaximumLength(ConfigurationConstraints.NameLength)
.WithMessage($"First Name length must be less than {ConfigurationConstraints.NameLength}"); .WithMessage($"First Name length must be less than {ConfigurationConstraints.NameLength}");
RuleFor(m => m.Surname) RuleFor(m => m.Surname)
.NotEmpty() .NotEmpty()
.WithMessage("Surname can not be empty") .WithMessage("Surname can not be empty")
.Matches(Constants.EnglishWordRegex)
.WithMessage("Surname must be in english characters")
.MaximumLength(ConfigurationConstraints.NameLength) .MaximumLength(ConfigurationConstraints.NameLength)
.WithMessage($"Surname length must be less than {ConfigurationConstraints.NameLength}"); .WithMessage($"Surname length must be less than {ConfigurationConstraints.NameLength}");
RuleFor(m => m.Patronymic) RuleFor(m => m.Patronymic)
.Matches(Constants.EnglishWordRegex)
.WithMessage("Patronymic must be in english characters")
.MaximumLength(ConfigurationConstraints.NameLength) .MaximumLength(ConfigurationConstraints.NameLength)
.WithMessage($"Patronymic length must be less than {ConfigurationConstraints.NameLength}"); .WithMessage($"Patronymic length must be less than {ConfigurationConstraints.NameLength}");
} }

View File

@@ -11,12 +11,16 @@ public class PassportModelValidator : AbstractValidator<PassportModel>
RuleFor(r => r.Issuer) RuleFor(r => r.Issuer)
.NotEmpty() .NotEmpty()
.WithMessage("Passport issuer can not be empty") .WithMessage("Passport issuer can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Passport issuer field can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.IssuerNameLength) .MaximumLength(ConfigurationConstraints.IssuerNameLength)
.WithMessage($"Passport issuer length must be less than {ConfigurationConstraints.IssuerNameLength}"); .WithMessage($"Passport issuer length must be less than {ConfigurationConstraints.IssuerNameLength}");
RuleFor(r => r.Number) RuleFor(r => r.Number)
.NotEmpty() .NotEmpty()
.WithMessage("Passport number can not be empty") .WithMessage("Passport number can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Passport number field can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.PassportNumberLength) .MaximumLength(ConfigurationConstraints.PassportNumberLength)
.WithMessage($"Passport number length must be less than {ConfigurationConstraints.PassportNumberLength}"); .WithMessage($"Passport number length must be less than {ConfigurationConstraints.PassportNumberLength}");

View File

@@ -10,12 +10,16 @@ public class PlaceOfWorkModelValidator : AbstractValidator<PlaceOfWorkModel>
RuleFor(p => p.Name) RuleFor(p => p.Name)
.NotEmpty() .NotEmpty()
.WithMessage("Place of work name can not be empty") .WithMessage("Place of work name can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Place of work name field can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.PlaceOfWorkNameLength) .MaximumLength(ConfigurationConstraints.PlaceOfWorkNameLength)
.WithMessage($"Place of work name length must be less than {ConfigurationConstraints.PlaceOfWorkNameLength}"); .WithMessage($"Place of work name length must be less than {ConfigurationConstraints.PlaceOfWorkNameLength}");
RuleFor(p => p.PhoneNum) RuleFor(p => p.PhoneNum)
.NotEmpty() .NotEmpty()
.WithMessage("Place of work phone number can not be empty") .WithMessage("Place of work phone number can not be empty")
.Matches(Constants.PhoneNumRegex)
.WithMessage("Place of work phone number field must be valid")
.MaximumLength(ConfigurationConstraints.PhoneNumberLength) .MaximumLength(ConfigurationConstraints.PhoneNumberLength)
.WithMessage( .WithMessage(
$"Phone number length must be in range from {ConfigurationConstraints.PhoneNumberMinLength} to {ConfigurationConstraints.PhoneNumberLength}") $"Phone number length must be in range from {ConfigurationConstraints.PhoneNumberMinLength} to {ConfigurationConstraints.PhoneNumberLength}")
@@ -23,27 +27,39 @@ public class PlaceOfWorkModelValidator : AbstractValidator<PlaceOfWorkModel>
.WithMessage( .WithMessage(
$"Phone number length must be in range from {ConfigurationConstraints.PhoneNumberMinLength} to {ConfigurationConstraints.PhoneNumberLength}"); $"Phone number length must be in range from {ConfigurationConstraints.PhoneNumberMinLength} to {ConfigurationConstraints.PhoneNumberLength}");
RuleFor(p => p.Address)
.NotEmpty()
.WithMessage("Place of work address can not be empty");
RuleFor(p => p.Address.Country) RuleFor(p => p.Address.Country)
.NotEmpty() .NotEmpty()
.WithMessage("Country name of place of work can not be empty") .WithMessage("Country name of place of work can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Place of work Country field can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.CountryNameLength) .MaximumLength(ConfigurationConstraints.CountryNameLength)
.WithMessage($"Country name of place of work length must be less than {ConfigurationConstraints.CountryNameLength}"); .WithMessage($"Country name of place of work length must be less than {ConfigurationConstraints.CountryNameLength}");
RuleFor(p => p.Address.City) RuleFor(p => p.Address.City)
.NotEmpty() .NotEmpty()
.WithMessage("City name of place of work can not be empty") .WithMessage("City name of place of work can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Place of work City field can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.CityNameLength) .MaximumLength(ConfigurationConstraints.CityNameLength)
.WithMessage($"City name of place of work length must be less than {ConfigurationConstraints.CityNameLength}"); .WithMessage($"City name of place of work length must be less than {ConfigurationConstraints.CityNameLength}");
RuleFor(p => p.Address.Street) RuleFor(p => p.Address.Street)
.NotEmpty() .NotEmpty()
.WithMessage("Street name of place of work can not be empty") .WithMessage("Street name of place of work can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Place of work Street field can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.StreetNameLength) .MaximumLength(ConfigurationConstraints.StreetNameLength)
.WithMessage($"Street name of place of work length must be less than {ConfigurationConstraints.StreetNameLength}"); .WithMessage($"Street name of place of work length must be less than {ConfigurationConstraints.StreetNameLength}");
RuleFor(p => p.Address.Building) RuleFor(p => p.Address.Building)
.NotEmpty() .NotEmpty()
.WithMessage("Building of place of work can not be empty") .WithMessage("Building of place of work can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Place of work building field can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.CountryNameLength) .MaximumLength(ConfigurationConstraints.CountryNameLength)
.WithMessage($"Building of place of work length must be less than {ConfigurationConstraints.BuildingNumberLength}"); .WithMessage($"Building of place of work length must be less than {ConfigurationConstraints.BuildingNumberLength}");
} }

View File

@@ -19,6 +19,8 @@ public class AuthDataValidator : AbstractValidator<AuthData>
RuleFor(d => d.Password) RuleFor(d => d.Password)
.NotEmpty() .NotEmpty()
.WithMessage("Password can not be empty") .WithMessage("Password can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Password can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.PasswordLength) .MaximumLength(ConfigurationConstraints.PasswordLength)
.WithMessage($"Password length must be less than {ConfigurationConstraints.PasswordLength}"); .WithMessage($"Password length must be less than {ConfigurationConstraints.PasswordLength}");
} }

View File

@@ -15,18 +15,23 @@ public class RegisterApplicantRequestValidator : AbstractValidator<RegisterAppli
IValidator<PlaceOfWorkModel> placeOfWorkModelValidator) IValidator<PlaceOfWorkModel> placeOfWorkModelValidator)
{ {
RuleFor(r => r.RegisterRequest) RuleFor(r => r.RegisterRequest)
.NotEmpty()
.SetValidator(registerRequestValidator); .SetValidator(registerRequestValidator);
RuleFor(r => r.ApplicantName) RuleFor(r => r.ApplicantName)
.NotEmpty()
.SetValidator(nameValidator); .SetValidator(nameValidator);
RuleFor(r => r.FatherName) RuleFor(r => r.FatherName)
.NotEmpty()
.SetValidator(nameValidator); .SetValidator(nameValidator);
RuleFor(r => r.MotherName) RuleFor(r => r.MotherName)
.NotEmpty()
.SetValidator(nameValidator); .SetValidator(nameValidator);
RuleFor(r => r.Passport) RuleFor(r => r.Passport)
.NotEmpty()
.SetValidator(passportValidator); .SetValidator(passportValidator);
RuleFor(r => r.BirthDate) RuleFor(r => r.BirthDate)
@@ -38,38 +43,51 @@ public class RegisterApplicantRequestValidator : AbstractValidator<RegisterAppli
RuleFor(r => r.CountryOfBirth) RuleFor(r => r.CountryOfBirth)
.NotEmpty() .NotEmpty()
.WithMessage("Country of birth can not be empty") .WithMessage("Country of birth can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Country of birth field can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.CountryNameLength) .MaximumLength(ConfigurationConstraints.CountryNameLength)
.WithMessage($"Country of birth name length must be less than {ConfigurationConstraints.CountryNameLength}"); .WithMessage($"Country of birth name length must be less than {ConfigurationConstraints.CountryNameLength}");
RuleFor(r => r.CityOfBirth) RuleFor(r => r.CityOfBirth)
.NotEmpty() .NotEmpty()
.WithMessage("City of birth can not be empty") .WithMessage("City of birth can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("City of birth field can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.CityNameLength) .MaximumLength(ConfigurationConstraints.CityNameLength)
.WithMessage($"City of birth name length must be less than {ConfigurationConstraints.CityNameLength}"); .WithMessage($"City of birth name length must be less than {ConfigurationConstraints.CityNameLength}");
RuleFor(r => r.Citizenship) RuleFor(r => r.Citizenship)
.NotEmpty() .NotEmpty()
.WithMessage("Citizenship can not be empty") .WithMessage("Citizenship can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Citizenship field can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.CitizenshipLength) .MaximumLength(ConfigurationConstraints.CitizenshipLength)
.WithMessage($"Citizenship length must be less than {ConfigurationConstraints.CitizenshipLength}"); .WithMessage($"Citizenship length must be less than {ConfigurationConstraints.CitizenshipLength}");
RuleFor(r => r.CitizenshipByBirth) RuleFor(r => r.CitizenshipByBirth)
.NotEmpty() .NotEmpty()
.WithMessage("Citizenship by birth can not be empty") .WithMessage("Citizenship by birth can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Citizenship by birth field can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.CitizenshipLength) .MaximumLength(ConfigurationConstraints.CitizenshipLength)
.WithMessage($"Citizenship by birth length must be less than {ConfigurationConstraints.CitizenshipLength}"); .WithMessage($"Citizenship by birth length must be less than {ConfigurationConstraints.CitizenshipLength}");
RuleFor(r => r.Gender).IsInEnum(); RuleFor(r => r.Gender)
.IsInEnum();
RuleFor(r => r.MaritalStatus).IsInEnum(); RuleFor(r => r.MaritalStatus)
.IsInEnum();
RuleFor(r => r.JobTitle) RuleFor(r => r.JobTitle)
.NotEmpty() .NotEmpty()
.WithMessage("Title of job can not be empty") .WithMessage("Title of job can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Title of job field can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.JobTitleLength) .MaximumLength(ConfigurationConstraints.JobTitleLength)
.WithMessage($"Title of job length must be less than {ConfigurationConstraints.JobTitleLength}"); .WithMessage($"Title of job length must be less than {ConfigurationConstraints.JobTitleLength}");
RuleFor(r => r.PlaceOfWork) RuleFor(r => r.PlaceOfWork)
.NotEmpty()
.SetValidator(placeOfWorkModelValidator); .SetValidator(placeOfWorkModelValidator);
} }
} }

View File

@@ -9,6 +9,7 @@ public class RegisterRequestValidator : AbstractValidator<RegisterRequest>
public RegisterRequestValidator(IUsersRepository users, IValidator<AuthData> authDataValidator) public RegisterRequestValidator(IUsersRepository users, IValidator<AuthData> authDataValidator)
{ {
RuleFor(r => r.AuthData) RuleFor(r => r.AuthData)
.NotEmpty()
.SetValidator(authDataValidator) .SetValidator(authDataValidator)
.MustAsync(async (authData, ct) => await users.FindByEmailAsync(authData.Email, ct) is null) .MustAsync(async (authData, ct) => await users.FindByEmailAsync(authData.Email, ct) is null)
.WithMessage("Email already exists"); .WithMessage("Email already exists");

View File

@@ -12,6 +12,7 @@ public class PastVisaModel
/// Name of visa /// Name of visa
[MaxLength(ConfigurationConstraints.VisaNameLength)] [MaxLength(ConfigurationConstraints.VisaNameLength)]
[Required]
public string Name { get; set; } = null!; public string Name { get; set; } = null!;
/// Date when visa expires /// Date when visa expires

View File

@@ -16,5 +16,6 @@ public class PastVisitModel
/// Destination country of past visit /// Destination country of past visit
[MaxLength(ConfigurationConstraints.CountryNameLength)] [MaxLength(ConfigurationConstraints.CountryNameLength)]
[Required]
public string DestinationCountry { get; set; } = null!; public string DestinationCountry { get; set; } = null!;
} }

View File

@@ -12,5 +12,6 @@ public class PermissionToDestCountryModel
/// Issuing authority /// Issuing authority
[MaxLength(ConfigurationConstraints.IssuerNameLength)] [MaxLength(ConfigurationConstraints.IssuerNameLength)]
[Required]
public string Issuer { get; set; } = null!; public string Issuer { get; set; } = null!;
} }

View File

@@ -1,4 +1,5 @@
using ApplicationLayer.InfrastructureServicesInterfaces; using ApplicationLayer.InfrastructureServicesInterfaces;
using Domains;
using FluentValidation; using FluentValidation;
namespace ApplicationLayer.Services.VisaApplications.Models.Validation; namespace ApplicationLayer.Services.VisaApplications.Models.Validation;
@@ -21,6 +22,10 @@ public class PastVisaModelValidator : AbstractValidator<PastVisaModel>
RuleFor(v => v.Name) RuleFor(v => v.Name)
.NotEmpty() .NotEmpty()
.WithMessage("Name of past visa can not be empty"); .WithMessage("Name of past visa can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Name of past visa can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.VisaNameLength)
.WithMessage($"Past visa name length must be less than {ConfigurationConstraints.VisaNameLength}");
} }
} }

View File

@@ -23,6 +23,8 @@ public class PastVisitModelValidator : AbstractValidator<PastVisitModel>
RuleFor(v => v.DestinationCountry) RuleFor(v => v.DestinationCountry)
.NotEmpty() .NotEmpty()
.WithMessage("Destination Country of past visit can not be null") .WithMessage("Destination Country of past visit can not be null")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Destination Country of past visit can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.CountryNameLength) .MaximumLength(ConfigurationConstraints.CountryNameLength)
.WithMessage($"Destination Country of past visit length must be less than {ConfigurationConstraints.CountryNameLength}"); .WithMessage($"Destination Country of past visit length must be less than {ConfigurationConstraints.CountryNameLength}");
} }

View File

@@ -17,6 +17,8 @@ public class PermissionToDestCountryModelValidator : AbstractValidator<Permissio
RuleFor(p => p!.Issuer) RuleFor(p => p!.Issuer)
.NotEmpty() .NotEmpty()
.WithMessage("Issuer of permission for destination Country can not be empty") .WithMessage("Issuer of permission for destination Country can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Issuer of permission for destination Country can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.IssuerNameLength) .MaximumLength(ConfigurationConstraints.IssuerNameLength)
.WithMessage($"Issuer of permission to destination Country length must be less than {ConfigurationConstraints.IssuerNameLength}"); .WithMessage($"Issuer of permission to destination Country length must be less than {ConfigurationConstraints.IssuerNameLength}");
} }

View File

@@ -11,6 +11,8 @@ public class ReentryPermitModelValidator : AbstractValidator<ReentryPermitModel?
RuleFor(p => p!.Number) RuleFor(p => p!.Number)
.NotEmpty() .NotEmpty()
.WithMessage("Re-entry permit number can not be empty") .WithMessage("Re-entry permit number can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Re-entry permit number can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.ReentryPermitNumberLength) .MaximumLength(ConfigurationConstraints.ReentryPermitNumberLength)
.WithMessage($"Re-entry permit number length must be less than {ConfigurationConstraints.ReentryPermitNumberLength}"); .WithMessage($"Re-entry permit number length must be less than {ConfigurationConstraints.ReentryPermitNumberLength}");

View File

@@ -27,7 +27,7 @@
Client.AuthToken = token; Client.AuthToken = token;
AuthData = authData; AuthData = authData;
Status?.SetSucces("Logged in successfully."); Status?.SetSuccess("Logged in successfully.");
} }
catch (ApiException<ProblemDetails> e) catch (ApiException<ProblemDetails> e)
{ {

View File

@@ -3,7 +3,7 @@
<div> <div>
<div > <div >
<label> <label>
First name:<br/> First name@(Constants.RequiredFieldMarkup):<br/>
<InputText DisplayName="First name" class="rounded" @bind-Value="Name.FirstName"/> <InputText DisplayName="First name" class="rounded" @bind-Value="Name.FirstName"/>
</label><br/> </label><br/>
<ValidationMessage For="() => Name.FirstName"></ValidationMessage> <ValidationMessage For="() => Name.FirstName"></ValidationMessage>
@@ -11,7 +11,7 @@
<div > <div >
<label> <label>
Surname:<br/> Surname@(Constants.RequiredFieldMarkup):<br/>
<InputText class="rounded" @bind-Value="Name.Surname"/> <InputText class="rounded" @bind-Value="Name.Surname"/>
</label><br/> </label><br/>
<ValidationMessage For="() => Name.Surname"></ValidationMessage> <ValidationMessage For="() => Name.Surname"></ValidationMessage>

View File

@@ -25,7 +25,7 @@
StateHasChanged(); StateHasChanged();
} }
public void SetSucces(string message) public void SetSuccess(string message)
{ {
statusClass = "text-success"; statusClass = "text-success";
StatusText = message; StatusText = message;

View File

@@ -0,0 +1,16 @@
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Components;
namespace BlazorWebAssemblyVisaApiClient
{
public static class Constants
{
public readonly static Regex EnglishWordRegex = new("^[a-zA-Z]*$");
public readonly static Regex EnglishPhraseRegex = new(@"^[a-zA-Z№0-9?><;,{}[\]\-_+=!@#$%\^&*|']*$");
public readonly static Regex PhoneNumRegex = new(@"^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$");
public readonly static MarkupString RequiredFieldMarkup = (MarkupString)"<span style=\"color: red;\">*</span>";
}
}

View File

@@ -22,7 +22,7 @@
<ObjectGraphDataAnnotationsValidator/> <ObjectGraphDataAnnotationsValidator/>
<div class="form-block"> <div class="form-block">
<h5>Visa</h5> <h5>Visa@(Constants.RequiredFieldMarkup)</h5>
<label> <label>
Destination Country:<br/> Destination Country:<br/>
<InputText DisplayName="Destination Country" class="rounded" @bind-Value="requestModel.DestinationCountry"/> <InputText DisplayName="Destination Country" class="rounded" @bind-Value="requestModel.DestinationCountry"/>
@@ -56,7 +56,7 @@
<div class="form-block"> <div class="form-block">
<h5>Past visas</h5> <h5>Past visas</h5>
@if (currentPastVisa > 0) @if (requestModel.PastVisas.Count > 0)
{ {
<table class="table table-bordered"> <table class="table table-bordered">
<thead> <thead>
@@ -65,9 +65,8 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@for (var i = 0; i < currentPastVisa; i++) @foreach (var visa in requestModel.PastVisas)
{ {
var visa = requestModel.PastVisas[i];
<tr> <tr>
<th>@visa.Name</th> <th>@visa.Name</th>
<th>@visa.IssueDate.ToString("d.MM.yyyy")</th> <th>@visa.IssueDate.ToString("d.MM.yyyy")</th>
@@ -81,39 +80,94 @@
</table> </table>
} }
<label> <label>
Name: Name:<br/>
<InputText DisplayName="Past visa name" @bind-Value="requestModel.PastVisas[currentPastVisa].Name"/> <InputText DisplayName="Past visa name" class="rounded" @bind-Value="editableVisa.Name"/>
</label><br/> </label><br/>
<ValidationMessage For="() => requestModel.PastVisas[currentPastVisa].Name"></ValidationMessage><br/> <ValidationMessage For="() => editableVisa.Name"></ValidationMessage><br/>
<label> <label>
Issue date:<br/> Issue date:<br/>
<InputDate DisplayName="Past visa issue date" <InputDate DisplayName="Past visa issue date"
class="rounded" class="rounded"
@bind-Value="requestModel.PastVisas[currentPastVisa].IssueDate" @bind-Value="editableVisa.IssueDate"
max="@formattedNow"/> max="@formattedNow"/>
</label><br/> </label><br/>
<ValidationMessage For="() => requestModel.PastVisas[currentPastVisa].IssueDate"></ValidationMessage><br/> <ValidationMessage For="() => editableVisa.IssueDate"></ValidationMessage><br/>
<label> <label>
Expiration date:<br/> Expiration date:<br/>
<InputDate DisplayName="Past visa expiration date" <InputDate DisplayName="Past visa expiration date"
class="rounded" class="rounded"
@bind-Value="requestModel.PastVisas[currentPastVisa].ExpirationDate" @bind-Value="editableVisa.ExpirationDate"/>
min="@formattedNow"/>
</label><br/> </label><br/>
<ValidationMessage For="() => requestModel.PastVisas[currentPastVisa].ExpirationDate"></ValidationMessage><br/> <ValidationMessage For="() => editableVisa.ExpirationDate"></ValidationMessage><br/>
<input type="button" class="btn-outline-primary" <input type="button" class="btn-outline-primary"
disabled="@(currentPastVisa == requestModel.PastVisas.Length - 1)" disabled="@(requestModel.PastVisas.Count == ConfigurationConstraints.MaxPastVisas)"
@onclick="AddPastVisa" value="Add"/> @onclick="AddPastVisa" value="Add"/>
<Status @ref="pastVisaStatus"/> <Status @ref="pastVisaStatus"/>
</div> </div>
<div class="form-block">
<h5>Past visits</h5>
@if (requestModel.PastVisits.Count > 0)
{
<table class="table table-bordered">
<thead>
<tr>
<th>Destination Country</th><th>Start date</th><th>End date</th><th></th>
</tr>
</thead>
<tbody>
@foreach (var visit in requestModel.PastVisits)
{
<tr>
<th>@visit.DestinationCountry</th>
<th>@visit.StartDate.ToString("d.MM.yyyy")</th>
<th>@visit.EndDate.ToString("d.MM.yyyy")</th>
<th>
<input type="button" class="border-danger" @onclick="() => RemovePastVisit(visit)" value="X"/>
</th>
</tr>
}
</tbody>
</table>
}
<label>
Destination Country:<br/>
<InputText DisplayName="Past visit destination Country" class="rounded" @bind-Value="editableVisit.DestinationCountry"/>
</label><br/>
<ValidationMessage For="() => editableVisit.DestinationCountry"></ValidationMessage><br/>
<label>
Start date:<br/>
<InputDate DisplayName="Past visit start date"
class="rounded"
@bind-Value="editableVisit.StartDate"
max="@formattedNow"/>
</label><br/>
<ValidationMessage For="() => editableVisit.StartDate"></ValidationMessage><br/>
<label>
End date:<br/>
<InputDate DisplayName="Past visit end date"
class="rounded"
@bind-Value="editableVisit.EndDate"
max="@formattedNow"/>
</label><br/>
<ValidationMessage For="() => editableVisit.EndDate"></ValidationMessage><br/>
<input type="button" class="btn-outline-primary"
disabled="@(requestModel.PastVisits.Count == ConfigurationConstraints.MaxPastVisits)"
@onclick="AddPastVisit" value="Add"/>
<Status @ref="pastVisitStatus"/>
</div>
@if (requestModel.VisaCategory is VisaCategory.Transit) @if (requestModel.VisaCategory is VisaCategory.Transit)
{ {
requestModel.PermissionToDestCountry ??= NewPermissionToDestCountry();
<div class="form-block"> <div class="form-block">
<h5>Permission to destination Country</h5> <h5>Permission to destination Country@(Constants.RequiredFieldMarkup)</h5>
<PermissionToDestCountryInput PermissionToDestCountry="requestModel.PermissionToDestCountry"/> <PermissionToDestCountryInput PermissionToDestCountry="requestModel.PermissionToDestCountry"/>
</div> </div>
} }
@@ -124,14 +178,14 @@
@if (isNonResident) @if (isNonResident)
{ {
requestModel.ReentryPermit = NewReentryPermit();
<div class="form-block"> <div class="form-block">
<h5>Re-entry permission</h5> <h5>Re-entry permission@(Constants.RequiredFieldMarkup)</h5>
<ReentryPermitInput ReentryPermit="requestModel.ReentryPermit"/> <ReentryPermitInput ReentryPermit="requestModel.ReentryPermit"/>
</div> </div>
<br/>
} }
<input type="submit" class="btn-outline-primary" value="Register"/> <br/><input type="submit" class="btn-outline-primary" value="Register"/>
<ValidationSummary/> <ValidationSummary/>
<Status @ref="status"/> <Status @ref="status"/>
</EditForm> </EditForm>
@@ -143,10 +197,11 @@
private VisaApplicationCreateRequestModel requestModel = new(); private VisaApplicationCreateRequestModel requestModel = new();
private Status status = null!; private Status status = null!;
private Status pastVisaStatus = null!; private Status pastVisaStatus = null!;
private Status pastVisitStatus = null!;
private bool isNonResident; private bool isNonResident;
private int currentPastVisa;
private int currentPastVisit;
private string formattedNow = null!; private string formattedNow = null!;
private PastVisaModel editableVisa = null!;
private PastVisitModel editableVisit = null!;
[Inject] IDateTimeProvider DateTimeProvider { get; set; } = null!; [Inject] IDateTimeProvider DateTimeProvider { get; set; } = null!;
@@ -156,19 +211,16 @@
[Inject] IValidator<PastVisaModel> PastVisaModelValidator { get; set; } = null!; [Inject] IValidator<PastVisaModel> PastVisaModelValidator { get; set; } = null!;
[Inject] IValidator<PastVisitModel> PastVisitModelValidator { get; set; } = null!;
[Inject] IMapper Mapper { get; set; } = null!; [Inject] IMapper Mapper { get; set; } = null!;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
requestModel.PastVisas = new PastVisaModel[ConfigurationConstraints.MaxPastVisas]; editableVisa = NewPastVisa();
for (var i = 0; i < requestModel.PastVisas.Length; i++) editableVisit = NewPastVisit();
{ requestModel.PermissionToDestCountry = NewPermissionToDestCountry();
requestModel.PastVisas[i] = new() formattedNow = DateTimeProvider.FormattedNow();
{
IssueDate = DateTimeProvider.Now(),
ExpirationDate = DateTimeProvider.Now()
};
}
try try
{ {
@@ -178,16 +230,10 @@
{ {
ErrorHandler.Handle(e); ErrorHandler.Handle(e);
} }
formattedNow = DateTimeProvider.FormattedNow();
requestModel.PermissionToDestCountry!.ExpirationDate = DateTimeProvider.Now();
} }
private async Task TryCreate() private async Task TryCreate()
{ {
requestModel.PastVisas = currentPastVisa == 0 ? [] : requestModel.PastVisas[..currentPastVisa];
requestModel.PastVisits = currentPastVisit == 0 ? [] : requestModel.PastVisits[..currentPastVisit];
var validationResult = await VisaApplicationCreateRequestValidator.ValidateAsync(requestModel); var validationResult = await VisaApplicationCreateRequestValidator.ValidateAsync(requestModel);
if (!validationResult.IsValid) if (!validationResult.IsValid)
{ {
@@ -201,7 +247,7 @@
try try
{ {
await Client.CreateApplicationAsync(request); await Client.CreateApplicationAsync(request);
status.SetSucces("Application created successfully."); status.SetSuccess("Application created successfully.");
} }
catch (ApiException<ProblemDetails> e) catch (ApiException<ProblemDetails> e)
{ {
@@ -230,49 +276,87 @@
} }
} }
private PastVisaModel NewPastVisa()
{
return new()
{
ExpirationDate = DateTimeProvider.Now(),
IssueDate = DateTimeProvider.Now()
};
}
private ReentryPermitModel NewReentryPermit()
{
return new()
{
ExpirationDate = DateTimeProvider.Now()
};
}
private PermissionToDestCountryModel NewPermissionToDestCountry()
{
return new()
{
ExpirationDate = DateTimeProvider.Now()
};
}
private PastVisitModel NewPastVisit()
{
return new()
{
StartDate = DateTimeProvider.Now(),
EndDate = DateTimeProvider.Now()
};
}
private void AddPastVisa() private void AddPastVisa()
{ {
var validationResult = PastVisaModelValidator.Validate(requestModel.PastVisas[currentPastVisa]); if (requestModel.PastVisas.Count >= ConfigurationConstraints.MaxPastVisas)
{
pastVisaStatus.SetError($"{ConfigurationConstraints.MaxPastVisas} past visas is maximum");
return;
}
var validationResult = PastVisaModelValidator.Validate(editableVisa);
if (!validationResult.IsValid) if (!validationResult.IsValid)
{ {
pastVisaStatus.SetError(validationResult.ToErrorsString()); pastVisaStatus.SetError(validationResult.ToErrorsString());
return; return;
} }
if (currentPastVisa < requestModel.PastVisas.Length - 1) requestModel.PastVisas.Add(editableVisa);
{ editableVisa = NewPastVisa();
currentPastVisa++; pastVisaStatus.SetSuccess("Added successfully");
pastVisaStatus.SetSucces("Added");
}
else
{
pastVisaStatus.SetError($"{requestModel.PastVisas.Length} past visas is maximum");
}
} }
private void RemovePastVisa(PastVisaModel visa) private void RemovePastVisa(PastVisaModel visa)
{ {
currentPastVisa--; requestModel.PastVisas.Remove(visa);
var found = false; }
if (requestModel.PastVisas[^1] == visa) private void AddPastVisit()
{ {
requestModel.PastVisas[^1] = new(); if (requestModel.PastVisits.Count >= ConfigurationConstraints.MaxPastVisits)
{
pastVisitStatus.SetError($"{ConfigurationConstraints.MaxPastVisits} past visits is maximum");
return; return;
} }
for (var i = 0; i < requestModel.PastVisas.Length - 1; i++) var validationResult = PastVisitModelValidator.Validate(editableVisit);
if (!validationResult.IsValid)
{ {
if (requestModel.PastVisas[i] == visa) pastVisitStatus.SetError(validationResult.ToErrorsString());
{ return;
found = true;
} }
if (found) requestModel.PastVisits.Add(editableVisit);
{ editableVisit = NewPastVisit();
requestModel.PastVisas[i] = requestModel.PastVisas[i + 1]; pastVisitStatus.SetSuccess("Added successfully");
}
}
} }
private void RemovePastVisit(PastVisitModel visit)
{
requestModel.PastVisits.Remove(visit);
}
} }

View File

@@ -9,6 +9,7 @@
@using BlazorWebAssemblyVisaApiClient.Components @using BlazorWebAssemblyVisaApiClient.Components
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers @using BlazorWebAssemblyVisaApiClient.Infrastructure.Helpers
@using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.DateTimeProvider @using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.DateTimeProvider
@using BlazorWebAssemblyVisaApiClient.Validation
@using BlazorWebAssemblyVisaApiClient.Validation.Applicants.Models @using BlazorWebAssemblyVisaApiClient.Validation.Applicants.Models
@inherits BlazorWebAssemblyVisaApiClient.Components.Base.VisaClientComponentBase @inherits BlazorWebAssemblyVisaApiClient.Components.Base.VisaClientComponentBase
@@ -20,7 +21,7 @@
<ObjectGraphDataAnnotationsValidator/> <ObjectGraphDataAnnotationsValidator/>
<div class="form-block"> <div class="form-block">
<h5>Authentication data</h5> <h5>Authentication data@(Constants.RequiredFieldMarkup)</h5>
<AuthDataInput AuthData="requestModel.RegisterRequest.AuthData"/> <AuthDataInput AuthData="requestModel.RegisterRequest.AuthData"/>
</div> </div>
@@ -40,12 +41,12 @@
</div> </div>
<div class="form-block"> <div class="form-block">
<h5>Your passport</h5> <h5>Your passport@(Constants.RequiredFieldMarkup)</h5>
<PassportInput Passport="requestModel.Passport"/> <PassportInput Passport="requestModel.Passport"/>
</div> </div>
<div class="form-block"> <div class="form-block">
<h5>Birth data</h5> <h5>Birth data@(Constants.RequiredFieldMarkup)</h5>
<div > <div >
<label> <label>
Country of birth:<br/> Country of birth:<br/>
@@ -59,14 +60,14 @@
<ValidationMessage For="() => requestModel.CityOfBirth"></ValidationMessage><br/> <ValidationMessage For="() => requestModel.CityOfBirth"></ValidationMessage><br/>
<label> <label>
Birth date:<br/> Birth date:<br/>
<InputDate DisplayName="Birth date" class="rounded" @bind-Value="requestModel.BirthDate" max="@DateTimeProvider.FormattedNow()"/> <InputDate DisplayName="Birth date" class="rounded" @bind-Value="requestModel.BirthDate" max="@formattedMaxBirthdayDate"/>
</label><br/> </label><br/>
<ValidationMessage For="() => requestModel.BirthDate"></ValidationMessage> <ValidationMessage For="() => requestModel.BirthDate"></ValidationMessage>
</div> </div>
</div> </div>
<div class="form-block"> <div class="form-block">
<h5>Citizenship</h5> <h5>Citizenship@(Constants.RequiredFieldMarkup)</h5>
<div > <div >
<label> <label>
Citizenship:<br/> Citizenship:<br/>
@@ -82,14 +83,14 @@
</div> </div>
<div class="form-block"> <div class="form-block">
<h5>Address of your place of work</h5> <h5>Address of your place of work@(Constants.RequiredFieldMarkup)</h5>
<div > <div >
<AddressInput Address="requestModel.PlaceOfWork.Address"/> <AddressInput Address="requestModel.PlaceOfWork.Address"/>
</div> </div>
</div> </div>
<div class="form-block"> <div class="form-block">
<h5>Place of work data</h5> <h5>Place of work data@(Constants.RequiredFieldMarkup)</h5>
<div > <div >
<PlaceOfWorkInput PlaceOfWork="requestModel.PlaceOfWork"/><br/> <PlaceOfWorkInput PlaceOfWork="requestModel.PlaceOfWork"/><br/>
@@ -132,6 +133,7 @@
{ {
private RegisterApplicantRequestModel requestModel = new(); private RegisterApplicantRequestModel requestModel = new();
private Status status = null!; private Status status = null!;
private string formattedMaxBirthdayDate = null!;
[Inject] IValidator<RegisterApplicantRequestModel> RegisterApplicantRequestValidator { get; set; } = null!; [Inject] IValidator<RegisterApplicantRequestModel> RegisterApplicantRequestValidator { get; set; } = null!;
@@ -141,7 +143,8 @@
protected override void OnInitialized() protected override void OnInitialized()
{ {
requestModel.BirthDate = DateTime.Now; requestModel.BirthDate = DateTime.Now.AddYears(-ConfigurationConstraints.ApplicantMinAge);
formattedMaxBirthdayDate = requestModel.BirthDate.ToString("yyyy-MM-dd");
} }
private async void TryRegisterApplicant() private async void TryRegisterApplicant()
@@ -161,7 +164,7 @@
try try
{ {
await Client.RegisterAsync(request); await Client.RegisterAsync(request);
status.SetSucces("Register successful. Now log in."); status.SetSuccess("Register successful. Now log in.");
} }
catch (ApiException<ProblemDetails> e) catch (ApiException<ProblemDetails> e)
{ {

View File

@@ -10,16 +10,22 @@ public class NameModelValidator : AbstractValidator<NameModel>
RuleFor(m => m.FirstName) RuleFor(m => m.FirstName)
.NotEmpty() .NotEmpty()
.WithMessage("First Name can not be empty") .WithMessage("First Name can not be empty")
.Matches(Constants.EnglishWordRegex)
.WithMessage("First name must be in english characters")
.MaximumLength(ConfigurationConstraints.NameLength) .MaximumLength(ConfigurationConstraints.NameLength)
.WithMessage($"First Name length must be less than {ConfigurationConstraints.NameLength}"); .WithMessage($"First Name length must be less than {ConfigurationConstraints.NameLength}");
RuleFor(m => m.Surname) RuleFor(m => m.Surname)
.NotEmpty() .NotEmpty()
.WithMessage("Surname can not be empty") .WithMessage("Surname can not be empty")
.Matches(Constants.EnglishWordRegex)
.WithMessage("Surname must be in english characters")
.MaximumLength(ConfigurationConstraints.NameLength) .MaximumLength(ConfigurationConstraints.NameLength)
.WithMessage($"Surname length must be less than {ConfigurationConstraints.NameLength}"); .WithMessage($"Surname length must be less than {ConfigurationConstraints.NameLength}");
RuleFor(m => m.Patronymic) RuleFor(m => m.Patronymic)
.Matches(Constants.EnglishWordRegex)
.WithMessage("Patronymic must be in english characters")
.MaximumLength(ConfigurationConstraints.NameLength) .MaximumLength(ConfigurationConstraints.NameLength)
.WithMessage($"Patronymic length must be less than {ConfigurationConstraints.NameLength}"); .WithMessage($"Patronymic length must be less than {ConfigurationConstraints.NameLength}");
} }

View File

@@ -11,12 +11,16 @@ public class PassportModelValidator : AbstractValidator<PassportModel>
RuleFor(r => r.Issuer) RuleFor(r => r.Issuer)
.NotEmpty() .NotEmpty()
.WithMessage("Passport issuer can not be empty") .WithMessage("Passport issuer can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Passport issuer field can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.IssuerNameLength) .MaximumLength(ConfigurationConstraints.IssuerNameLength)
.WithMessage($"Passport issuer length must be less than {ConfigurationConstraints.IssuerNameLength}"); .WithMessage($"Passport issuer length must be less than {ConfigurationConstraints.IssuerNameLength}");
RuleFor(r => r.Number) RuleFor(r => r.Number)
.NotEmpty() .NotEmpty()
.WithMessage("Passport number can not be empty") .WithMessage("Passport number can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Passport number field can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.PassportNumberLength) .MaximumLength(ConfigurationConstraints.PassportNumberLength)
.WithMessage($"Passport number length must be less than {ConfigurationConstraints.PassportNumberLength}"); .WithMessage($"Passport number length must be less than {ConfigurationConstraints.PassportNumberLength}");

View File

@@ -10,12 +10,16 @@ public class PlaceOfWorkModelValidator : AbstractValidator<PlaceOfWorkModel>
RuleFor(p => p.Name) RuleFor(p => p.Name)
.NotEmpty() .NotEmpty()
.WithMessage("Place of work name can not be empty") .WithMessage("Place of work name can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Place of work name field can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.PlaceOfWorkNameLength) .MaximumLength(ConfigurationConstraints.PlaceOfWorkNameLength)
.WithMessage($"Place of work name length must be less than {ConfigurationConstraints.PlaceOfWorkNameLength}"); .WithMessage($"Place of work name length must be less than {ConfigurationConstraints.PlaceOfWorkNameLength}");
RuleFor(p => p.PhoneNum) RuleFor(p => p.PhoneNum)
.NotEmpty() .NotEmpty()
.WithMessage("Place of work phone number can not be empty") .WithMessage("Place of work phone number can not be empty")
.Matches(Constants.PhoneNumRegex)
.WithMessage("Place of work phone number field must be valid")
.MaximumLength(ConfigurationConstraints.PhoneNumberLength) .MaximumLength(ConfigurationConstraints.PhoneNumberLength)
.WithMessage( .WithMessage(
$"Phone number length must be in range from {ConfigurationConstraints.PhoneNumberMinLength} to {ConfigurationConstraints.PhoneNumberLength}") $"Phone number length must be in range from {ConfigurationConstraints.PhoneNumberMinLength} to {ConfigurationConstraints.PhoneNumberLength}")
@@ -23,27 +27,39 @@ public class PlaceOfWorkModelValidator : AbstractValidator<PlaceOfWorkModel>
.WithMessage( .WithMessage(
$"Phone number length must be in range from {ConfigurationConstraints.PhoneNumberMinLength} to {ConfigurationConstraints.PhoneNumberLength}"); $"Phone number length must be in range from {ConfigurationConstraints.PhoneNumberMinLength} to {ConfigurationConstraints.PhoneNumberLength}");
RuleFor(p => p.Address)
.NotEmpty()
.WithMessage("Place of work address can not be empty");
RuleFor(p => p.Address.Country) RuleFor(p => p.Address.Country)
.NotEmpty() .NotEmpty()
.WithMessage("Country name of place of work can not be empty") .WithMessage("Country name of place of work can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Place of work Country field can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.CountryNameLength) .MaximumLength(ConfigurationConstraints.CountryNameLength)
.WithMessage($"Country name of place of work length must be less than {ConfigurationConstraints.CountryNameLength}"); .WithMessage($"Country name of place of work length must be less than {ConfigurationConstraints.CountryNameLength}");
RuleFor(p => p.Address.City) RuleFor(p => p.Address.City)
.NotEmpty() .NotEmpty()
.WithMessage("City name of place of work can not be empty") .WithMessage("City name of place of work can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Place of work City field can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.CityNameLength) .MaximumLength(ConfigurationConstraints.CityNameLength)
.WithMessage($"City name of place of work length must be less than {ConfigurationConstraints.CityNameLength}"); .WithMessage($"City name of place of work length must be less than {ConfigurationConstraints.CityNameLength}");
RuleFor(p => p.Address.Street) RuleFor(p => p.Address.Street)
.NotEmpty() .NotEmpty()
.WithMessage("Street name of place of work can not be empty") .WithMessage("Street name of place of work can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Place of work Street field can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.StreetNameLength) .MaximumLength(ConfigurationConstraints.StreetNameLength)
.WithMessage($"Street name of place of work length must be less than {ConfigurationConstraints.StreetNameLength}"); .WithMessage($"Street name of place of work length must be less than {ConfigurationConstraints.StreetNameLength}");
RuleFor(p => p.Address.Building) RuleFor(p => p.Address.Building)
.NotEmpty() .NotEmpty()
.WithMessage("Building of place of work can not be empty") .WithMessage("Building of place of work can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Place of work building field can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.CountryNameLength) .MaximumLength(ConfigurationConstraints.CountryNameLength)
.WithMessage($"Building of place of work length must be less than {ConfigurationConstraints.BuildingNumberLength}"); .WithMessage($"Building of place of work length must be less than {ConfigurationConstraints.BuildingNumberLength}");
} }

View File

@@ -11,23 +11,28 @@ public class RegisterApplicantRequestValidator : AbstractValidator<RegisterAppli
public RegisterApplicantRequestValidator( public RegisterApplicantRequestValidator(
IDateTimeProvider dateTimeProvider, IDateTimeProvider dateTimeProvider,
IValidator<NameModel> nameValidator, IValidator<NameModel> nameValidator,
IValidator<RegisterRequestModel> registerRequestModelValidator, IValidator<RegisterRequestModel> registerRequestValidator,
IValidator<PassportModel> passportValidator, IValidator<PassportModel> passportValidator,
IValidator<PlaceOfWorkModel> placeOfWorkModelValidator) IValidator<PlaceOfWorkModel> placeOfWorkModelValidator)
{ {
RuleFor(r => r.RegisterRequest) RuleFor(r => r.RegisterRequest)
.SetValidator(registerRequestModelValidator); .NotEmpty()
.SetValidator(registerRequestValidator);
RuleFor(r => r.ApplicantName) RuleFor(r => r.ApplicantName)
.NotEmpty()
.SetValidator(nameValidator); .SetValidator(nameValidator);
RuleFor(r => r.FatherName) RuleFor(r => r.FatherName)
.NotEmpty()
.SetValidator(nameValidator); .SetValidator(nameValidator);
RuleFor(r => r.MotherName) RuleFor(r => r.MotherName)
.NotEmpty()
.SetValidator(nameValidator); .SetValidator(nameValidator);
RuleFor(r => r.Passport) RuleFor(r => r.Passport)
.NotEmpty()
.SetValidator(passportValidator); .SetValidator(passportValidator);
RuleFor(r => r.BirthDate) RuleFor(r => r.BirthDate)
@@ -39,38 +44,51 @@ public class RegisterApplicantRequestValidator : AbstractValidator<RegisterAppli
RuleFor(r => r.CountryOfBirth) RuleFor(r => r.CountryOfBirth)
.NotEmpty() .NotEmpty()
.WithMessage("Country of birth can not be empty") .WithMessage("Country of birth can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Country of birth field can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.CountryNameLength) .MaximumLength(ConfigurationConstraints.CountryNameLength)
.WithMessage($"Country of birth name length must be less than {ConfigurationConstraints.CountryNameLength}"); .WithMessage($"Country of birth name length must be less than {ConfigurationConstraints.CountryNameLength}");
RuleFor(r => r.CityOfBirth) RuleFor(r => r.CityOfBirth)
.NotEmpty() .NotEmpty()
.WithMessage("City of birth can not be empty") .WithMessage("City of birth can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("City of birth field can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.CityNameLength) .MaximumLength(ConfigurationConstraints.CityNameLength)
.WithMessage($"City of birth name length must be less than {ConfigurationConstraints.CityNameLength}"); .WithMessage($"City of birth name length must be less than {ConfigurationConstraints.CityNameLength}");
RuleFor(r => r.Citizenship) RuleFor(r => r.Citizenship)
.NotEmpty() .NotEmpty()
.WithMessage("Citizenship can not be empty") .WithMessage("Citizenship can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Citizenship field can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.CitizenshipLength) .MaximumLength(ConfigurationConstraints.CitizenshipLength)
.WithMessage($"Citizenship length must be less than {ConfigurationConstraints.CitizenshipLength}"); .WithMessage($"Citizenship length must be less than {ConfigurationConstraints.CitizenshipLength}");
RuleFor(r => r.CitizenshipByBirth) RuleFor(r => r.CitizenshipByBirth)
.NotEmpty() .NotEmpty()
.WithMessage("Citizenship by birth can not be empty") .WithMessage("Citizenship by birth can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Citizenship by birth field can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.CitizenshipLength) .MaximumLength(ConfigurationConstraints.CitizenshipLength)
.WithMessage($"Citizenship by birth length must be less than {ConfigurationConstraints.CitizenshipLength}"); .WithMessage($"Citizenship by birth length must be less than {ConfigurationConstraints.CitizenshipLength}");
RuleFor(r => r.Gender).IsInEnum(); RuleFor(r => r.Gender)
.IsInEnum();
RuleFor(r => r.MaritalStatus).IsInEnum(); RuleFor(r => r.MaritalStatus)
.IsInEnum();
RuleFor(r => r.JobTitle) RuleFor(r => r.JobTitle)
.NotEmpty() .NotEmpty()
.WithMessage("Title of job can not be empty") .WithMessage("Title of job can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Title of job field can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.JobTitleLength) .MaximumLength(ConfigurationConstraints.JobTitleLength)
.WithMessage($"Title of job length must be less than {ConfigurationConstraints.JobTitleLength}"); .WithMessage($"Title of job length must be less than {ConfigurationConstraints.JobTitleLength}");
RuleFor(r => r.PlaceOfWork) RuleFor(r => r.PlaceOfWork)
.NotEmpty()
.SetValidator(placeOfWorkModelValidator); .SetValidator(placeOfWorkModelValidator);
} }
} }

View File

@@ -18,6 +18,8 @@ public class AuthDataValidator : AbstractValidator<AuthData>
RuleFor(d => d.Password) RuleFor(d => d.Password)
.NotEmpty() .NotEmpty()
.WithMessage("Password can not be empty") .WithMessage("Password can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Password can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.PasswordLength) .MaximumLength(ConfigurationConstraints.PasswordLength)
.WithMessage($"Password length must be less than {ConfigurationConstraints.PasswordLength}"); .WithMessage($"Password length must be less than {ConfigurationConstraints.PasswordLength}");
} }

View File

@@ -4,11 +4,12 @@ using VisaApiClient;
namespace BlazorWebAssemblyVisaApiClient.Validation.Common; namespace BlazorWebAssemblyVisaApiClient.Validation.Common;
public class RegisterRequestModelValidator : AbstractValidator<RegisterRequestModel> public class RegisterRequestValidator : AbstractValidator<RegisterRequestModel>
{ {
public RegisterRequestModelValidator(IValidator<AuthData> authDataValidator) public RegisterRequestValidator(IValidator<AuthData> authDataValidator)
{ {
RuleFor(r => r.AuthData) RuleFor(r => r.AuthData)
.NotEmpty()
.SetValidator(authDataValidator); .SetValidator(authDataValidator);
} }
} }

View File

@@ -7,7 +7,7 @@ namespace BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Models
public class VisaApplicationCreateRequestModel public class VisaApplicationCreateRequestModel
{ {
[ValidateComplexType] [ValidateComplexType]
public ReentryPermitModel? ReentryPermit { get; set; } = new(); public ReentryPermitModel? ReentryPermit { get; set; } = default!;
[Required] [Required]
[MaxLength(ConfigurationConstraints.CountryNameLength)] [MaxLength(ConfigurationConstraints.CountryNameLength)]
@@ -26,11 +26,13 @@ namespace BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Models
[Range(0, ConfigurationConstraints.MaxValidDays)] [Range(0, ConfigurationConstraints.MaxValidDays)]
public int ValidDaysRequested { get; set; } public int ValidDaysRequested { get; set; }
[ValidateComplexType] public PastVisaModel[] PastVisas { get; set; } = default!; [ValidateComplexType]
public List<PastVisaModel> PastVisas { get; set; } = [];
[ValidateComplexType] [ValidateComplexType]
public PermissionToDestCountryModel? PermissionToDestCountry { get; set; } = new(); public PermissionToDestCountryModel? PermissionToDestCountry { get; set; } = default!;
[ValidateComplexType] public PastVisitModel[] PastVisits { get; set; } = default!; [ValidateComplexType]
public List<PastVisitModel> PastVisits { get; set; } = [];
} }
} }

View File

@@ -22,6 +22,10 @@ public class PastVisaModelValidator : AbstractValidator<PastVisaModel>
RuleFor(v => v.Name) RuleFor(v => v.Name)
.NotEmpty() .NotEmpty()
.WithMessage("Name of past visa can not be empty"); .WithMessage("Name of past visa can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Name of past visa can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.VisaNameLength)
.WithMessage($"Past visa name length must be less than {ConfigurationConstraints.VisaNameLength}");
} }
} }

View File

@@ -23,6 +23,8 @@ public class PastVisitModelValidator : AbstractValidator<PastVisitModel>
RuleFor(v => v.DestinationCountry) RuleFor(v => v.DestinationCountry)
.NotEmpty() .NotEmpty()
.WithMessage("Destination Country of past visit can not be null") .WithMessage("Destination Country of past visit can not be null")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Destination Country of past visit can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.CountryNameLength) .MaximumLength(ConfigurationConstraints.CountryNameLength)
.WithMessage($"Destination Country of past visit length must be less than {ConfigurationConstraints.CountryNameLength}"); .WithMessage($"Destination Country of past visit length must be less than {ConfigurationConstraints.CountryNameLength}");
} }

View File

@@ -17,6 +17,8 @@ public class PermissionToDestCountryModelValidator : AbstractValidator<Permissio
RuleFor(p => p!.Issuer) RuleFor(p => p!.Issuer)
.NotEmpty() .NotEmpty()
.WithMessage("Issuer of permission for destination Country can not be empty") .WithMessage("Issuer of permission for destination Country can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Issuer of permission for destination Country can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.IssuerNameLength) .MaximumLength(ConfigurationConstraints.IssuerNameLength)
.WithMessage($"Issuer of permission to destination Country length must be less than {ConfigurationConstraints.IssuerNameLength}"); .WithMessage($"Issuer of permission to destination Country length must be less than {ConfigurationConstraints.IssuerNameLength}");
} }

View File

@@ -0,0 +1,25 @@
using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.DateTimeProvider;
using FluentValidation;
using VisaApiClient;
namespace BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Validators;
public class ReentryPermitModelValidator : AbstractValidator<ReentryPermitModel?>
{
public ReentryPermitModelValidator(IDateTimeProvider dateTimeProvider)
{
RuleFor(p => p!.Number)
.NotEmpty()
.WithMessage("Re-entry permit number can not be empty")
.Matches(Constants.EnglishPhraseRegex)
.WithMessage("Re-entry permit number can contain only english letters, digits and special symbols")
.MaximumLength(ConfigurationConstraints.ReentryPermitNumberLength)
.WithMessage($"Re-entry permit number length must be less than {ConfigurationConstraints.ReentryPermitNumberLength}");
RuleFor(p => p!.ExpirationDate)
.NotEmpty()
.WithMessage("Re-entry permit expiration date can not be empty")
.GreaterThan(dateTimeProvider.Now())
.WithMessage("Re-entry permit must not be expired");
}
}

View File

@@ -1,15 +1,18 @@
using BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Models; using BlazorWebAssemblyVisaApiClient.Infrastructure.Services.UserDataProvider;
using BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Models;
using FluentValidation; using FluentValidation;
using VisaApiClient; using VisaApiClient;
namespace BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Validators; namespace BlazorWebAssemblyVisaApiClient.Validation.VisaApplications.Validators;
public class VisaApplicationCreateRequestModelValidator : AbstractValidator<VisaApplicationCreateRequestModel> public class VisaApplicationCreateRequestValidator : AbstractValidator<VisaApplicationCreateRequestModel>
{ {
public VisaApplicationCreateRequestModelValidator( public VisaApplicationCreateRequestValidator(
IValidator<ReentryPermitModel?> reentryPermitModelValidator,
IValidator<PastVisaModel> pastVisaModelValidator, IValidator<PastVisaModel> pastVisaModelValidator,
IValidator<PermissionToDestCountryModel?> permissionToDestCountryModelValidator, IValidator<PermissionToDestCountryModel?> permissionToDestCountryModelValidator,
IValidator<PastVisitModel> pastVisitModelValidator) IValidator<PastVisitModel> pastVisitModelValidator,
IUserDataProvider userDataProvider)
{ {
RuleFor(r => r.PermissionToDestCountry) RuleFor(r => r.PermissionToDestCountry)
.NotEmpty() .NotEmpty()
@@ -17,6 +20,13 @@ public class VisaApplicationCreateRequestModelValidator : AbstractValidator<Visa
.SetValidator(permissionToDestCountryModelValidator) .SetValidator(permissionToDestCountryModelValidator)
.When(r => r.VisaCategory is VisaCategory.Transit); .When(r => r.VisaCategory is VisaCategory.Transit);
RuleFor(r => r.ReentryPermit)
.NotEmpty()
.WithMessage("Non-residents must provide re-entry permission")
.SetValidator(reentryPermitModelValidator)
.WhenAsync(async (_, _) =>
(await userDataProvider.GetApplicant()).IsNonResident);
RuleFor(r => r.DestinationCountry) RuleFor(r => r.DestinationCountry)
.NotEmpty() .NotEmpty()
.WithMessage("Destination country can not be empty"); .WithMessage("Destination country can not be empty");

View File

@@ -1799,9 +1799,10 @@ namespace VisaApiClient
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
public System.DateTimeOffset IssueDate { get; set; } = default!; public System.DateTimeOffset IssueDate { get; set; } = default!;
[Newtonsoft.Json.JsonProperty("name", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] [Newtonsoft.Json.JsonProperty("name", Required = Newtonsoft.Json.Required.Always)]
[System.ComponentModel.DataAnnotations.StringLength(70)] [System.ComponentModel.DataAnnotations.Required]
public string? Name { get; set; } = default!; [System.ComponentModel.DataAnnotations.StringLength(70, MinimumLength = 1)]
public string Name { get; set; } = default!;
[Newtonsoft.Json.JsonProperty("expirationDate", Required = Newtonsoft.Json.Required.Always)] [Newtonsoft.Json.JsonProperty("expirationDate", Required = Newtonsoft.Json.Required.Always)]
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
@@ -1820,9 +1821,10 @@ namespace VisaApiClient
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
public System.DateTimeOffset EndDate { get; set; } = default!; public System.DateTimeOffset EndDate { get; set; } = default!;
[Newtonsoft.Json.JsonProperty("destinationCountry", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] [Newtonsoft.Json.JsonProperty("destinationCountry", Required = Newtonsoft.Json.Required.Always)]
[System.ComponentModel.DataAnnotations.StringLength(70)] [System.ComponentModel.DataAnnotations.Required]
public string? DestinationCountry { get; set; } = default!; [System.ComponentModel.DataAnnotations.StringLength(70, MinimumLength = 1)]
public string DestinationCountry { get; set; } = default!;
} }
@@ -1833,9 +1835,10 @@ namespace VisaApiClient
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
public System.DateTimeOffset ExpirationDate { get; set; } = default!; public System.DateTimeOffset ExpirationDate { get; set; } = default!;
[Newtonsoft.Json.JsonProperty("issuer", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] [Newtonsoft.Json.JsonProperty("issuer", Required = Newtonsoft.Json.Required.Always)]
[System.ComponentModel.DataAnnotations.StringLength(200)] [System.ComponentModel.DataAnnotations.Required]
public string? Issuer { get; set; } = default!; [System.ComponentModel.DataAnnotations.StringLength(200, MinimumLength = 1)]
public string Issuer { get; set; } = default!;
} }

File diff suppressed because one or more lines are too long