Added models for presentation layer with data annotations
This commit is contained in:
		| @@ -0,0 +1,27 @@ | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using Domains; | ||||
|  | ||||
| namespace ApplicationLayer.Services.Applicants.Models; | ||||
|  | ||||
| public class AddressModel | ||||
| { | ||||
|     /// Country part of address | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.CountryNameLength)] | ||||
|     public string Country { get; set; } = null!; | ||||
|  | ||||
|     /// City part of address | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.CityNameLength)] | ||||
|     public string City { get; set; } = null!; | ||||
|  | ||||
|     /// Street part of address | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.StreetNameLength)] | ||||
|     public string Street { get; set; } = null!; | ||||
|  | ||||
|     /// Building part of address | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.BuildingNumberLength)] | ||||
|     public string Building { get; set; } = null!; | ||||
| } | ||||
| @@ -2,48 +2,49 @@ | ||||
|  | ||||
| namespace ApplicationLayer.Services.Applicants.Models; | ||||
|  | ||||
| /// Model of <see cref="Applicant"/> | ||||
| /// Model of | ||||
| /// <see cref="Applicant" /> | ||||
| public class ApplicantModel | ||||
| { | ||||
|     /// <inheritdoc cref="Applicant.Name"/> | ||||
|     /// <inheritdoc cref="Applicant.Name" /> | ||||
|     public Name Name { get; set; } = null!; | ||||
|  | ||||
|     /// <inheritdoc cref="Applicant.Passport"/> | ||||
|     /// <inheritdoc cref="Applicant.Passport" /> | ||||
|     public Passport Passport { get; set; } = null!; | ||||
|  | ||||
|     /// <inheritdoc cref="Applicant.BirthDate"/> | ||||
|     /// <inheritdoc cref="Applicant.BirthDate" /> | ||||
|     public DateTime BirthDate { get; set; } | ||||
|  | ||||
|     /// <inheritdoc cref="Applicant.CountryOfBirth"/> | ||||
|     /// <inheritdoc cref="Applicant.CountryOfBirth" /> | ||||
|     public string CountryOfBirth { get; set; } = null!; | ||||
|  | ||||
|     /// <inheritdoc cref="Applicant.CityOfBirth"/> | ||||
|     /// <inheritdoc cref="Applicant.CityOfBirth" /> | ||||
|     public string CityOfBirth { get; set; } = null!; | ||||
|  | ||||
|     /// <inheritdoc cref="Applicant.Citizenship"/> | ||||
|     /// <inheritdoc cref="Applicant.Citizenship" /> | ||||
|     public string Citizenship { get; set; } = null!; | ||||
|  | ||||
|     /// <inheritdoc cref="Applicant.CitizenshipByBirth"/> | ||||
|     /// <inheritdoc cref="Applicant.CitizenshipByBirth" /> | ||||
|     public string CitizenshipByBirth { get; set; } = null!; | ||||
|  | ||||
|     /// <inheritdoc cref="Applicant.Gender"/> | ||||
|     /// <inheritdoc cref="Applicant.Gender" /> | ||||
|     public Gender Gender { get; set; } | ||||
|  | ||||
|     /// <inheritdoc cref="Applicant.MaritalStatus"/> | ||||
|     /// <inheritdoc cref="Applicant.MaritalStatus" /> | ||||
|     public MaritalStatus MaritalStatus { get; set; } | ||||
|  | ||||
|     /// <inheritdoc cref="Applicant.FatherName"/> | ||||
|     /// <inheritdoc cref="Applicant.FatherName" /> | ||||
|     public Name FatherName { get; set; } = null!; | ||||
|  | ||||
|     /// <inheritdoc cref="Applicant.MotherName"/> | ||||
|     /// <inheritdoc cref="Applicant.MotherName" /> | ||||
|     public Name MotherName { get; set; } = null!; | ||||
|  | ||||
|     /// <inheritdoc cref="Applicant.JobTitle"/> | ||||
|     /// <inheritdoc cref="Applicant.JobTitle" /> | ||||
|     public string JobTitle { get; set; } = null!; | ||||
|  | ||||
|     /// <inheritdoc cref="Applicant.PlaceOfWork"/> | ||||
|     /// <inheritdoc cref="Applicant.PlaceOfWork" /> | ||||
|     public PlaceOfWork PlaceOfWork { get; set; } = null!; | ||||
|  | ||||
|     /// <inheritdoc cref="Applicant.IsNonResident"/> | ||||
|     /// <inheritdoc cref="Applicant.IsNonResident" /> | ||||
|     public bool IsNonResident { get; set; } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,19 @@ | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using Domains; | ||||
|  | ||||
| namespace ApplicationLayer.Services.Applicants.Models; | ||||
|  | ||||
| /// Model of name for presentation layer | ||||
| public class NameModel | ||||
| { | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.NameLength)] | ||||
|     public string FirstName { get; set; } = null!; | ||||
|  | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.NameLength)] | ||||
|     public string Surname { get; set; } = null!; | ||||
|  | ||||
|     [MaxLength(ConfigurationConstraints.NameLength)] | ||||
|     public string? Patronymic { get; set; } | ||||
| } | ||||
| @@ -0,0 +1,26 @@ | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using Domains; | ||||
|  | ||||
| namespace ApplicationLayer.Services.Applicants.Models; | ||||
|  | ||||
| /// Model of passport fpr presentation layer | ||||
| public class PassportModel | ||||
| { | ||||
|     /// Number of passport | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.PassportNumberLength)] | ||||
|     public string Number { get; set; } = null!; | ||||
|  | ||||
|     /// Issuing authority of passport | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.IssuerNameLength)] | ||||
|     public string Issuer { get; set; } = null!; | ||||
|  | ||||
|     /// Date of issue | ||||
|     [Required] | ||||
|     public DateTime IssueDate { get; set; } | ||||
|  | ||||
|     /// Date when the passport expires | ||||
|     [Required] | ||||
|     public DateTime ExpirationDate { get; set; } | ||||
| } | ||||
| @@ -1,15 +1,22 @@ | ||||
| using Domains.ApplicantDomain; | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using Domains; | ||||
|  | ||||
| namespace ApplicationLayer.Services.Applicants.Models; | ||||
|  | ||||
| public class PlaceOfWorkModel | ||||
| { | ||||
|     /// Name of hirer | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.PlaceOfWorkNameLength)] | ||||
|     public string Name { get; set; } = null!; | ||||
|  | ||||
|     /// Address of hirer | ||||
|     public Address Address { get; set; } = null!; | ||||
|     [Required] | ||||
|     public AddressModel Address { get; set; } = null!; | ||||
|  | ||||
|     /// Phone number of hirer | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.PhoneNumberLength)] | ||||
|     [MinLength(ConfigurationConstraints.PhoneNumberMinLength)] | ||||
|     public string PhoneNum { get; set; } = null!; | ||||
| } | ||||
| @@ -0,0 +1,26 @@ | ||||
| using Domains; | ||||
| using FluentValidation; | ||||
|  | ||||
| namespace ApplicationLayer.Services.Applicants.Models.Validation; | ||||
|  | ||||
| public class NameModelValidator : AbstractValidator<NameModel> | ||||
| { | ||||
|     public NameModelValidator() | ||||
|     { | ||||
|         RuleFor(m => m.FirstName) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("First Name can not be empty") | ||||
|             .MaximumLength(ConfigurationConstraints.NameLength) | ||||
|             .WithMessage($"First Name length must be less than {ConfigurationConstraints.NameLength}"); | ||||
|  | ||||
|         RuleFor(m => m.Surname) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Surname can not be empty") | ||||
|             .MaximumLength(ConfigurationConstraints.NameLength) | ||||
|             .WithMessage($"Surname length must be less than {ConfigurationConstraints.NameLength}"); | ||||
|  | ||||
|         RuleFor(m => m.Patronymic) | ||||
|             .MaximumLength(ConfigurationConstraints.NameLength) | ||||
|             .WithMessage($"Patronymic length must be less than {ConfigurationConstraints.NameLength}"); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,35 @@ | ||||
| using ApplicationLayer.InfrastructureServicesInterfaces; | ||||
| using Domains; | ||||
| using FluentValidation; | ||||
|  | ||||
| namespace ApplicationLayer.Services.Applicants.Models.Validation; | ||||
|  | ||||
| public class PassportModelValidator : AbstractValidator<PassportModel> | ||||
| { | ||||
|     public PassportModelValidator(IDateTimeProvider dateTimeProvider) | ||||
|     { | ||||
|         RuleFor(r => r.Issuer) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Passport issuer can not be empty") | ||||
|             .MaximumLength(ConfigurationConstraints.IssuerNameLength) | ||||
|             .WithMessage($"Passport issuer length must be less than {ConfigurationConstraints.IssuerNameLength}"); | ||||
|  | ||||
|         RuleFor(r => r.Number) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Passport number can not be empty") | ||||
|             .MaximumLength(ConfigurationConstraints.PassportNumberLength) | ||||
|             .WithMessage($"Passport number length must be less than {ConfigurationConstraints.PassportNumberLength}"); | ||||
|  | ||||
|         RuleFor(r => r.ExpirationDate) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Passport expiration date can not be empty") | ||||
|             .GreaterThan(dateTimeProvider.Now()) | ||||
|             .WithMessage("Can not approve visa for applicants with expired passport"); | ||||
|  | ||||
|         RuleFor(r => r.IssueDate) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Passport issue date can not be empty") | ||||
|             .LessThanOrEqualTo(dateTimeProvider.Now()) | ||||
|             .WithMessage("Passport issue date must be in past"); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,50 @@ | ||||
| using Domains; | ||||
| using FluentValidation; | ||||
|  | ||||
| namespace ApplicationLayer.Services.Applicants.Models.Validation; | ||||
|  | ||||
| public class PlaceOfWorkModelValidator : AbstractValidator<PlaceOfWorkModel> | ||||
| { | ||||
|     public PlaceOfWorkModelValidator() | ||||
|     { | ||||
|         RuleFor(p => p.Name) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Place of work name can not be empty") | ||||
|             .MaximumLength(ConfigurationConstraints.PlaceOfWorkNameLength) | ||||
|             .WithMessage($"Place of work name length must be less than {ConfigurationConstraints.PlaceOfWorkNameLength}"); | ||||
|  | ||||
|         RuleFor(p => p.PhoneNum) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Place of work phone number can not be empty") | ||||
|             .MaximumLength(ConfigurationConstraints.PhoneNumberLength) | ||||
|             .WithMessage( | ||||
|                 $"Phone number length must be in range from {ConfigurationConstraints.PhoneNumberMinLength} to {ConfigurationConstraints.PhoneNumberLength}") | ||||
|             .MinimumLength(ConfigurationConstraints.PhoneNumberMinLength) | ||||
|             .WithMessage( | ||||
|                 $"Phone number length must be in range from {ConfigurationConstraints.PhoneNumberMinLength} to {ConfigurationConstraints.PhoneNumberLength}"); | ||||
|  | ||||
|         RuleFor(p => p.Address.Country) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Country name of place of work can not be empty") | ||||
|             .MaximumLength(ConfigurationConstraints.CountryNameLength) | ||||
|             .WithMessage($"Country name of place of work length must be less than {ConfigurationConstraints.CountryNameLength}"); | ||||
|  | ||||
|         RuleFor(p => p.Address.City) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("City name of place of work can not be empty") | ||||
|             .MaximumLength(ConfigurationConstraints.CityNameLength) | ||||
|             .WithMessage($"City name of place of work length must be less than {ConfigurationConstraints.CityNameLength}"); | ||||
|  | ||||
|         RuleFor(p => p.Address.Street) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Street name of place of work can not be empty") | ||||
|             .MaximumLength(ConfigurationConstraints.StreetNameLength) | ||||
|             .WithMessage($"Street name of place of work length must be less than {ConfigurationConstraints.StreetNameLength}"); | ||||
|  | ||||
|         RuleFor(p => p.Address.Building) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Building of place of work can not be empty") | ||||
|             .MaximumLength(ConfigurationConstraints.CountryNameLength) | ||||
|             .WithMessage($"Building of place of work length must be less than {ConfigurationConstraints.BuildingNumberLength}"); | ||||
|     } | ||||
| } | ||||
| @@ -1,3 +1,15 @@ | ||||
| namespace ApplicationLayer.Services.AuthServices.Common; | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using Domains; | ||||
|  | ||||
| public record AuthData(string Email, string Password); | ||||
| namespace ApplicationLayer.Services.AuthServices.Common; | ||||
|  | ||||
| public class AuthData | ||||
| { | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.EmailLength)] | ||||
|     public string Email { get; set; } = null!; | ||||
|  | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.PasswordLength)] | ||||
|     public string Password { get; set; } = null!; | ||||
| } | ||||
|   | ||||
| @@ -1,26 +1,27 @@ | ||||
| using ApplicationLayer.Services.AuthServices.LoginService.Exceptions; | ||||
| using ApplicationLayer.Services.AuthServices.NeededServices; | ||||
| using ApplicationLayer.Services.AuthServices.Requests; | ||||
| using Domains.Users; | ||||
|  | ||||
| namespace ApplicationLayer.Services.AuthServices.LoginService; | ||||
|  | ||||
| public class DevelopmentLoginService(IUsersRepository users, ITokenGenerator tokenGenerator) : ILoginService | ||||
| { | ||||
|     async Task<string> ILoginService.LoginAsync(string email, string password, CancellationToken cancellationToken) | ||||
|     async Task<string> ILoginService.LoginAsync(LoginRequest request, CancellationToken cancellationToken) | ||||
|     { | ||||
|             if (email == "admin@mail.ru" && password == "admin") | ||||
|             { | ||||
|                 var admin = new User { Role = Role.Admin }; | ||||
|         if (request.AuthData is { Email: "admin@mail.ru", Password: "admin" }) | ||||
|         { | ||||
|             var admin = new User { Role = Role.Admin }; | ||||
|  | ||||
|                 return tokenGenerator.CreateToken(admin); | ||||
|             } | ||||
|  | ||||
|             var user = await users.FindByEmailAsync(email, cancellationToken); | ||||
|             if (user is null || user.Password != password) | ||||
|             { | ||||
|                 throw new IncorrectLoginDataException(); | ||||
|             } | ||||
|  | ||||
|             return tokenGenerator.CreateToken(user); | ||||
|             return tokenGenerator.CreateToken(admin); | ||||
|         } | ||||
| } | ||||
|  | ||||
|         var user = await users.FindByEmailAsync(request.AuthData.Email, cancellationToken); | ||||
|         if (user is null || user.Password != request.AuthData.Password) | ||||
|         { | ||||
|             throw new IncorrectLoginDataException(); | ||||
|         } | ||||
|  | ||||
|         return tokenGenerator.CreateToken(user); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,9 +1,11 @@ | ||||
| namespace ApplicationLayer.Services.AuthServices.LoginService; | ||||
| using ApplicationLayer.Services.AuthServices.Requests; | ||||
|  | ||||
| namespace ApplicationLayer.Services.AuthServices.LoginService; | ||||
|  | ||||
| /// Handles login requests | ||||
| public interface ILoginService | ||||
| { | ||||
|     /// Handle login request | ||||
|     /// <returns>JWT-token</returns> | ||||
|     Task<string> LoginAsync(string email, string password, CancellationToken cancellationToken); | ||||
| } | ||||
|     Task<string> LoginAsync(LoginRequest request, CancellationToken cancellationToken); | ||||
| } | ||||
|   | ||||
| @@ -1,19 +1,20 @@ | ||||
| using ApplicationLayer.Services.AuthServices.LoginService.Exceptions; | ||||
| using ApplicationLayer.Services.AuthServices.NeededServices; | ||||
| using ApplicationLayer.Services.AuthServices.Requests; | ||||
|  | ||||
| namespace ApplicationLayer.Services.AuthServices.LoginService; | ||||
|  | ||||
| /// <inheritdoc cref="ILoginService"/> | ||||
| /// <inheritdoc cref="ILoginService" /> | ||||
| public class LoginService(IUsersRepository users, ITokenGenerator tokenGenerator) : ILoginService | ||||
| { | ||||
|     async Task<string> ILoginService.LoginAsync(string email, string password, CancellationToken cancellationToken) | ||||
|     async Task<string> ILoginService.LoginAsync(LoginRequest request, CancellationToken cancellationToken) | ||||
|     { | ||||
|             var user = await users.FindByEmailAsync(email, cancellationToken); | ||||
|             if (user is null || user.Password != password) | ||||
|             { | ||||
|                 throw new IncorrectLoginDataException(); | ||||
|             } | ||||
|  | ||||
|             return tokenGenerator.CreateToken(user); | ||||
|         var user = await users.FindByEmailAsync(request.AuthData.Email, cancellationToken); | ||||
|         if (user is null || user.Password != request.AuthData.Password) | ||||
|         { | ||||
|             throw new IncorrectLoginDataException(); | ||||
|         } | ||||
| } | ||||
|  | ||||
|         return tokenGenerator.CreateToken(user); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -17,7 +17,7 @@ public class RegisterService( | ||||
| { | ||||
|     async Task IRegisterService.RegisterApplicant(RegisterApplicantRequest request, CancellationToken cancellationToken) | ||||
|     { | ||||
|         var user = mapper.Map<User>(request.AuthData); | ||||
|         var user = mapper.Map<User>(request.RegisterRequest.AuthData); | ||||
|         user.Role = Role.Applicant; | ||||
|  | ||||
|         var applicant = mapper.Map<Applicant>(request); | ||||
|   | ||||
| @@ -0,0 +1,9 @@ | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using ApplicationLayer.Services.AuthServices.Common; | ||||
|  | ||||
| namespace ApplicationLayer.Services.AuthServices.Requests; | ||||
|  | ||||
| public class LoginRequest | ||||
| { | ||||
|     [Required] public AuthData AuthData { get; set; } = null!; | ||||
| } | ||||
| @@ -1,22 +1,49 @@ | ||||
| using ApplicationLayer.Services.Applicants.Models; | ||||
| using ApplicationLayer.Services.AuthServices.Common; | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using ApplicationLayer.Services.Applicants.Models; | ||||
| using Domains; | ||||
| using Domains.ApplicantDomain; | ||||
|  | ||||
| namespace ApplicationLayer.Services.AuthServices.Requests; | ||||
|  | ||||
| public record RegisterApplicantRequest( | ||||
|     AuthData AuthData, | ||||
|     Name ApplicantName, | ||||
|     Passport Passport, | ||||
|     DateTime BirthDate, | ||||
|     string CityOfBirth, | ||||
|     string CountryOfBirth, | ||||
|     string Citizenship, | ||||
|     string CitizenshipByBirth, | ||||
|     Gender Gender, | ||||
|     MaritalStatus MaritalStatus, | ||||
|     Name FatherName, | ||||
|     Name MotherName, | ||||
|     string JobTitle, | ||||
|     PlaceOfWorkModel PlaceOfWork, | ||||
|     bool IsNonResident) : RegisterRequest(AuthData); | ||||
| public record RegisterApplicantRequest | ||||
| { | ||||
|     [Required] public RegisterRequest RegisterRequest { get; set; } = null!; | ||||
|  | ||||
|     [Required] public NameModel ApplicantName { get; set; } = null!; | ||||
|  | ||||
|     [Required] public PassportModel Passport { get; set; } = null!; | ||||
|  | ||||
|     [Required] public DateTime BirthDate { get; set; } | ||||
|  | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.CityNameLength)] | ||||
|     public string CityOfBirth { get; set; } = null!; | ||||
|  | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.CountryNameLength)] | ||||
|     public string CountryOfBirth { get; set; } = null!; | ||||
|  | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.CitizenshipLength)] | ||||
|     public string Citizenship { get; set; } = null!; | ||||
|  | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.CitizenshipLength)] | ||||
|     public string CitizenshipByBirth { get; set; } = null!; | ||||
|  | ||||
|     [Required] public Gender Gender { get; set; } | ||||
|  | ||||
|     [Required] public MaritalStatus MaritalStatus { get; set; } | ||||
|  | ||||
|     [Required] public NameModel FatherName { get; set; } = null!; | ||||
|  | ||||
|     [Required] public NameModel MotherName { get; set; } = null!; | ||||
|  | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.JobTitleLength)] | ||||
|     public string JobTitle { get; set; } = null!; | ||||
|  | ||||
|     [Required] public PlaceOfWorkModel PlaceOfWork { get; set; } = null!; | ||||
|  | ||||
|     [Required] public bool IsNonResident { get; set; } | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,9 @@ | ||||
| using ApplicationLayer.Services.AuthServices.Common; | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using ApplicationLayer.Services.AuthServices.Common; | ||||
|  | ||||
| namespace ApplicationLayer.Services.AuthServices.Requests; | ||||
|  | ||||
| public record RegisterRequest(AuthData AuthData); | ||||
| public class RegisterRequest | ||||
| { | ||||
|     [Required] public AuthData AuthData { get; set; } = null!; | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| using ApplicationLayer.Services.AuthServices.Common; | ||||
| using ApplicationLayer.Services.AuthServices.NeededServices; | ||||
| using Domains; | ||||
| using FluentValidation; | ||||
|  | ||||
| @@ -7,25 +6,20 @@ namespace ApplicationLayer.Services.AuthServices.Requests.Validation; | ||||
|  | ||||
| public class AuthDataValidator : AbstractValidator<AuthData> | ||||
| { | ||||
|     public AuthDataValidator(IUsersRepository users) | ||||
|     public AuthDataValidator() | ||||
|     { | ||||
|             RuleFor(d => d.Email) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("Email can not be empty") | ||||
|                 .EmailAddress() | ||||
|                 .WithMessage("Email must be valid") | ||||
|                 .MaximumLength(ConfigurationConstraints.EmailLength) | ||||
|                 .WithMessage($"Email length must be less than {ConfigurationConstraints.EmailLength}") | ||||
|                 .MustAsync(async (email, ct) => | ||||
|                 { | ||||
|                     return await users.FindByEmailAsync(email, ct) is null; | ||||
|                 }) | ||||
|                 .WithMessage("Email already exists"); | ||||
|         RuleFor(d => d.Email) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Email can not be empty") | ||||
|             .EmailAddress() | ||||
|             .WithMessage("Email must be valid") | ||||
|             .MaximumLength(ConfigurationConstraints.EmailLength) | ||||
|             .WithMessage($"Email length must be less than {ConfigurationConstraints.EmailLength}"); | ||||
|  | ||||
|             RuleFor(d => d.Password) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("Password can not be empty") | ||||
|                 .MaximumLength(ConfigurationConstraints.PasswordLength) | ||||
|                 .WithMessage($"Password length must be less than {ConfigurationConstraints.PasswordLength}"); | ||||
|         } | ||||
| } | ||||
|         RuleFor(d => d.Password) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Password can not be empty") | ||||
|             .MaximumLength(ConfigurationConstraints.PasswordLength) | ||||
|             .WithMessage($"Password length must be less than {ConfigurationConstraints.PasswordLength}"); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,27 +0,0 @@ | ||||
| using Domains; | ||||
| using Domains.ApplicantDomain; | ||||
| using FluentValidation; | ||||
|  | ||||
| namespace ApplicationLayer.Services.AuthServices.Requests.Validation; | ||||
|  | ||||
| public class NameValidator : AbstractValidator<Name> | ||||
| { | ||||
|     public NameValidator() | ||||
|     { | ||||
|             RuleFor(m => m.FirstName) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("First Name can not be empty") | ||||
|                 .MaximumLength(ConfigurationConstraints.NameLength) | ||||
|                 .WithMessage($"First Name length must be less than {ConfigurationConstraints.NameLength}"); | ||||
|  | ||||
|             RuleFor(m => m.Surname) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("Surname can not be empty") | ||||
|                 .MaximumLength(ConfigurationConstraints.NameLength) | ||||
|                 .WithMessage($"Surname length must be less than {ConfigurationConstraints.NameLength}"); | ||||
|  | ||||
|             RuleFor(m => m.Patronymic) | ||||
|                 .MaximumLength(ConfigurationConstraints.NameLength) | ||||
|                 .WithMessage($"Patronymic length must be less than {ConfigurationConstraints.NameLength}"); | ||||
|         } | ||||
| } | ||||
| @@ -1,36 +0,0 @@ | ||||
| using ApplicationLayer.InfrastructureServicesInterfaces; | ||||
| using Domains; | ||||
| using Domains.ApplicantDomain; | ||||
| using FluentValidation; | ||||
|  | ||||
| namespace ApplicationLayer.Services.AuthServices.Requests.Validation; | ||||
|  | ||||
| public class PassportValidator : AbstractValidator<Passport> | ||||
| { | ||||
|     public PassportValidator(IDateTimeProvider dateTimeProvider) | ||||
|     { | ||||
|             RuleFor(r => r.Issuer) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("Passport issuer can not be empty") | ||||
|                 .MaximumLength(ConfigurationConstraints.IssuerNameLength) | ||||
|                 .WithMessage($"Passport issuer length must be less than {ConfigurationConstraints.IssuerNameLength}"); | ||||
|  | ||||
|             RuleFor(r => r.Number) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("Passport number can not be empty") | ||||
|                 .MaximumLength(ConfigurationConstraints.PassportNumberLength) | ||||
|                 .WithMessage($"Passport number length must be less than {ConfigurationConstraints.PassportNumberLength}"); | ||||
|  | ||||
|             RuleFor(r => r.ExpirationDate) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("Passport expiration date can not be empty") | ||||
|                 .GreaterThan(dateTimeProvider.Now()) | ||||
|                 .WithMessage("Can not approve visa for applicants with expired passport"); | ||||
|  | ||||
|             RuleFor(r => r.IssueDate) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("Passport issue date can not be empty") | ||||
|                 .LessThanOrEqualTo(dateTimeProvider.Now()) | ||||
|                 .WithMessage("Passport issue date must be in past"); | ||||
|         } | ||||
| } | ||||
| @@ -1,49 +0,0 @@ | ||||
| using ApplicationLayer.Services.Applicants.Models; | ||||
| using Domains; | ||||
| using FluentValidation; | ||||
|  | ||||
| namespace ApplicationLayer.Services.AuthServices.Requests.Validation; | ||||
|  | ||||
| public class PlaceOfWorkModelValidator : AbstractValidator<PlaceOfWorkModel> | ||||
| { | ||||
|     public PlaceOfWorkModelValidator() | ||||
|     { | ||||
|             RuleFor(p => p.Name) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("Place of work name can not be empty") | ||||
|                 .MaximumLength(ConfigurationConstraints.PlaceOfWorkNameLength) | ||||
|                 .WithMessage($"Place of work name length must be less than {ConfigurationConstraints.PlaceOfWorkNameLength}"); | ||||
|  | ||||
|             RuleFor(p => p.PhoneNum) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("Place of work phone number can not be empty") | ||||
|                 .MaximumLength(ConfigurationConstraints.PhoneNumberLength) | ||||
|                 .WithMessage($"Phone number length must be in range from {ConfigurationConstraints.PhoneNumberMinLength} to {ConfigurationConstraints.PhoneNumberLength}") | ||||
|                 .MinimumLength(ConfigurationConstraints.PhoneNumberMinLength) | ||||
|                 .WithMessage($"Phone number length must be in range from {ConfigurationConstraints.PhoneNumberMinLength} to {ConfigurationConstraints.PhoneNumberLength}"); | ||||
|  | ||||
|             RuleFor(p => p.Address.Country) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("Country name of place of work can not be empty") | ||||
|                 .MaximumLength(ConfigurationConstraints.CountryNameLength) | ||||
|                 .WithMessage($"Country name of place of work length must be less than {ConfigurationConstraints.CountryNameLength}"); | ||||
|  | ||||
|             RuleFor(p => p.Address.City) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("City name of place of work can not be empty") | ||||
|                 .MaximumLength(ConfigurationConstraints.CityNameLength) | ||||
|                 .WithMessage($"City name of place of work length must be less than {ConfigurationConstraints.CityNameLength}"); | ||||
|  | ||||
|             RuleFor(p => p.Address.Street) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("Street name of place of work can not be empty") | ||||
|                 .MaximumLength(ConfigurationConstraints.StreetNameLength) | ||||
|                 .WithMessage($"Street name of place of work length must be less than {ConfigurationConstraints.StreetNameLength}"); | ||||
|  | ||||
|             RuleFor(p => p.Address.Building) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("Building of place of work can not be empty") | ||||
|                 .MaximumLength(ConfigurationConstraints.CountryNameLength) | ||||
|                 .WithMessage($"Building of place of work length must be less than {ConfigurationConstraints.BuildingNumberLength}"); | ||||
|         } | ||||
| } | ||||
| @@ -1,8 +1,6 @@ | ||||
| using ApplicationLayer.InfrastructureServicesInterfaces; | ||||
| using ApplicationLayer.Services.Applicants.Models; | ||||
| using ApplicationLayer.Services.AuthServices.Common; | ||||
| using Domains; | ||||
| using Domains.ApplicantDomain; | ||||
| using FluentValidation; | ||||
|  | ||||
| namespace ApplicationLayer.Services.AuthServices.Requests.Validation; | ||||
| @@ -11,67 +9,67 @@ public class RegisterApplicantRequestValidator : AbstractValidator<RegisterAppli | ||||
| { | ||||
|     public RegisterApplicantRequestValidator( | ||||
|         IDateTimeProvider dateTimeProvider, | ||||
|         IValidator<Name> nameValidator, | ||||
|         IValidator<AuthData> authDataValidator, | ||||
|         IValidator<Passport> passportValidator, | ||||
|         IValidator<NameModel> nameValidator, | ||||
|         IValidator<RegisterRequest> registerRequestValidator, | ||||
|         IValidator<PassportModel> passportValidator, | ||||
|         IValidator<PlaceOfWorkModel> placeOfWorkModelValidator) | ||||
|     { | ||||
|             RuleFor(r => r.AuthData) | ||||
|                 .SetValidator(authDataValidator); | ||||
|         RuleFor(r => r.RegisterRequest) | ||||
|             .SetValidator(registerRequestValidator); | ||||
|  | ||||
|             RuleFor(r => r.ApplicantName) | ||||
|                 .SetValidator(nameValidator); | ||||
|         RuleFor(r => r.ApplicantName) | ||||
|             .SetValidator(nameValidator); | ||||
|  | ||||
|             RuleFor(r => r.FatherName) | ||||
|                 .SetValidator(nameValidator); | ||||
|         RuleFor(r => r.FatherName) | ||||
|             .SetValidator(nameValidator); | ||||
|  | ||||
|             RuleFor(r => r.MotherName) | ||||
|                 .SetValidator(nameValidator); | ||||
|         RuleFor(r => r.MotherName) | ||||
|             .SetValidator(nameValidator); | ||||
|  | ||||
|             RuleFor(r => r.Passport) | ||||
|                 .SetValidator(passportValidator); | ||||
|         RuleFor(r => r.Passport) | ||||
|             .SetValidator(passportValidator); | ||||
|  | ||||
|             RuleFor(r => r.BirthDate) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("Birth date can not be empty") | ||||
|                 .LessThanOrEqualTo(dateTimeProvider.Now().AddYears(-ConfigurationConstraints.ApplicantMinAge)) | ||||
|                 .WithMessage($"Applicant must be older than {ConfigurationConstraints.ApplicantMinAge}"); | ||||
|         RuleFor(r => r.BirthDate) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Birth date can not be empty") | ||||
|             .LessThanOrEqualTo(dateTimeProvider.Now().AddYears(-ConfigurationConstraints.ApplicantMinAge)) | ||||
|             .WithMessage($"Applicant must be older than {ConfigurationConstraints.ApplicantMinAge}"); | ||||
|  | ||||
|             RuleFor(r => r.CountryOfBirth) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("Country of birth can not be empty") | ||||
|                 .MaximumLength(ConfigurationConstraints.CountryNameLength) | ||||
|                 .WithMessage($"Country of birth name length must be less than {ConfigurationConstraints.CountryNameLength}"); | ||||
|         RuleFor(r => r.CountryOfBirth) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Country of birth can not be empty") | ||||
|             .MaximumLength(ConfigurationConstraints.CountryNameLength) | ||||
|             .WithMessage($"Country of birth name length must be less than {ConfigurationConstraints.CountryNameLength}"); | ||||
|  | ||||
|             RuleFor(r => r.CityOfBirth) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("City of birth can not be empty") | ||||
|                 .MaximumLength(ConfigurationConstraints.CityNameLength) | ||||
|                 .WithMessage($"City of birth name length must be less than {ConfigurationConstraints.CityNameLength}"); | ||||
|         RuleFor(r => r.CityOfBirth) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("City of birth can not be empty") | ||||
|             .MaximumLength(ConfigurationConstraints.CityNameLength) | ||||
|             .WithMessage($"City of birth name length must be less than {ConfigurationConstraints.CityNameLength}"); | ||||
|  | ||||
|             RuleFor(r => r.Citizenship) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("Citizenship can not be empty") | ||||
|                 .MaximumLength(ConfigurationConstraints.CitizenshipLength) | ||||
|                 .WithMessage($"Citizenship length must be less than {ConfigurationConstraints.CitizenshipLength}"); | ||||
|         RuleFor(r => r.Citizenship) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Citizenship can not be empty") | ||||
|             .MaximumLength(ConfigurationConstraints.CitizenshipLength) | ||||
|             .WithMessage($"Citizenship length must be less than {ConfigurationConstraints.CitizenshipLength}"); | ||||
|  | ||||
|             RuleFor(r => r.CitizenshipByBirth) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("Citizenship by birth can not be empty") | ||||
|                 .MaximumLength(ConfigurationConstraints.CitizenshipLength) | ||||
|                 .WithMessage($"Citizenship by birth length must be less than {ConfigurationConstraints.CitizenshipLength}"); | ||||
|         RuleFor(r => r.CitizenshipByBirth) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Citizenship by birth can not be empty") | ||||
|             .MaximumLength(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) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("Title of job can not be empty") | ||||
|                 .MaximumLength(ConfigurationConstraints.JobTitleLength) | ||||
|                 .WithMessage($"Title of job length must be less than {ConfigurationConstraints.JobTitleLength}"); | ||||
|         RuleFor(r => r.JobTitle) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Title of job can not be empty") | ||||
|             .MaximumLength(ConfigurationConstraints.JobTitleLength) | ||||
|             .WithMessage($"Title of job length must be less than {ConfigurationConstraints.JobTitleLength}"); | ||||
|  | ||||
|             RuleFor(r => r.PlaceOfWork) | ||||
|                 .SetValidator(placeOfWorkModelValidator); | ||||
|         } | ||||
| } | ||||
|         RuleFor(r => r.PlaceOfWork) | ||||
|             .SetValidator(placeOfWorkModelValidator); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,16 @@ | ||||
| using ApplicationLayer.Services.AuthServices.Common; | ||||
| using ApplicationLayer.Services.AuthServices.NeededServices; | ||||
| using FluentValidation; | ||||
|  | ||||
| namespace ApplicationLayer.Services.AuthServices.Requests.Validation; | ||||
|  | ||||
| public class RegisterRequestValidator : AbstractValidator<RegisterRequest> | ||||
| { | ||||
|     public RegisterRequestValidator(IUsersRepository users, IValidator<AuthData> authDataValidator) | ||||
|     { | ||||
|         RuleFor(r => r.AuthData) | ||||
|             .SetValidator(authDataValidator) | ||||
|             .MustAsync(async (authData, ct) => await users.FindByEmailAsync(authData.Email, ct) is null) | ||||
|             .WithMessage("Email already exists"); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,6 @@ | ||||
| using ApplicationLayer.Services.GeneralExceptions; | ||||
| using Domains.Users; | ||||
|  | ||||
| namespace ApplicationLayer.Services.Users.Exceptions; | ||||
|  | ||||
| public class WrongRoleException(Guid userId) : EntityNotFoundByIdException<User>(userId); | ||||
| @@ -10,13 +10,13 @@ public interface IUsersService | ||||
|     /// <param name="cancellationToken">Cancellation token</param> | ||||
|     Task<List<User>> GetAuthoritiesAccountsAsync(CancellationToken cancellationToken); | ||||
|  | ||||
|     /// Changes authentication data for an account | ||||
|     /// Changes authentication data for an authority account | ||||
|     /// <param name="request"> Request object with identifier of user and new authentication data</param> | ||||
|     /// <param name="cancellationToken">Cancellation token</param> | ||||
|     Task ChangeAccountAuthDataAsync(ChangeUserAuthDataRequest request, CancellationToken cancellationToken); | ||||
|     Task ChangeAuthorityAuthDataAsync(ChangeUserAuthDataRequest request, CancellationToken cancellationToken); | ||||
|  | ||||
|     /// Removes user account | ||||
|     /// Removes account of authority | ||||
|     /// <param name="userId">Identifier of account</param> | ||||
|     /// <param name="cancellationToken">Cancellation token</param> | ||||
|     Task RemoveUserAccount(Guid userId, CancellationToken cancellationToken); | ||||
| } | ||||
|     Task RemoveAuthorityAccount(Guid userId, CancellationToken cancellationToken); | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,11 @@ | ||||
| using ApplicationLayer.Services.AuthServices.Common; | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using ApplicationLayer.Services.AuthServices.Common; | ||||
|  | ||||
| namespace ApplicationLayer.Services.Users.Requests; | ||||
|  | ||||
| public record ChangeUserAuthDataRequest(Guid UserId, AuthData NewAuthData); | ||||
| public class ChangeUserAuthDataRequest(Guid userId, AuthData newAuthData) | ||||
| { | ||||
|     [Required] public Guid UserId { get; set; } = userId; | ||||
|  | ||||
|     [Required] public AuthData NewAuthData { get; set; } = newAuthData; | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| using ApplicationLayer.InfrastructureServicesInterfaces; | ||||
| using ApplicationLayer.Services.AuthServices.Common; | ||||
| using ApplicationLayer.Services.AuthServices.NeededServices; | ||||
| using ApplicationLayer.Services.Users.Exceptions; | ||||
| using ApplicationLayer.Services.Users.Requests; | ||||
| using Domains.Users; | ||||
|  | ||||
| @@ -7,27 +9,59 @@ namespace ApplicationLayer.Services.Users; | ||||
|  | ||||
| public class UsersService(IUsersRepository users, IUnitOfWork unitOfWork) : IUsersService | ||||
| { | ||||
|     async Task<List<User>> IUsersService.GetAuthoritiesAccountsAsync(CancellationToken cancellationToken) | ||||
|     async Task<List<User>> IUsersService.GetAuthoritiesAccountsAsync(CancellationToken cancellationToken) => | ||||
|         await users.GetAllOfRoleAsync(Role.ApprovingAuthority, cancellationToken); | ||||
|  | ||||
|     async Task IUsersService.ChangeAuthorityAuthDataAsync(ChangeUserAuthDataRequest request, CancellationToken cancellationToken) | ||||
|     { | ||||
|             return await users.GetAllOfRoleAsync(Role.ApprovingAuthority, cancellationToken); | ||||
|         } | ||||
|         var user = await users.GetByIdAsync(request.UserId, cancellationToken); | ||||
|  | ||||
|     async Task IUsersService.ChangeAccountAuthDataAsync(ChangeUserAuthDataRequest request, CancellationToken cancellationToken) | ||||
|         ValidateRole(user, Role.ApprovingAuthority); | ||||
|  | ||||
|         await ChangeAccountAuthDataAsync(user, request.NewAuthData, cancellationToken); | ||||
|     } | ||||
|  | ||||
|     async Task IUsersService.RemoveAuthorityAccount(Guid userId, CancellationToken cancellationToken) | ||||
|     { | ||||
|             var user = await users.GetByIdAsync(request.UserId, cancellationToken); | ||||
|         var user = await users.GetByIdAsync(userId, cancellationToken); | ||||
|  | ||||
|             user.Email = request.NewAuthData.Email; | ||||
|             user.Password = request.NewAuthData.Password; | ||||
|             await users.UpdateAsync(user, cancellationToken); | ||||
|         ValidateRole(user, Role.ApprovingAuthority); | ||||
|  | ||||
|             await unitOfWork.SaveAsync(cancellationToken); | ||||
|         } | ||||
|         await RemoveUserAccount(user, cancellationToken); | ||||
|     } | ||||
|  | ||||
|     async Task IUsersService.RemoveUserAccount(Guid userId, CancellationToken cancellationToken) | ||||
|     /// Updates user account auth data | ||||
|     /// <param name="user">User to remove</param> | ||||
|     /// <param name="authData">New auth data</param> | ||||
|     /// <param name="cancellationToken">Cancellation token</param> | ||||
|     private async Task ChangeAccountAuthDataAsync(User user, AuthData authData, CancellationToken cancellationToken) | ||||
|     { | ||||
|             var user = await users.GetByIdAsync(userId, cancellationToken); | ||||
|             users.Remove(user); | ||||
|         user.Email = authData.Email; | ||||
|         user.Password = authData.Password; | ||||
|         await users.UpdateAsync(user, cancellationToken); | ||||
|  | ||||
|             await unitOfWork.SaveAsync(cancellationToken); | ||||
|         await unitOfWork.SaveAsync(cancellationToken); | ||||
|     } | ||||
|  | ||||
|     /// Removes user account from DB | ||||
|     /// <param name="user">User to remove</param> | ||||
|     /// <param name="cancellationToken">Cancellation token</param> | ||||
|     private async Task RemoveUserAccount(User user, CancellationToken cancellationToken) | ||||
|     { | ||||
|         users.Remove(user); | ||||
|  | ||||
|         await unitOfWork.SaveAsync(cancellationToken); | ||||
|     } | ||||
|  | ||||
|     /// Checks if role of user equals expected | ||||
|     /// <param name="user">User to check</param> | ||||
|     /// <param name="expectedRole">Expected role</param> | ||||
|     /// <exception cref="WrongRoleException">Role is not expected</exception> | ||||
|     private static void ValidateRole(User user, Role expectedRole) | ||||
|     { | ||||
|         if (user.Role != expectedRole) | ||||
|         { | ||||
|             throw new WrongRoleException(user.Id); | ||||
|         } | ||||
| } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -29,18 +29,6 @@ public class VisaApplicationRequestsHandler( | ||||
|         return applicationModels; | ||||
|     } | ||||
|  | ||||
|     private async Task<VisaApplicationModelForAuthority> MapVisaApplicationToModelForAuthorities(VisaApplication visaApplication, | ||||
|         CancellationToken cancellationToken) | ||||
|     { | ||||
|         var applicant = await applicants.GetByIdAsync(visaApplication.ApplicantId, cancellationToken); | ||||
|         var applicantModel = mapper.Map<ApplicantModel>(applicant); | ||||
|  | ||||
|         var model = mapper.Map<VisaApplicationModelForAuthority>(visaApplication); | ||||
|         model.Applicant = applicantModel; | ||||
|  | ||||
|         return model; | ||||
|     } | ||||
|  | ||||
|     public async Task<List<VisaApplicationModelForApplicant>> GetForApplicantAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         var applicantId = await applicants.GetApplicantIdByUserId(userIdProvider.GetUserId(), cancellationToken); | ||||
| @@ -83,7 +71,7 @@ public class VisaApplicationRequestsHandler( | ||||
|             throw new ApplicationAlreadyProcessedException(); | ||||
|         } | ||||
|  | ||||
|         ApplicationStatus statusToSet = status switch | ||||
|         var statusToSet = status switch | ||||
|         { | ||||
|             AuthorityRequestStatuses.Approved => ApplicationStatus.Approved, | ||||
|             AuthorityRequestStatuses.Rejected => ApplicationStatus.Rejected, | ||||
| @@ -95,4 +83,16 @@ public class VisaApplicationRequestsHandler( | ||||
|  | ||||
|         await unitOfWork.SaveAsync(cancellationToken); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     private async Task<VisaApplicationModelForAuthority> MapVisaApplicationToModelForAuthorities(VisaApplication visaApplication, | ||||
|         CancellationToken cancellationToken) | ||||
|     { | ||||
|         var applicant = await applicants.GetByIdAsync(visaApplication.ApplicantId, cancellationToken); | ||||
|         var applicantModel = mapper.Map<ApplicantModel>(applicant); | ||||
|  | ||||
|         var model = mapper.Map<VisaApplicationModelForAuthority>(visaApplication); | ||||
|         model.Applicant = applicantModel; | ||||
|  | ||||
|         return model; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,21 @@ | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using Domains; | ||||
|  | ||||
| namespace ApplicationLayer.Services.VisaApplications.Models; | ||||
|  | ||||
| /// Model of past visa for presentation layer | ||||
| public class PastVisaModel | ||||
| { | ||||
|     // Date of issue | ||||
|     [Required] | ||||
|     public DateTime IssueDate { get; set; } | ||||
|  | ||||
|     /// Name of visa | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.VisaNameLength)] | ||||
|     public string Name { get; set; } = null!; | ||||
|  | ||||
|     /// Date when visa expires | ||||
|     [Required] | ||||
|     public DateTime ExpirationDate { get; set; } | ||||
| } | ||||
| @@ -0,0 +1,21 @@ | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using Domains; | ||||
|  | ||||
| namespace ApplicationLayer.Services.VisaApplications.Models; | ||||
|  | ||||
| /// Model of past visit for presentation layer | ||||
| public class PastVisitModel | ||||
| { | ||||
|     /// First day of past visit | ||||
|     [Required] | ||||
|     public DateTime StartDate { get; set; } | ||||
|  | ||||
|     /// Last day of past visit | ||||
|     [Required] | ||||
|     public DateTime EndDate { get; set; } | ||||
|  | ||||
|     /// Destination country of past visit | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.CountryNameLength)] | ||||
|     public string DestinationCountry { get; set; } = null!; | ||||
| } | ||||
| @@ -0,0 +1,17 @@ | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using Domains; | ||||
|  | ||||
| namespace ApplicationLayer.Services.VisaApplications.Models; | ||||
|  | ||||
| /// Model of permission to destination country for presentation layer | ||||
| public class PermissionToDestCountryModel | ||||
| { | ||||
|     /// Date when permission to destination country expires | ||||
|     [Required] | ||||
|     public DateTime ExpirationDate { get; set; } | ||||
|  | ||||
|     /// Issuing authority | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.IssuerNameLength)] | ||||
|     public string Issuer { get; set; } = null!; | ||||
| } | ||||
| @@ -0,0 +1,17 @@ | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using Domains; | ||||
|  | ||||
| namespace ApplicationLayer.Services.VisaApplications.Models; | ||||
|  | ||||
| /// Model of re-entry permit for presentation layer | ||||
| public class ReentryPermitModel | ||||
| { | ||||
|     /// Number of re-entry permit | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.ReentryPermitNumberLength)] | ||||
|     public string Number { get; set; } = null!; | ||||
|  | ||||
|     /// Date when re-entry permit expires | ||||
|     [Required] | ||||
|     public DateTime ExpirationDate { get; set; } | ||||
| } | ||||
| @@ -0,0 +1,26 @@ | ||||
| using ApplicationLayer.InfrastructureServicesInterfaces; | ||||
| using FluentValidation; | ||||
|  | ||||
| namespace ApplicationLayer.Services.VisaApplications.Models.Validation; | ||||
|  | ||||
| public class PastVisaModelValidator : AbstractValidator<PastVisaModel> | ||||
| { | ||||
|     public PastVisaModelValidator(IDateTimeProvider dateTimeProvider) | ||||
|     { | ||||
|         RuleFor(v => v.ExpirationDate) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Expiration date of past visa can not be empty") | ||||
|             .GreaterThan(v => v.IssueDate) | ||||
|             .WithMessage("Past visa expiration date can not be earlier than issue date"); | ||||
|  | ||||
|         RuleFor(v => v.IssueDate) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Issue date of past visa can not be empty") | ||||
|             .LessThan(dateTimeProvider.Now()) | ||||
|             .WithMessage("Issue date of past visa must be in past"); | ||||
|  | ||||
|         RuleFor(v => v.Name) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Name of past visa can not be empty"); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,29 @@ | ||||
| using ApplicationLayer.InfrastructureServicesInterfaces; | ||||
| using Domains; | ||||
| using FluentValidation; | ||||
|  | ||||
| namespace ApplicationLayer.Services.VisaApplications.Models.Validation; | ||||
|  | ||||
| public class PastVisitModelValidator : AbstractValidator<PastVisitModel> | ||||
| { | ||||
|     public PastVisitModelValidator(IDateTimeProvider dateTimeProvider) | ||||
|     { | ||||
|         RuleFor(v => v.StartDate) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Start date of past visit can not be empty") | ||||
|             .LessThan(v => v.EndDate) | ||||
|             .WithMessage("Start date of past visit must be earlier than end date") | ||||
|             .LessThan(dateTimeProvider.Now()) | ||||
|             .WithMessage("Start date of past visit must be in past"); | ||||
|  | ||||
|         RuleFor(v => v.EndDate) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("End date of past visit can not be empty"); | ||||
|  | ||||
|         RuleFor(v => v.DestinationCountry) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Destination Country of past visit can not be null") | ||||
|             .MaximumLength(ConfigurationConstraints.CountryNameLength) | ||||
|             .WithMessage($"Destination Country of past visit length must be less than {ConfigurationConstraints.CountryNameLength}"); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,23 @@ | ||||
| using ApplicationLayer.InfrastructureServicesInterfaces; | ||||
| using Domains; | ||||
| using FluentValidation; | ||||
|  | ||||
| namespace ApplicationLayer.Services.VisaApplications.Models.Validation; | ||||
|  | ||||
| public class PermissionToDestCountryModelValidator : AbstractValidator<PermissionToDestCountryModel?> | ||||
| { | ||||
|     public PermissionToDestCountryModelValidator(IDateTimeProvider dateTimeProvider) | ||||
|     { | ||||
|         RuleFor(p => p!.ExpirationDate) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Expiration date of permission to destination Country can not be empty") | ||||
|             .GreaterThan(dateTimeProvider.Now()) | ||||
|             .WithMessage("Permission to destination Country must not be expired"); | ||||
|  | ||||
|         RuleFor(p => p!.Issuer) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Issuer of permission for destination Country can not be empty") | ||||
|             .MaximumLength(ConfigurationConstraints.IssuerNameLength) | ||||
|             .WithMessage($"Issuer of permission to destination Country length must be less than {ConfigurationConstraints.IssuerNameLength}"); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,23 @@ | ||||
| using ApplicationLayer.InfrastructureServicesInterfaces; | ||||
| using Domains; | ||||
| using FluentValidation; | ||||
|  | ||||
| namespace ApplicationLayer.Services.VisaApplications.Models.Validation; | ||||
|  | ||||
| public class ReentryPermitModelValidator : AbstractValidator<ReentryPermitModel?> | ||||
| { | ||||
|     public ReentryPermitModelValidator(IDateTimeProvider dateTimeProvider) | ||||
|     { | ||||
|         RuleFor(p => p!.Number) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Re-entry permit number can not be empty") | ||||
|             .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"); | ||||
|     } | ||||
| } | ||||
| @@ -2,42 +2,42 @@ | ||||
|  | ||||
| namespace ApplicationLayer.Services.VisaApplications.Models; | ||||
|  | ||||
| /// Model of <see cref="VisaApplication"/> | ||||
| /// Model of <see cref="VisaApplication" /> | ||||
| public class VisaApplicationModelForApplicant | ||||
| { | ||||
|     /// <inheritdoc cref="VisaApplication.Id"/> | ||||
|     /// <inheritdoc cref="VisaApplication.Id" /> | ||||
|     public Guid Id { get; set; } | ||||
|  | ||||
|     /// <inheritdoc cref="VisaApplication.Status"/> | ||||
|     /// <inheritdoc cref="VisaApplication.Status" /> | ||||
|     public ApplicationStatus Status { get; set; } | ||||
|  | ||||
|     /// <inheritdoc cref="VisaApplication.ReentryPermit"/> | ||||
|     public ReentryPermit? ReentryPermit { get; set; } | ||||
|     /// <inheritdoc cref="VisaApplication.ReentryPermit" /> | ||||
|     public ReentryPermitModel? ReentryPermit { get; set; } | ||||
|  | ||||
|     /// <inheritdoc cref="VisaApplication.DestinationCountry"/> | ||||
|     /// <inheritdoc cref="VisaApplication.DestinationCountry" /> | ||||
|     public string DestinationCountry { get; set; } = null!; | ||||
|  | ||||
|     /// <inheritdoc cref="VisaApplication.PastVisas"/> | ||||
|     public List<PastVisa> PastVisas { get; set; } = null!; | ||||
|     /// <inheritdoc cref="VisaApplication.PastVisas" /> | ||||
|     public List<PastVisaModel> PastVisas { get; set; } = null!; | ||||
|  | ||||
|     /// <inheritdoc cref="VisaApplication.PermissionToDestCountry"/> | ||||
|     public PermissionToDestCountry? PermissionToDestCountry { get; set; } | ||||
|     /// <inheritdoc cref="VisaApplication.PermissionToDestCountry" /> | ||||
|     public PermissionToDestCountryModel? PermissionToDestCountry { get; set; } | ||||
|  | ||||
|     /// <inheritdoc cref="VisaApplication.PastVisits"/> | ||||
|     public List<PastVisit> PastVisits { get; set; } = null!; | ||||
|     /// <inheritdoc cref="VisaApplication.PastVisits" /> | ||||
|     public List<PastVisitModel> PastVisits { get; set; } = null!; | ||||
|  | ||||
|     /// <inheritdoc cref="VisaApplication.VisaCategory"/> | ||||
|     /// <inheritdoc cref="VisaApplication.VisaCategory" /> | ||||
|     public VisaCategory VisaCategory { get; set; } | ||||
|  | ||||
|     /// <inheritdoc cref="VisaApplication.ForGroup"/> | ||||
|     /// <inheritdoc cref="VisaApplication.ForGroup" /> | ||||
|     public bool ForGroup { get; set; } | ||||
|  | ||||
|     /// <inheritdoc cref="VisaApplication.RequestedNumberOfEntries"/> | ||||
|     /// <inheritdoc cref="VisaApplication.RequestedNumberOfEntries" /> | ||||
|     public RequestedNumberOfEntries RequestedNumberOfEntries { get; set; } | ||||
|  | ||||
|     /// <inheritdoc cref="VisaApplication.RequestDate"/> | ||||
|     /// <inheritdoc cref="VisaApplication.RequestDate" /> | ||||
|     public DateTime RequestDate { get; set; } | ||||
|  | ||||
|     /// <inheritdoc cref="VisaApplication.ValidDaysRequested"/> | ||||
|     /// <inheritdoc cref="VisaApplication.ValidDaysRequested" /> | ||||
|     public int ValidDaysRequested { get; set; } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -3,44 +3,44 @@ using Domains.VisaApplicationDomain; | ||||
|  | ||||
| namespace ApplicationLayer.Services.VisaApplications.Models; | ||||
|  | ||||
| /// Model of <see cref="VisaApplication"/> with applicant property | ||||
| /// Model of <see cref="VisaApplication" /> with applicant property | ||||
| public class VisaApplicationModelForAuthority | ||||
| { | ||||
|     /// <inheritdoc cref="VisaApplication.Id"/> | ||||
|     /// <inheritdoc cref="VisaApplication.Id" /> | ||||
|     public Guid Id { get; set; } | ||||
|  | ||||
|     /// Applicant of application | ||||
|     public ApplicantModel Applicant { get; set; } = null!; | ||||
|  | ||||
|     /// <inheritdoc cref="VisaApplication.Status"/> | ||||
|     /// <inheritdoc cref="VisaApplication.Status" /> | ||||
|     public ApplicationStatus Status { get; set; } | ||||
|  | ||||
|     /// <inheritdoc cref="VisaApplication.ReentryPermit"/> | ||||
|     public ReentryPermit? ReentryPermit { get; set; } | ||||
|     /// <inheritdoc cref="VisaApplication.ReentryPermit" /> | ||||
|     public ReentryPermitModel? ReentryPermit { get; set; } | ||||
|  | ||||
|     /// <inheritdoc cref="VisaApplication.DestinationCountry"/> | ||||
|     /// <inheritdoc cref="VisaApplication.DestinationCountry" /> | ||||
|     public string DestinationCountry { get; set; } = null!; | ||||
|  | ||||
|     /// <inheritdoc cref="VisaApplication.PastVisas"/> | ||||
|     public List<PastVisa> PastVisas { get; set; } = null!; | ||||
|     /// <inheritdoc cref="VisaApplication.PastVisas" /> | ||||
|     public List<PastVisaModel> PastVisas { get; set; } = null!; | ||||
|  | ||||
|     /// <inheritdoc cref="VisaApplication.PermissionToDestCountry"/> | ||||
|     public PermissionToDestCountry? PermissionToDestCountry { get; set; } | ||||
|     /// <inheritdoc cref="VisaApplication.PermissionToDestCountry" /> | ||||
|     public PermissionToDestCountryModel? PermissionToDestCountry { get; set; } | ||||
|  | ||||
|     public List<PastVisit> PastVisits { get; set; } = null!; | ||||
|     public List<PastVisitModel> PastVisits { get; set; } = null!; | ||||
|  | ||||
|     /// <inheritdoc cref="VisaApplication.VisaCategory"/> | ||||
|     /// <inheritdoc cref="VisaApplication.VisaCategory" /> | ||||
|     public VisaCategory VisaCategory { get; set; } | ||||
|  | ||||
|     /// <inheritdoc cref="VisaApplication.ForGroup"/> | ||||
|     /// <inheritdoc cref="VisaApplication.ForGroup" /> | ||||
|     public bool ForGroup { get; set; } | ||||
|  | ||||
|     /// <inheritdoc cref="VisaApplication.RequestedNumberOfEntries"/> | ||||
|     /// <inheritdoc cref="VisaApplication.RequestedNumberOfEntries" /> | ||||
|     public RequestedNumberOfEntries RequestedNumberOfEntries { get; set; } | ||||
|  | ||||
|     /// <inheritdoc cref="VisaApplication.RequestDate"/> | ||||
|     /// <inheritdoc cref="VisaApplication.RequestDate" /> | ||||
|     public DateTime RequestDate { get; set; } | ||||
|  | ||||
|     /// <inheritdoc cref="VisaApplication.ValidDaysRequested"/> | ||||
|     /// <inheritdoc cref="VisaApplication.ValidDaysRequested" /> | ||||
|     public int ValidDaysRequested { get; set; } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -1,27 +0,0 @@ | ||||
| using ApplicationLayer.InfrastructureServicesInterfaces; | ||||
| using Domains.VisaApplicationDomain; | ||||
| using FluentValidation; | ||||
|  | ||||
| namespace ApplicationLayer.Services.VisaApplications.Requests.Validation; | ||||
|  | ||||
| public class PastVisaValidator : AbstractValidator<PastVisa> | ||||
| { | ||||
|     public PastVisaValidator(IDateTimeProvider dateTimeProvider) | ||||
|     { | ||||
|             RuleFor(v => v.ExpirationDate) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("Expiration date of past visa can not be empty") | ||||
|                 .GreaterThan(v => v.IssueDate) | ||||
|                 .WithMessage("Past visa expiration date can not be earlier than issue date"); | ||||
|  | ||||
|             RuleFor(v => v.IssueDate) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("Issue date of past visa can not be empty") | ||||
|                 .LessThan(dateTimeProvider.Now()) | ||||
|                 .WithMessage("Issue date of past visa must be in past"); | ||||
|  | ||||
|             RuleFor(v => v.Name) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("Name of past visa can not be empty"); | ||||
|         } | ||||
| } | ||||
| @@ -1,30 +0,0 @@ | ||||
| using ApplicationLayer.InfrastructureServicesInterfaces; | ||||
| using Domains; | ||||
| using Domains.VisaApplicationDomain; | ||||
| using FluentValidation; | ||||
|  | ||||
| namespace ApplicationLayer.Services.VisaApplications.Requests.Validation; | ||||
|  | ||||
| public class PastVisitValidator : AbstractValidator<PastVisit> | ||||
| { | ||||
|     public PastVisitValidator(IDateTimeProvider dateTimeProvider) | ||||
|     { | ||||
|             RuleFor(v => v.StartDate) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("Start date of past visit can not be empty") | ||||
|                 .LessThan(v => v.EndDate) | ||||
|                 .WithMessage("Start date of past visit must be earlier than end date") | ||||
|                 .LessThan(dateTimeProvider.Now()) | ||||
|                 .WithMessage("Start date of past visit must be in past"); | ||||
|  | ||||
|             RuleFor(v => v.EndDate) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("End date of past visit can not be empty"); | ||||
|  | ||||
|             RuleFor(v => v.DestinationCountry) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("Destination Country of past visit can not be null") | ||||
|                 .MaximumLength(ConfigurationConstraints.CountryNameLength) | ||||
|                 .WithMessage($"Destination Country of past visit length must be less than {ConfigurationConstraints.CountryNameLength}"); | ||||
|         } | ||||
| } | ||||
| @@ -1,24 +0,0 @@ | ||||
| using ApplicationLayer.InfrastructureServicesInterfaces; | ||||
| using Domains; | ||||
| using Domains.VisaApplicationDomain; | ||||
| using FluentValidation; | ||||
|  | ||||
| namespace ApplicationLayer.Services.VisaApplications.Requests.Validation; | ||||
|  | ||||
| public class PermissionToDestCountryValidator : AbstractValidator<PermissionToDestCountry?> | ||||
| { | ||||
|     public PermissionToDestCountryValidator(IDateTimeProvider dateTimeProvider) | ||||
|     { | ||||
|             RuleFor(p => p!.ExpirationDate) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("Expiration date of permission to destination Country can not be empty") | ||||
|                 .GreaterThan(dateTimeProvider.Now()) | ||||
|                 .WithMessage("Permission to destination Country must not be expired"); | ||||
|  | ||||
|             RuleFor(p => p!.Issuer) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("Issuer of permission for destination Country can not be empty") | ||||
|                 .MaximumLength(ConfigurationConstraints.IssuerNameLength) | ||||
|                 .WithMessage($"Issuer of permission to destination Country length must be less than {ConfigurationConstraints.IssuerNameLength}"); | ||||
|         } | ||||
| } | ||||
| @@ -1,24 +0,0 @@ | ||||
| using ApplicationLayer.InfrastructureServicesInterfaces; | ||||
| using Domains; | ||||
| using Domains.VisaApplicationDomain; | ||||
| using FluentValidation; | ||||
|  | ||||
| namespace ApplicationLayer.Services.VisaApplications.Requests.Validation; | ||||
|  | ||||
| public class ReentryPermitValidator : AbstractValidator<ReentryPermit?> | ||||
| { | ||||
|     public ReentryPermitValidator(IDateTimeProvider dateTimeProvider) | ||||
|     { | ||||
|             RuleFor(p => p!.Number) | ||||
|                 .NotEmpty() | ||||
|                 .WithMessage("Re-entry permit number can not be empty") | ||||
|                 .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"); | ||||
|         } | ||||
| } | ||||
| @@ -1,5 +1,6 @@ | ||||
| using ApplicationLayer.InfrastructureServicesInterfaces; | ||||
| using ApplicationLayer.Services.Applicants.NeededServices; | ||||
| using ApplicationLayer.Services.VisaApplications.Models; | ||||
| using Domains; | ||||
| using Domains.VisaApplicationDomain; | ||||
| using FluentValidation; | ||||
| @@ -9,17 +10,17 @@ namespace ApplicationLayer.Services.VisaApplications.Requests.Validation; | ||||
| public class VisaApplicationCreateRequestValidator : AbstractValidator<VisaApplicationCreateRequest> | ||||
| { | ||||
|     public VisaApplicationCreateRequestValidator( | ||||
|         IValidator<ReentryPermit?> reentryPermitValidator, | ||||
|         IValidator<PastVisa> pastVisaValidator, | ||||
|         IValidator<PermissionToDestCountry?> permissionToDestCountryValidator, | ||||
|         IValidator<PastVisit> pastVisitValidator, | ||||
|         IValidator<ReentryPermitModel?> reentryPermitModelValidator, | ||||
|         IValidator<PastVisaModel> pastVisaModelValidator, | ||||
|         IValidator<PermissionToDestCountryModel?> permissionToDestCountryModelValidator, | ||||
|         IValidator<PastVisitModel> pastVisitModelValidator, | ||||
|         IApplicantsRepository applicants, | ||||
|         IUserIdProvider userIdProvider) | ||||
|     { | ||||
|         RuleFor(r => r.ReentryPermit) | ||||
|             .NotEmpty() | ||||
|             .WithMessage("Non-residents must provide re-entry permission") | ||||
|             .SetValidator(reentryPermitValidator) | ||||
|             .SetValidator(reentryPermitModelValidator) | ||||
|             .WhenAsync(async (_, ct) => | ||||
|                 await applicants.IsApplicantNonResidentByUserId(userIdProvider.GetUserId(), ct)); | ||||
|  | ||||
| @@ -40,14 +41,14 @@ public class VisaApplicationCreateRequestValidator : AbstractValidator<VisaAppli | ||||
|             .WithMessage($"Valid days requested must be less than or equal to {ConfigurationConstraints.MaxValidDays}"); | ||||
|  | ||||
|         RuleForEach(r => r.PastVisas) | ||||
|             .SetValidator(pastVisaValidator); | ||||
|             .SetValidator(pastVisaModelValidator); | ||||
|  | ||||
|         When(r => r.VisaCategory == VisaCategory.Transit, | ||||
|             () => | ||||
|                 RuleFor(r => r.PermissionToDestCountry) | ||||
|                     .SetValidator(permissionToDestCountryValidator)); | ||||
|                     .SetValidator(permissionToDestCountryModelValidator)); | ||||
|  | ||||
|         RuleForEach(r => r.PastVisits) | ||||
|             .SetValidator(pastVisitValidator); | ||||
|             .SetValidator(pastVisitModelValidator); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,16 +1,40 @@ | ||||
| using Domains.VisaApplicationDomain; | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using ApplicationLayer.Services.VisaApplications.Models; | ||||
| using Domains; | ||||
| using Domains.VisaApplicationDomain; | ||||
|  | ||||
| namespace ApplicationLayer.Services.VisaApplications.Requests; | ||||
|  | ||||
| /// Model of visa request from user | ||||
| public record VisaApplicationCreateRequest( | ||||
|     ReentryPermit? ReentryPermit, | ||||
|     string DestinationCountry, | ||||
|     VisaCategory VisaCategory, | ||||
|     bool IsForGroup, | ||||
|     RequestedNumberOfEntries RequestedNumberOfEntries, | ||||
|     int ValidDaysRequested, | ||||
|     PastVisa[] PastVisas, | ||||
|     PermissionToDestCountry? PermissionToDestCountry, | ||||
|     PastVisit[] PastVisits | ||||
| ); | ||||
| public class VisaApplicationCreateRequest | ||||
| { | ||||
|  | ||||
|     [Required] | ||||
|     public ReentryPermitModel? ReentryPermit { get; set; } | ||||
|  | ||||
|     [Required] | ||||
|     [MaxLength(ConfigurationConstraints.CountryNameLength)] | ||||
|     public string DestinationCountry { get; set; } = null!; | ||||
|  | ||||
|     [Required] | ||||
|     public VisaCategory VisaCategory { get; set; } | ||||
|  | ||||
|     [Required] | ||||
|     public bool IsForGroup { get; set; } | ||||
|  | ||||
|     [Required] | ||||
|     public RequestedNumberOfEntries RequestedNumberOfEntries { get; set; } | ||||
|  | ||||
|     [Required] | ||||
|     [Range(0, ConfigurationConstraints.MaxValidDays)] | ||||
|     public int ValidDaysRequested { get; set; } | ||||
|  | ||||
|     [Required] | ||||
|     public PastVisaModel[] PastVisas { get; set; } = null!; | ||||
|  | ||||
|     [Required] | ||||
|     public PermissionToDestCountryModel? PermissionToDestCountry { get; set; } | ||||
|  | ||||
|     [Required] | ||||
|     public PastVisitModel[] PastVisits { get; set; } = null!; | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user