Merge pull request #10 from prtsie/9-refactor

9 refactor
This commit is contained in:
prtsie
2024-08-26 11:53:06 +03:00
committed by GitHub
59 changed files with 667 additions and 722 deletions

View File

@@ -1,4 +1,3 @@
namespace ApplicationLayer.GeneralExceptions
{
public class AlreadyExistsException(string message) : ApiException(message);
}
namespace ApplicationLayer.GeneralExceptions;
public class AlreadyExistsException(string message) : ApiException(message);

View File

@@ -1,4 +1,3 @@
namespace ApplicationLayer.GeneralExceptions
{
public class ApiException(string message) : Exception(message);
}
namespace ApplicationLayer.GeneralExceptions;
public class ApiException(string message) : Exception(message);

View File

@@ -1,8 +1,7 @@
namespace ApplicationLayer.InfrastructureServicesInterfaces
namespace ApplicationLayer.InfrastructureServicesInterfaces;
public interface IDateTimeProvider
{
public interface IDateTimeProvider
{
/// Returns current date and time
DateTime Now();
}
}

View File

@@ -1,8 +1,7 @@
namespace ApplicationLayer.InfrastructureServicesInterfaces
namespace ApplicationLayer.InfrastructureServicesInterfaces;
public interface IUserIdProvider
{
public interface IUserIdProvider
{
/// Returns identifier of authenticated user who sent the request
Guid GetUserId();
}
}

View File

@@ -1,10 +1,10 @@
using Domains.ApplicantDomain;
namespace ApplicationLayer.Services.Applicants.Models
namespace ApplicationLayer.Services.Applicants.Models;
/// Model of <see cref="Applicant"/>
public class ApplicantModel
{
/// Model of <see cref="Applicant"/>
public class ApplicantModel
{
/// <inheritdoc cref="Applicant.Name"/>
public Name Name { get; set; } = null!;
@@ -46,5 +46,4 @@ namespace ApplicationLayer.Services.Applicants.Models
/// <inheritdoc cref="Applicant.IsNonResident"/>
public bool IsNonResident { get; set; }
}
}

View File

@@ -1,9 +1,9 @@
using Domains.ApplicantDomain;
namespace ApplicationLayer.Services.Applicants.Models
namespace ApplicationLayer.Services.Applicants.Models;
public class PlaceOfWorkModel
{
public class PlaceOfWorkModel
{
/// Name of hirer
public string Name { get; set; } = null!;
@@ -12,5 +12,4 @@ namespace ApplicationLayer.Services.Applicants.Models
/// Phone number of hirer
public string PhoneNum { get; set; } = null!;
}
}

View File

@@ -1,4 +1,3 @@
namespace ApplicationLayer.Services.AuthServices.Common
{
public record AuthData(string Email, string Password);
}
namespace ApplicationLayer.Services.AuthServices.Common;
public record AuthData(string Email, string Password);

View File

@@ -2,10 +2,10 @@
using ApplicationLayer.Services.AuthServices.NeededServices;
using Domains.Users;
namespace ApplicationLayer.Services.AuthServices.LoginService
namespace ApplicationLayer.Services.AuthServices.LoginService;
public class DevelopmentLoginService(IUsersRepository users, ITokenGenerator tokenGenerator) : ILoginService
{
public class DevelopmentLoginService(IUsersRepository users, ITokenGenerator tokenGenerator) : ILoginService
{
async Task<string> ILoginService.LoginAsync(string email, string password, CancellationToken cancellationToken)
{
if (email == "admin@mail.ru" && password == "admin")
@@ -23,5 +23,4 @@ namespace ApplicationLayer.Services.AuthServices.LoginService
return tokenGenerator.CreateToken(user);
}
}
}

View File

@@ -1,6 +1,5 @@
using ApplicationLayer.GeneralExceptions;
namespace ApplicationLayer.Services.AuthServices.LoginService.Exceptions
{
public class IncorrectLoginDataException() : ApiException("Incorrect email or password");
}
namespace ApplicationLayer.Services.AuthServices.LoginService.Exceptions;
public class IncorrectLoginDataException() : ApiException("Incorrect email or password");

View File

@@ -1,10 +1,9 @@
namespace ApplicationLayer.Services.AuthServices.LoginService
namespace ApplicationLayer.Services.AuthServices.LoginService;
/// Handles login requests
public interface ILoginService
{
/// Handles login requests
public interface ILoginService
{
/// Handle login request
/// <returns>JWT-token</returns>
Task<string> LoginAsync(string email, string password, CancellationToken cancellationToken);
}
}

View File

@@ -1,11 +1,11 @@
using ApplicationLayer.Services.AuthServices.LoginService.Exceptions;
using ApplicationLayer.Services.AuthServices.NeededServices;
namespace ApplicationLayer.Services.AuthServices.LoginService
namespace ApplicationLayer.Services.AuthServices.LoginService;
/// <inheritdoc cref="ILoginService"/>
public class LoginService(IUsersRepository users, ITokenGenerator tokenGenerator) : ILoginService
{
/// <inheritdoc cref="ILoginService"/>
public class LoginService(IUsersRepository users, ITokenGenerator tokenGenerator) : ILoginService
{
async Task<string> ILoginService.LoginAsync(string email, string password, CancellationToken cancellationToken)
{
var user = await users.FindByEmailAsync(email, cancellationToken);
@@ -16,5 +16,4 @@ namespace ApplicationLayer.Services.AuthServices.LoginService
return tokenGenerator.CreateToken(user);
}
}
}

View File

@@ -1,9 +1,10 @@
using Domains.Users;
namespace ApplicationLayer.Services.AuthServices.NeededServices
namespace ApplicationLayer.Services.AuthServices.NeededServices;
/// Generates jwt-tokens
public interface ITokenGenerator
{
public interface ITokenGenerator
{
/// returns jwt-token for specific user
string CreateToken(User user);
}
}

View File

@@ -1,11 +1,11 @@
using ApplicationLayer.InfrastructureServicesInterfaces;
using Domains.Users;
namespace ApplicationLayer.Services.AuthServices.NeededServices
namespace ApplicationLayer.Services.AuthServices.NeededServices;
/// Repository pattern for <see cref="User"/>
public interface IUsersRepository : IGenericRepository<User>
{
/// Repository pattern for <see cref="User"/>
public interface IUsersRepository : IGenericRepository<User>
{
/// Find <see cref="User"/> by email
/// <param name="email"><see cref="User"/>'s email</param>
/// <param name="cancellationToken">Cancellation token</param>
@@ -17,5 +17,4 @@ namespace ApplicationLayer.Services.AuthServices.NeededServices
/// <param name="cancellationToken">cancellation token</param>
/// <returns>list of accounts</returns>
Task<List<User>> GetAllOfRoleAsync(Role role, CancellationToken cancellationToken);
}
}

View File

@@ -1,14 +1,13 @@
using ApplicationLayer.Services.AuthServices.Requests;
namespace ApplicationLayer.Services.AuthServices.RegisterService
namespace ApplicationLayer.Services.AuthServices.RegisterService;
/// Handles register request
public interface IRegisterService
{
/// Handles register request
public interface IRegisterService
{
/// Handle <see cref="RegisterApplicantRequest"/>
Task RegisterApplicant(RegisterApplicantRequest request, CancellationToken cancellationToken);
/// Handles <see cref="RegisterRequest"/> and adds approving authority account
Task RegisterAuthority(RegisterRequest request, CancellationToken cancellationToken);
}
}

View File

@@ -6,15 +6,15 @@ using AutoMapper;
using Domains.ApplicantDomain;
using Domains.Users;
namespace ApplicationLayer.Services.AuthServices.RegisterService
{
/// <inheritdoc cref="IRegisterService"/>
public class RegisterService(
namespace ApplicationLayer.Services.AuthServices.RegisterService;
/// <inheritdoc cref="IRegisterService"/>
public class RegisterService(
IUsersRepository users,
IApplicantsRepository applicants,
IUnitOfWork unitOfWork,
IMapper mapper) : IRegisterService
{
{
async Task IRegisterService.RegisterApplicant(RegisterApplicantRequest request, CancellationToken cancellationToken)
{
var user = mapper.Map<User>(request.AuthData);
@@ -38,5 +38,4 @@ namespace ApplicationLayer.Services.AuthServices.RegisterService
await unitOfWork.SaveAsync(cancellationToken);
}
}
}

View File

@@ -2,9 +2,9 @@
using ApplicationLayer.Services.AuthServices.Common;
using Domains.ApplicantDomain;
namespace ApplicationLayer.Services.AuthServices.Requests
{
public record RegisterApplicantRequest(
namespace ApplicationLayer.Services.AuthServices.Requests;
public record RegisterApplicantRequest(
AuthData AuthData,
Name ApplicantName,
Passport Passport,
@@ -20,4 +20,3 @@ namespace ApplicationLayer.Services.AuthServices.Requests
string JobTitle,
PlaceOfWorkModel PlaceOfWork,
bool IsNonResident) : RegisterRequest(AuthData);
}

View File

@@ -1,6 +1,5 @@
using ApplicationLayer.Services.AuthServices.Common;
namespace ApplicationLayer.Services.AuthServices.Requests
{
public record RegisterRequest(AuthData AuthData);
}
namespace ApplicationLayer.Services.AuthServices.Requests;
public record RegisterRequest(AuthData AuthData);

View File

@@ -3,10 +3,10 @@ using ApplicationLayer.Services.AuthServices.NeededServices;
using Domains;
using FluentValidation;
namespace ApplicationLayer.Services.AuthServices.Requests.Validation
namespace ApplicationLayer.Services.AuthServices.Requests.Validation;
public class AuthDataValidator : AbstractValidator<AuthData>
{
public class AuthDataValidator : AbstractValidator<AuthData>
{
public AuthDataValidator(IUsersRepository users)
{
RuleFor(d => d.Email)
@@ -28,5 +28,4 @@ namespace ApplicationLayer.Services.AuthServices.Requests.Validation
.MaximumLength(ConfigurationConstraints.PasswordLength)
.WithMessage($"Password length must be less than {ConfigurationConstraints.PasswordLength}");
}
}
}

View File

@@ -2,10 +2,10 @@
using Domains.ApplicantDomain;
using FluentValidation;
namespace ApplicationLayer.Services.AuthServices.Requests.Validation
namespace ApplicationLayer.Services.AuthServices.Requests.Validation;
public class NameValidator : AbstractValidator<Name>
{
public class NameValidator : AbstractValidator<Name>
{
public NameValidator()
{
RuleFor(m => m.FirstName)
@@ -24,5 +24,4 @@ namespace ApplicationLayer.Services.AuthServices.Requests.Validation
.MaximumLength(ConfigurationConstraints.NameLength)
.WithMessage($"Patronymic length must be less than {ConfigurationConstraints.NameLength}");
}
}
}

View File

@@ -3,10 +3,10 @@ using Domains;
using Domains.ApplicantDomain;
using FluentValidation;
namespace ApplicationLayer.Services.AuthServices.Requests.Validation
namespace ApplicationLayer.Services.AuthServices.Requests.Validation;
public class PassportValidator : AbstractValidator<Passport>
{
public class PassportValidator : AbstractValidator<Passport>
{
public PassportValidator(IDateTimeProvider dateTimeProvider)
{
RuleFor(r => r.Issuer)
@@ -33,5 +33,4 @@ namespace ApplicationLayer.Services.AuthServices.Requests.Validation
.LessThanOrEqualTo(dateTimeProvider.Now())
.WithMessage("Passport issue date must be in past");
}
}
}

View File

@@ -2,10 +2,10 @@
using Domains;
using FluentValidation;
namespace ApplicationLayer.Services.AuthServices.Requests.Validation
namespace ApplicationLayer.Services.AuthServices.Requests.Validation;
public class PlaceOfWorkModelValidator : AbstractValidator<PlaceOfWorkModel>
{
public class PlaceOfWorkModelValidator : AbstractValidator<PlaceOfWorkModel>
{
public PlaceOfWorkModelValidator()
{
RuleFor(p => p.Name)
@@ -46,5 +46,4 @@ namespace ApplicationLayer.Services.AuthServices.Requests.Validation
.MaximumLength(ConfigurationConstraints.CountryNameLength)
.WithMessage($"Building of place of work length must be less than {ConfigurationConstraints.BuildingNumberLength}");
}
}
}

View File

@@ -5,10 +5,10 @@ using Domains;
using Domains.ApplicantDomain;
using FluentValidation;
namespace ApplicationLayer.Services.AuthServices.Requests.Validation
namespace ApplicationLayer.Services.AuthServices.Requests.Validation;
public class RegisterApplicantRequestValidator : AbstractValidator<RegisterApplicantRequest>
{
public class RegisterApplicantRequestValidator : AbstractValidator<RegisterApplicantRequest>
{
public RegisterApplicantRequestValidator(
IDateTimeProvider dateTimeProvider,
IValidator<Name> nameValidator,
@@ -74,5 +74,4 @@ namespace ApplicationLayer.Services.AuthServices.Requests.Validation
RuleFor(r => r.PlaceOfWork)
.SetValidator(placeOfWorkModelValidator);
}
}
}

View File

@@ -1,11 +1,11 @@
using ApplicationLayer.Services.Users.Requests;
using Domains.Users;
namespace ApplicationLayer.Services.Users
namespace ApplicationLayer.Services.Users;
/// user accounts service
public interface IUsersService
{
/// user accounts service
public interface IUsersService
{
/// Returns all user accounts with role of approving authority
/// <param name="cancellationToken">Cancellation token</param>
Task<List<User>> GetAuthoritiesAccountsAsync(CancellationToken cancellationToken);
@@ -19,5 +19,4 @@ namespace ApplicationLayer.Services.Users
/// <param name="userId">Identifier of account</param>
/// <param name="cancellationToken">Cancellation token</param>
Task RemoveUserAccount(Guid userId, CancellationToken cancellationToken);
}
}

View File

@@ -1,6 +1,5 @@
using ApplicationLayer.Services.AuthServices.Common;
namespace ApplicationLayer.Services.Users.Requests
{
public record ChangeUserAuthDataRequest(Guid UserId, AuthData NewAuthData);
}
namespace ApplicationLayer.Services.Users.Requests;
public record ChangeUserAuthDataRequest(Guid UserId, AuthData NewAuthData);

View File

@@ -3,10 +3,10 @@ using ApplicationLayer.Services.AuthServices.NeededServices;
using ApplicationLayer.Services.Users.Requests;
using Domains.Users;
namespace ApplicationLayer.Services.Users
namespace ApplicationLayer.Services.Users;
public class UsersService(IUsersRepository users, IUnitOfWork unitOfWork) : IUsersService
{
public class UsersService(IUsersRepository users, IUnitOfWork unitOfWork) : IUsersService
{
async Task<List<User>> IUsersService.GetAuthoritiesAccountsAsync(CancellationToken cancellationToken)
{
return await users.GetAllOfRoleAsync(Role.ApprovingAuthority, cancellationToken);
@@ -30,5 +30,4 @@ namespace ApplicationLayer.Services.Users
await unitOfWork.SaveAsync(cancellationToken);
}
}
}

View File

@@ -1,6 +1,5 @@
using ApplicationLayer.GeneralExceptions;
namespace ApplicationLayer.Services.VisaApplications.Exceptions
{
public class ApplicationAlreadyProcessedException() : ApiException("This application already processed or closed by applicant.");
}
namespace ApplicationLayer.Services.VisaApplications.Exceptions;
public class ApplicationAlreadyProcessedException() : ApiException("This application already processed or closed by applicant.");

View File

@@ -9,13 +9,13 @@ public interface IVisaApplicationRequestsHandler
Task<List<VisaApplicationModelForAuthority>> GetAllAsync(CancellationToken cancellationToken);
/// Returns all applications of one applicant
Task<List<VisaApplicationModelForApplicant>> GetForApplicantAsync(Guid userId, CancellationToken cancellationToken);
Task<List<VisaApplicationModelForApplicant>> GetForApplicantAsync(CancellationToken cancellationToken);
/// Creates application for applicant with specific user identifier
Task HandleCreateRequestAsync(Guid userId, VisaApplicationCreateRequest request, CancellationToken cancellationToken);
Task HandleCreateRequestAsync(VisaApplicationCreateRequest request, CancellationToken cancellationToken);
/// Sets application status to closed
Task HandleCloseRequestAsync(Guid userId, Guid applicationId, CancellationToken cancellationToken);
Task HandleCloseRequestAsync(Guid applicationId, CancellationToken cancellationToken);
Task SetApplicationStatusFromAuthorityAsync(Guid applicationId, AuthorityRequestStatuses status, CancellationToken cancellationToken);
}

View File

@@ -16,7 +16,8 @@ public class VisaApplicationRequestsHandler(
IApplicantsRepository applicants,
IUnitOfWork unitOfWork,
IMapper mapper,
IDateTimeProvider dateTimeProvider) : IVisaApplicationRequestsHandler
IDateTimeProvider dateTimeProvider,
IUserIdProvider userIdProvider) : IVisaApplicationRequestsHandler
{
async Task<List<VisaApplicationModelForAuthority>> IVisaApplicationRequestsHandler.GetAllAsync(CancellationToken cancellationToken)
{
@@ -40,16 +41,16 @@ public class VisaApplicationRequestsHandler(
return model;
}
public async Task<List<VisaApplicationModelForApplicant>> GetForApplicantAsync(Guid userId, CancellationToken cancellationToken)
public async Task<List<VisaApplicationModelForApplicant>> GetForApplicantAsync(CancellationToken cancellationToken)
{
var applicantId = await applicants.GetApplicantIdByUserId(userId, cancellationToken);
var applicantId = await applicants.GetApplicantIdByUserId(userIdProvider.GetUserId(), cancellationToken);
var visaApplications = await applications.GetOfApplicantAsync(applicantId, cancellationToken);
return mapper.Map<List<VisaApplicationModelForApplicant>>(visaApplications);
}
public async Task HandleCreateRequestAsync(Guid userId, VisaApplicationCreateRequest request, CancellationToken cancellationToken)
public async Task HandleCreateRequestAsync(VisaApplicationCreateRequest request, CancellationToken cancellationToken)
{
var applicant = await applicants.FindByUserIdAsync(userId, cancellationToken);
var applicant = await applicants.FindByUserIdAsync(userIdProvider.GetUserId(), cancellationToken);
var visaApplication = mapper.Map<VisaApplication>(request);
visaApplication.RequestDate = dateTimeProvider.Now();
@@ -60,9 +61,9 @@ public class VisaApplicationRequestsHandler(
await unitOfWork.SaveAsync(cancellationToken);
}
async Task IVisaApplicationRequestsHandler.HandleCloseRequestAsync(Guid userId, Guid applicationId, CancellationToken cancellationToken)
async Task IVisaApplicationRequestsHandler.HandleCloseRequestAsync(Guid applicationId, CancellationToken cancellationToken)
{
var applicantId = await applicants.GetApplicantIdByUserId(userId, cancellationToken);
var applicantId = await applicants.GetApplicantIdByUserId(userIdProvider.GetUserId(), cancellationToken);
var application = await applications.GetByApplicantAndApplicationIdAsync(applicantId, applicationId, cancellationToken);
application.Status = ApplicationStatus.Closed;

View File

@@ -1,8 +1,7 @@
namespace ApplicationLayer.Services.VisaApplications.Models
namespace ApplicationLayer.Services.VisaApplications.Models;
public enum AuthorityRequestStatuses
{
public enum AuthorityRequestStatuses
{
Approved,
Rejected
}
}

View File

@@ -1,10 +1,10 @@
using Domains.VisaApplicationDomain;
namespace ApplicationLayer.Services.VisaApplications.Models
namespace ApplicationLayer.Services.VisaApplications.Models;
/// Model of <see cref="VisaApplication"/>
public class VisaApplicationModelForApplicant
{
/// Model of <see cref="VisaApplication"/>
public class VisaApplicationModelForApplicant
{
/// <inheritdoc cref="VisaApplication.Id"/>
public Guid Id { get; set; }
@@ -40,5 +40,4 @@ namespace ApplicationLayer.Services.VisaApplications.Models
/// <inheritdoc cref="VisaApplication.ValidDaysRequested"/>
public int ValidDaysRequested { get; set; }
}
}

View File

@@ -1,11 +1,11 @@
using ApplicationLayer.Services.Applicants.Models;
using Domains.VisaApplicationDomain;
namespace ApplicationLayer.Services.VisaApplications.Models
namespace ApplicationLayer.Services.VisaApplications.Models;
/// Model of <see cref="VisaApplication"/> with applicant property
public class VisaApplicationModelForAuthority
{
/// Model of <see cref="VisaApplication"/> with applicant property
public class VisaApplicationModelForAuthority
{
/// <inheritdoc cref="VisaApplication.Id"/>
public Guid Id { get; set; }
@@ -43,5 +43,4 @@ namespace ApplicationLayer.Services.VisaApplications.Models
/// <inheritdoc cref="VisaApplication.ValidDaysRequested"/>
public int ValidDaysRequested { get; set; }
}
}

View File

@@ -2,10 +2,10 @@
using Domains.VisaApplicationDomain;
using FluentValidation;
namespace ApplicationLayer.Services.VisaApplications.Requests.Validation
namespace ApplicationLayer.Services.VisaApplications.Requests.Validation;
public class PastVisaValidator : AbstractValidator<PastVisa>
{
public class PastVisaValidator : AbstractValidator<PastVisa>
{
public PastVisaValidator(IDateTimeProvider dateTimeProvider)
{
RuleFor(v => v.ExpirationDate)
@@ -24,5 +24,4 @@ namespace ApplicationLayer.Services.VisaApplications.Requests.Validation
.NotEmpty()
.WithMessage("Name of past visa can not be empty");
}
}
}

View File

@@ -3,10 +3,10 @@ using Domains;
using Domains.VisaApplicationDomain;
using FluentValidation;
namespace ApplicationLayer.Services.VisaApplications.Requests.Validation
namespace ApplicationLayer.Services.VisaApplications.Requests.Validation;
public class PastVisitValidator : AbstractValidator<PastVisit>
{
public class PastVisitValidator : AbstractValidator<PastVisit>
{
public PastVisitValidator(IDateTimeProvider dateTimeProvider)
{
RuleFor(v => v.StartDate)
@@ -27,5 +27,4 @@ namespace ApplicationLayer.Services.VisaApplications.Requests.Validation
.MaximumLength(ConfigurationConstraints.CountryNameLength)
.WithMessage($"Destination Country of past visit length must be less than {ConfigurationConstraints.CountryNameLength}");
}
}
}

View File

@@ -3,10 +3,10 @@ using Domains;
using Domains.VisaApplicationDomain;
using FluentValidation;
namespace ApplicationLayer.Services.VisaApplications.Requests.Validation
namespace ApplicationLayer.Services.VisaApplications.Requests.Validation;
public class PermissionToDestCountryValidator : AbstractValidator<PermissionToDestCountry?>
{
public class PermissionToDestCountryValidator : AbstractValidator<PermissionToDestCountry?>
{
public PermissionToDestCountryValidator(IDateTimeProvider dateTimeProvider)
{
RuleFor(p => p!.ExpirationDate)
@@ -21,5 +21,4 @@ namespace ApplicationLayer.Services.VisaApplications.Requests.Validation
.MaximumLength(ConfigurationConstraints.IssuerNameLength)
.WithMessage($"Issuer of permission to destination Country length must be less than {ConfigurationConstraints.IssuerNameLength}");
}
}
}

View File

@@ -3,10 +3,10 @@ using Domains;
using Domains.VisaApplicationDomain;
using FluentValidation;
namespace ApplicationLayer.Services.VisaApplications.Requests.Validation
namespace ApplicationLayer.Services.VisaApplications.Requests.Validation;
public class ReentryPermitValidator : AbstractValidator<ReentryPermit?>
{
public class ReentryPermitValidator : AbstractValidator<ReentryPermit?>
{
public ReentryPermitValidator(IDateTimeProvider dateTimeProvider)
{
RuleFor(p => p!.Number)
@@ -21,5 +21,4 @@ namespace ApplicationLayer.Services.VisaApplications.Requests.Validation
.GreaterThan(dateTimeProvider.Now())
.WithMessage("Re-entry permit must not be expired");
}
}
}

View File

@@ -4,10 +4,10 @@ using Domains;
using Domains.VisaApplicationDomain;
using FluentValidation;
namespace ApplicationLayer.Services.VisaApplications.Requests.Validation
namespace ApplicationLayer.Services.VisaApplications.Requests.Validation;
public class VisaApplicationCreateRequestValidator : AbstractValidator<VisaApplicationCreateRequest>
{
public class VisaApplicationCreateRequestValidator : AbstractValidator<VisaApplicationCreateRequest>
{
public VisaApplicationCreateRequestValidator(
IValidator<ReentryPermit?> reentryPermitValidator,
IValidator<PastVisa> pastVisaValidator,
@@ -20,7 +20,7 @@ namespace ApplicationLayer.Services.VisaApplications.Requests.Validation
.NotEmpty()
.WithMessage("Non-residents must provide re-entry permission")
.SetValidator(reentryPermitValidator)
.WhenAsync(async (r, ct) =>
.WhenAsync(async (_, ct) =>
await applicants.IsApplicantNonResidentByUserId(userIdProvider.GetUserId(), ct));
RuleFor(r => r.DestinationCountry)
@@ -50,5 +50,4 @@ namespace ApplicationLayer.Services.VisaApplications.Requests.Validation
RuleForEach(r => r.PastVisits)
.SetValidator(pastVisitValidator);
}
}
}

View File

@@ -1,7 +1,7 @@
namespace Domains
namespace Domains;
public static class ConfigurationConstraints
{
public static class ConfigurationConstraints
{
public const int CityNameLength = 70;
public const int CountryNameLength = 70;
public const int CitizenshipLength = 30;
@@ -20,5 +20,4 @@
public const int ApplicantMinAge = 14;
public const int JobTitleLength = 50;
public const int MaxValidDays = 90;
}
}

View File

@@ -1,13 +1,12 @@
namespace Domains.Users
namespace Domains.Users;
/// Role of <see cref="User"/>
public enum Role
{
/// Role of <see cref="User"/>
public enum Role
{
/// Requests visa applications
Applicant,
/// Approves or declines applications
ApprovingAuthority,
/// Manages approving authorities
Admin
}
}

View File

@@ -1,7 +1,7 @@
namespace Domains.Users
namespace Domains.Users;
public class User : IEntity
{
public class User : IEntity
{
/// Unique Identifier of <see cref="User"/>
public Guid Id { get; private set; } = Guid.NewGuid();
@@ -10,5 +10,4 @@
public string Email { get; set; } = null!;
public string Password { get; set; } = null!;
}
}

View File

@@ -1,12 +1,11 @@
namespace Domains.VisaApplicationDomain
namespace Domains.VisaApplicationDomain;
public enum ApplicationStatus
{
public enum ApplicationStatus
{
/// Waits for approve
Pending,
Approved,
Rejected,
/// Closed by applicant
Closed
}
}

View File

@@ -3,10 +3,10 @@ using ApplicationLayer.InfrastructureServicesInterfaces;
using ApplicationLayer.Services.AuthServices.NeededServices;
using Microsoft.Extensions.DependencyInjection;
namespace Infrastructure.Auth
namespace Infrastructure.Auth;
public static class ServiceCollectionExtensions
{
public static class ServiceCollectionsExtensions
{
public static IServiceCollection AddTokenGenerator(this IServiceCollection services, TokenGeneratorOptions options)
{
services.AddSingleton<JwtSecurityTokenHandler>();
@@ -20,5 +20,4 @@ namespace Infrastructure.Auth
return services;
}
}
}

View File

@@ -4,11 +4,16 @@ using ApplicationLayer.InfrastructureServicesInterfaces;
using ApplicationLayer.Services.AuthServices.NeededServices;
using Domains.Users;
namespace Infrastructure.Auth
{
public class TokenGenerator(TokenGeneratorOptions options, JwtSecurityTokenHandler tokenHandler, IDateTimeProvider dateTimeProvider)
namespace Infrastructure.Auth;
/// <inheritdoc cref="ITokenGenerator"/>
/// <param name="options">options kind of one in authorization registration in DI methods</param>
/// <param name="tokenHandler">token handler</param>
/// <param name="dateTimeProvider">date time provider</param>
public class TokenGenerator(TokenGeneratorOptions options, JwtSecurityTokenHandler tokenHandler, IDateTimeProvider dateTimeProvider)
: ITokenGenerator
{
{
/// <inheritdoc cref="ITokenGenerator.CreateToken"/>
public string CreateToken(User user)
{
var claims = new List<Claim>
@@ -26,5 +31,4 @@ namespace Infrastructure.Auth
return tokenHandler.WriteToken(token);
}
}
}

View File

@@ -1,6 +1,5 @@
using Microsoft.IdentityModel.Tokens;
namespace Infrastructure.Auth
{
public record TokenGeneratorOptions(string Issuer, string Audience, TimeSpan ValidTime, SigningCredentials Credentials);
}
namespace Infrastructure.Auth;
public record TokenGeneratorOptions(string Issuer, string Audience, TimeSpan ValidTime, SigningCredentials Credentials);

View File

@@ -3,10 +3,10 @@ using ApplicationLayer.Services.AuthServices.Requests;
using AutoMapper;
using Domains.ApplicantDomain;
namespace Infrastructure.Automapper.Profiles
namespace Infrastructure.Automapper.Profiles;
public class ApplicantProfile : Profile
{
public class ApplicantProfile : Profile
{
public ApplicantProfile()
{
CreateMap<Applicant, ApplicantModel>(MemberList.Destination);
@@ -16,5 +16,4 @@ namespace Infrastructure.Automapper.Profiles
.ForMember(a => a.Name,
opts => opts.MapFrom(r => r.ApplicantName));
}
}
}

View File

@@ -2,15 +2,14 @@
using AutoMapper;
using Domains.ApplicantDomain;
namespace Infrastructure.Automapper.Profiles
namespace Infrastructure.Automapper.Profiles;
public class PlaceOfWorkProfile : Profile
{
public class PlaceOfWorkProfile : Profile
{
public PlaceOfWorkProfile()
{
CreateMap<PlaceOfWorkModel, PlaceOfWork>(MemberList.Destination)
.ForMember(p => p.Id,
opts => opts.UseDestinationValue());
}
}
}

View File

@@ -2,15 +2,14 @@
using AutoMapper;
using Domains.Users;
namespace Infrastructure.Automapper.Profiles
namespace Infrastructure.Automapper.Profiles;
public class UserProfile : Profile
{
public class UserProfile : Profile
{
public UserProfile()
{
CreateMap<AuthData, User>(MemberList.Destination)
.ForMember(u => u.Role,
opts => opts.Ignore());
}
}
}

View File

@@ -3,10 +3,10 @@ using ApplicationLayer.Services.VisaApplications.Requests;
using AutoMapper;
using Domains.VisaApplicationDomain;
namespace Infrastructure.Automapper.Profiles
namespace Infrastructure.Automapper.Profiles;
public class VisaApplicationProfile : Profile
{
public class VisaApplicationProfile : Profile
{
public VisaApplicationProfile()
{
CreateMap<VisaApplication, VisaApplicationModelForApplicant>(MemberList.Destination);
@@ -21,5 +21,4 @@ namespace Infrastructure.Automapper.Profiles
.ForMember(va => va.ApplicantId,
opts => opts.Ignore());
}
}
}

View File

@@ -1,10 +1,9 @@
using ApplicationLayer.InfrastructureServicesInterfaces;
namespace Infrastructure.Common
namespace Infrastructure.Common;
/// Implements <see cref="IDateTimeProvider"/>
public class DateTimeProvider : IDateTimeProvider
{
/// Implements <see cref="IDateTimeProvider"/>
public class DateTimeProvider : IDateTimeProvider
{
DateTime IDateTimeProvider.Now() => DateTime.Now;
}
}

View File

@@ -2,10 +2,10 @@
using ApplicationLayer.InfrastructureServicesInterfaces;
using Microsoft.AspNetCore.Http;
namespace Infrastructure.Common
namespace Infrastructure.Common;
public class UserIdProvider(IHttpContextAccessor contextAccessor) : IUserIdProvider
{
public class UserIdProvider(IHttpContextAccessor contextAccessor) : IUserIdProvider
{
Guid IUserIdProvider.GetUserId()
{
var claim = contextAccessor.HttpContext!.User.Claims.SingleOrDefault(claim => claim.Type == ClaimTypes.NameIdentifier);
@@ -15,5 +15,4 @@ namespace Infrastructure.Common
}
return Guid.Parse(claim.Value);
}
}
}

View File

@@ -1,6 +1,5 @@
using ApplicationLayer.Services.GeneralExceptions;
namespace Infrastructure.Database.Applicants.Repositories.Exceptions
{
public class ApplicantNotFoundByUserIdException() : EntityNotFoundException("Applicant not found.");
}
namespace Infrastructure.Database.Applicants.Repositories.Exceptions;
public class ApplicantNotFoundByUserIdException() : EntityNotFoundException("Applicant not found.");

View File

@@ -3,10 +3,10 @@ using Domains.Users;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Infrastructure.Database.Users.Configuration
namespace Infrastructure.Database.Users.Configuration;
public class UserConfiguration : IEntityTypeConfiguration<User>
{
public class UserConfiguration : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> entity)
{
entity.Property(u => u.Email)
@@ -19,5 +19,4 @@ namespace Infrastructure.Database.Users.Configuration
.IsUnicode(false)
.HasMaxLength(ConfigurationConstraints.PasswordLength);
}
}
}

View File

@@ -3,12 +3,12 @@ using Domains.Users;
using Infrastructure.Database.Generic;
using Microsoft.EntityFrameworkCore;
namespace Infrastructure.Database.Users.Repositories
{
/// <inheritdoc cref="IUsersRepository"/>
public class UsersRepository(IGenericReader reader, IGenericWriter writer)
namespace Infrastructure.Database.Users.Repositories;
/// <inheritdoc cref="IUsersRepository"/>
public class UsersRepository(IGenericReader reader, IGenericWriter writer)
: GenericRepository<User>(reader, writer), IUsersRepository
{
{
async Task<User?> IUsersRepository.FindByEmailAsync(string email, CancellationToken cancellationToken)
{
return await LoadDomain().SingleOrDefaultAsync(u => u.Email == email, cancellationToken);
@@ -18,5 +18,4 @@ namespace Infrastructure.Database.Users.Repositories
{
return await LoadDomain().Where(u => u.Role == role).ToListAsync(cancellationToken);
}
}
}

View File

@@ -2,15 +2,14 @@
using Domains.VisaApplicationDomain;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Infrastructure.Database.VisaApplications.Configuration
namespace Infrastructure.Database.VisaApplications.Configuration;
public static class PastVisitConfiguration<T> where T : class, IEntity
{
public static class PastVisitConfiguration<T> where T : class, IEntity
{
public static void Configure(OwnedNavigationBuilder<T, PastVisit> entity)
{
entity.Property(pv => pv.DestinationCountry)
.IsUnicode(false)
.HasMaxLength(ConfigurationConstraints.CountryNameLength);
}
}
}

View File

@@ -1,7 +1,6 @@
using ApplicationLayer.Services.GeneralExceptions;
namespace Infrastructure.Database.VisaApplications.Repositories.Exceptions
{
public class ApplicationNotFoundByApplicantAndApplicationIdException(Guid applicationId)
namespace Infrastructure.Database.VisaApplications.Repositories.Exceptions;
public class ApplicationNotFoundByApplicantAndApplicationIdException(Guid applicationId)
: EntityNotFoundException($"Application with id {applicationId} not found for authenticated user");
}

View File

@@ -2,11 +2,11 @@
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace SchengenVisaApi.Common
namespace SchengenVisaApi.Common;
/// Adds auth for swagger
public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions>
{
/// Adds auth for swagger
public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions>
{
void IConfigureOptions<SwaggerGenOptions>.Configure(SwaggerGenOptions options)
{
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
@@ -34,5 +34,4 @@ namespace SchengenVisaApi.Common
}
});
}
}
}

View File

@@ -1,12 +1,9 @@
namespace SchengenVisaApi.Common
{
namespace SchengenVisaApi.Common;
#pragma warning disable CS1591
public static class PolicyConstants
{
public static class PolicyConstants
{
public const string AdminPolicy = "AdminPolicy";
public const string ApplicantPolicy = "ApplicantPolicy";
public const string ApprovingAuthorityPolicy = "ApprovingAuthorityPolicy";
}
#pragma warning enable CS1591
}
#pragma warning enable CS1591

View File

@@ -10,18 +10,18 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using SchengenVisaApi.Common;
namespace SchengenVisaApi.Controllers
{
///<summary> Controller for user-auth and registration </summary>
[ApiController]
[Route("users")]
public class UsersController(
namespace SchengenVisaApi.Controllers;
///<summary> Controller for user-auth and registration </summary>
[ApiController]
[Route("users")]
public class UsersController(
IRegisterService registerService,
ILoginService loginService,
IUsersService usersService,
IValidator<RegisterApplicantRequest> registerApplicantRequestValidator,
IValidator<AuthData> authDataValidator) : ControllerBase
{
{
/// <summary> Adds applicant with user account to DB </summary>
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)]
@@ -111,5 +111,4 @@ namespace SchengenVisaApi.Controllers
await usersService.RemoveUserAccount(authorityAccountId, cancellationToken);
return Ok();
}
}
}

View File

@@ -1,4 +1,3 @@
using ApplicationLayer.InfrastructureServicesInterfaces;
using ApplicationLayer.Services.VisaApplications.Handlers;
using ApplicationLayer.Services.VisaApplications.Models;
using ApplicationLayer.Services.VisaApplications.Requests;
@@ -14,7 +13,6 @@ namespace SchengenVisaApi.Controllers;
[Route("visaApplications")]
public class VisaApplicationController(
IVisaApplicationRequestsHandler visaApplicationRequestsHandler,
IUserIdProvider userIdProvider,
IValidator<VisaApplicationCreateRequest> visaApplicationCreateRequestValidator) : ControllerBase
{
/// <summary> Returns all applications from DB </summary>
@@ -41,8 +39,7 @@ public class VisaApplicationController(
[Route("OfApplicant")]
public async Task<IActionResult> GetForApplicant(CancellationToken cancellationToken)
{
var userId = userIdProvider.GetUserId();
var result = await visaApplicationRequestsHandler.GetForApplicantAsync(userId, cancellationToken);
var result = await visaApplicationRequestsHandler.GetForApplicantAsync(cancellationToken);
return Ok(result);
}
@@ -59,8 +56,7 @@ public class VisaApplicationController(
{
await visaApplicationCreateRequestValidator.ValidateAndThrowAsync(request, cancellationToken);
var userId = userIdProvider.GetUserId();
await visaApplicationRequestsHandler.HandleCreateRequestAsync(userId, request, cancellationToken);
await visaApplicationRequestsHandler.HandleCreateRequestAsync(request, cancellationToken);
return Ok();
}
@@ -75,8 +71,7 @@ public class VisaApplicationController(
[Route("{applicationId:guid}")]
public async Task<IActionResult> CloseApplication(Guid applicationId, CancellationToken cancellationToken)
{
var userId = userIdProvider.GetUserId();
await visaApplicationRequestsHandler.HandleCloseRequestAsync(userId, applicationId, cancellationToken);
await visaApplicationRequestsHandler.HandleCloseRequestAsync(applicationId, cancellationToken);
return Ok();
}

View File

@@ -6,11 +6,11 @@ using FluentValidation;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace SchengenVisaApi.ExceptionFilters
namespace SchengenVisaApi.ExceptionFilters;
/// Handles <see cref="ApiException"/>
public class GlobalExceptionsFilter : IAsyncExceptionFilter
{
/// Handles <see cref="ApiException"/>
public class GlobalExceptionsFilter : IAsyncExceptionFilter
{
/// <inheritdoc cref="IExceptionFilter.OnException"/>
public async Task OnExceptionAsync(ExceptionContext context)
{
@@ -68,5 +68,4 @@ namespace SchengenVisaApi.ExceptionFilters
await Results.Problem(problemDetails).ExecuteAsync(context.HttpContext);
context.ExceptionHandled = true;
}
}
}