Added models for presentation layer with data annotations

This commit is contained in:
2024-08-29 12:49:47 +03:00
parent ce077ad6b9
commit 7c8631b3b4
45 changed files with 742 additions and 453 deletions

View File

@@ -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!;
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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!;
}

View File

@@ -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; }
}

View File

@@ -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!;
}

View File

@@ -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}");
}
}

View File

@@ -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}");
}
}

View File

@@ -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");
}
}

View File

@@ -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}");
}
}

View File

@@ -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);
}
}

View File

@@ -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");
}
}