Admin controller, Locations controller, requests to add available countries, request to get available countries

This commit is contained in:
2024-08-17 21:30:51 +03:00
parent 7cbe3d9698
commit 3cb2083222
59 changed files with 477 additions and 167 deletions

View File

@@ -1,4 +1,4 @@
namespace ApplicationLayer.DataAccessingServices.VisaApplications.Models;
namespace ApplicationLayer.DataAccessingServices.Applicants.Models;
public class AddressModel
{

View File

@@ -1,4 +1,4 @@
namespace ApplicationLayer.DataAccessingServices.VisaApplications.Models;
namespace ApplicationLayer.DataAccessingServices.Applicants.Models;
public class PlaceOfWorkModel
{

View File

@@ -4,4 +4,8 @@ using Domains.ApplicantDomain;
namespace ApplicationLayer.DataAccessingServices.Applicants.NeededServices;
/// Repository pattern for <see cref="Applicant"/>
public interface IApplicantsRepository : IGenericRepository<Applicant>;
public interface IApplicantsRepository : IGenericRepository<Applicant>
{
/// Find <see cref="Applicant"/> by Identifier
Task<Applicant> FindByUserIdAsync(Guid userId, CancellationToken cancellationToken);
}

View File

@@ -0,0 +1,28 @@
using ApplicationLayer.DataAccessingServices.AuthServices.LoginService.Exceptions;
using ApplicationLayer.DataAccessingServices.AuthServices.NeededServices;
using ApplicationLayer.DataAccessingServices.AuthServices.Requests;
using Domains.Users;
namespace ApplicationLayer.DataAccessingServices.AuthServices.LoginService
{
public class DevelopmentLoginService(IUsersRepository users, ITokenGenerator tokenGenerator) : ILoginService
{
async Task<string> ILoginService.LoginAsync(UserLoginRequest request, CancellationToken cancellationToken)
{
if (request is { Email: "admin@mail.ru", Password: "admin" })
{
var admin = new User { Role = Role.Admin };
return tokenGenerator.CreateToken(admin);
}
var user = await users.FindByEmailAsync(request.Email, cancellationToken);
if (user is null || user.Password != request.Password)
{
throw new IncorrectLoginDataException();
}
return tokenGenerator.CreateToken(user);
}
}
}

View File

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

View File

@@ -0,0 +1,12 @@
using ApplicationLayer.DataAccessingServices.AuthServices.Requests;
namespace ApplicationLayer.DataAccessingServices.AuthServices.LoginService
{
/// Handles <see cref="UserLoginRequest"/>
public interface ILoginService
{
/// Handle <see cref="UserLoginRequest"/>
/// <returns>JWT-token</returns>
Task<string> LoginAsync(UserLoginRequest request, CancellationToken cancellationToken);
}
}

View File

@@ -0,0 +1,21 @@
using ApplicationLayer.DataAccessingServices.AuthServices.LoginService.Exceptions;
using ApplicationLayer.DataAccessingServices.AuthServices.NeededServices;
using ApplicationLayer.DataAccessingServices.AuthServices.Requests;
namespace ApplicationLayer.DataAccessingServices.AuthServices.LoginService
{
/// <inheritdoc cref="ILoginService"/>
public class LoginService(IUsersRepository users, ITokenGenerator tokenGenerator) : ILoginService
{
async Task<string> ILoginService.LoginAsync(UserLoginRequest request, CancellationToken cancellationToken)
{
var user = await users.FindByEmailAsync(request.Email, cancellationToken);
if (user is null || user.Password != request.Password)
{
throw new IncorrectLoginDataException();
}
return tokenGenerator.CreateToken(user);
}
}
}

View File

@@ -0,0 +1,9 @@
using Domains.Users;
namespace ApplicationLayer.DataAccessingServices.AuthServices.NeededServices
{
public interface ITokenGenerator
{
string CreateToken(User user);
}
}

View File

@@ -0,0 +1,15 @@
using ApplicationLayer.GeneralNeededServices;
using Domains.Users;
namespace ApplicationLayer.DataAccessingServices.AuthServices.NeededServices
{
/// 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>
/// <returns>User or null if not found</returns>
Task<User?> FindByEmailAsync(string email, CancellationToken cancellationToken);
}
}

View File

@@ -0,0 +1,7 @@
using ApplicationLayer.DataAccessingServices.AuthServices.Requests;
using ApplicationLayer.GeneralExceptions;
namespace ApplicationLayer.DataAccessingServices.AuthServices.RegisterService.Exceptions
{
public class UserAlreadyExistsException(RegisterApplicantRequest request) : AlreadyExistsException($"User with email '{request.Email}' already exists");
}

View File

@@ -0,0 +1,11 @@
using ApplicationLayer.DataAccessingServices.AuthServices.Requests;
namespace ApplicationLayer.DataAccessingServices.AuthServices.RegisterService
{
/// Handles <see cref="RegisterApplicantRequest"/>
public interface IRegisterService
{
/// Handle <see cref="RegisterApplicantRequest"/>
Task Register(RegisterApplicantRequest request, CancellationToken cancellationToken);
}
}

View File

@@ -0,0 +1,71 @@
using ApplicationLayer.DataAccessingServices.Applicants.NeededServices;
using ApplicationLayer.DataAccessingServices.AuthServices.NeededServices;
using ApplicationLayer.DataAccessingServices.AuthServices.RegisterService.Exceptions;
using ApplicationLayer.DataAccessingServices.AuthServices.Requests;
using ApplicationLayer.DataAccessingServices.Locations.NeededServices;
using ApplicationLayer.GeneralNeededServices;
using Domains.ApplicantDomain;
using Domains.Users;
namespace ApplicationLayer.DataAccessingServices.AuthServices.RegisterService
{
/// <inheritdoc cref="IRegisterService"/>
public class RegisterService(
IUsersRepository users,
IApplicantsRepository applicants,
ICitiesRepository cities,
IUnitOfWork unitOfWork) : IRegisterService
{
async Task IRegisterService.Register(RegisterApplicantRequest request, CancellationToken cancellationToken)
{
if (await users.FindByEmailAsync(request.Email, cancellationToken) is not null)
{
throw new UserAlreadyExistsException(request);
}
//TODO mapper
var user = new User { Email = request.Email, Password = request.Password, Role = Role.Applicant };
var applicantCity = await cities.GetByIdAsync(request.CityOfBirthId, cancellationToken);
var placeOfWorkCity = await cities.GetByIdAsync(request.PlaceOfWork.Address.CityId, cancellationToken);
var placeOfWorkAddress = new Address
{
Country = placeOfWorkCity.Country,
City = placeOfWorkCity,
Building = request.PlaceOfWork.Address.Building,
Street = request.PlaceOfWork.Address.Street
};
var placeOfWork = new PlaceOfWork
{
Name = request.PlaceOfWork.Name,
Address = placeOfWorkAddress,
PhoneNum = request.PlaceOfWork.PhoneNum
};
var applicant = new Applicant
{
Citizenship = request.Citizenship,
CitizenshipByBirth = request.CitizenshipByBirth,
Gender = request.Gender,
Name = request.ApplicantName,
Passport = request.Passport,
BirthDate = request.BirthDate,
FatherName = request.FatherName,
JobTitle = request.JobTitle,
MaritalStatus = request.MaritalStatus,
MotherName = request.MotherName,
UserId = user.Id,
CityOfBirth = applicantCity,
CountryOfBirth = applicantCity.Country,
IsNonResident = request.IsNonResident,
PlaceOfWork = placeOfWork
};
await users.AddAsync(user, cancellationToken);
await applicants.AddAsync(applicant, cancellationToken);
await unitOfWork.SaveAsync(cancellationToken);
}
}
}

View File

@@ -0,0 +1,22 @@
using ApplicationLayer.DataAccessingServices.Applicants.Models;
using Domains.ApplicantDomain;
namespace ApplicationLayer.DataAccessingServices.AuthServices.Requests
{
public record RegisterApplicantRequest(
string Email,
string Password,
Name ApplicantName,
Passport Passport,
DateTime BirthDate,
Guid CityOfBirthId,
string Citizenship,
string CitizenshipByBirth,
Gender Gender,
MaritalStatus MaritalStatus,
Name FatherName,
Name MotherName,
string JobTitle,
PlaceOfWorkModel PlaceOfWork,
bool IsNonResident);
}

View File

@@ -0,0 +1,4 @@
namespace ApplicationLayer.DataAccessingServices.AuthServices.Requests
{
public record UserLoginRequest(string Email, string Password);
}

View File

@@ -3,4 +3,11 @@ using Domains.LocationDomain;
namespace ApplicationLayer.DataAccessingServices.Locations.NeededServices;
public interface ICountriesRepository : IGenericRepository<Country>;
public interface ICountriesRepository : IGenericRepository<Country>
{
/// Gets country by name
/// <param name="countryName">Name of country to seek</param>
/// <param name="cancellationToken">Cancellation Token</param>
/// <returns>Country or null if not found</returns>
Task<Country?> FindByName(string countryName, CancellationToken cancellationToken);
}

View File

@@ -0,0 +1,36 @@
using ApplicationLayer.DataAccessingServices.Locations.NeededServices;
using ApplicationLayer.DataAccessingServices.Locations.RequestHandlers.AdminRequests.Exceptions;
using ApplicationLayer.DataAccessingServices.Locations.Requests;
using ApplicationLayer.GeneralNeededServices;
using Domains.LocationDomain;
namespace ApplicationLayer.DataAccessingServices.Locations.RequestHandlers.AdminRequests
{
/// <inheritdoc cref="IEditLocationsRequestsHandler"/>
public class EditLocationsRequestsHandler(ICountriesRepository countries, IUnitOfWork unitOfWork) : IEditLocationsRequestsHandler
{
async Task IEditLocationsRequestsHandler.AddCountryAsync(AddCountryRequest request, CancellationToken cancellationToken)
{
if (await countries.FindByName(request.CountryName, cancellationToken) is not null)
{
throw new CountryAlreadyExists(request.CountryName);
}
if (request.Cities.Distinct().Count() < request.Cities.Length)
{
throw new MultipleIdenticalCitiesInCountry();
}
var country = new Country
{
Name = request.CountryName,
IsSchengen = request.IsSchengen,
Cities = request.Cities.Select(cityName => new City { Name = cityName }).ToList()
};
await countries.AddAsync(country, cancellationToken);
await unitOfWork.SaveAsync(cancellationToken);
}
}
}

View File

@@ -0,0 +1,6 @@
using ApplicationLayer.GeneralExceptions;
namespace ApplicationLayer.DataAccessingServices.Locations.RequestHandlers.AdminRequests.Exceptions
{
public class CountryAlreadyExists(string countryName) : AlreadyExistsException($"{countryName} already exists.");
}

View File

@@ -0,0 +1,6 @@
using ApplicationLayer.GeneralExceptions;
namespace ApplicationLayer.DataAccessingServices.Locations.RequestHandlers.AdminRequests.Exceptions
{
public class MultipleIdenticalCitiesInCountry() : ApiException("There are multiple cities with one name in the country.");
}

View File

@@ -0,0 +1,11 @@
using ApplicationLayer.DataAccessingServices.Locations.Requests;
namespace ApplicationLayer.DataAccessingServices.Locations.RequestHandlers.AdminRequests
{
/// Handles edit requests of locations from admins
public interface IEditLocationsRequestsHandler
{
/// Handles add country requests
Task AddCountryAsync(AddCountryRequest request, CancellationToken cancellationToken);
}
}

View File

@@ -0,0 +1,12 @@
using Domains.LocationDomain;
namespace ApplicationLayer.DataAccessingServices.Locations.RequestHandlers.ApplicantRequests
{
/// Handles location requests
public interface ILocationRequestsHandler
{
/// Handle get request
/// <returns>List of available countries</returns>
Task<List<Country>> HandleGetRequestAsync(CancellationToken cancellationToken);
}
}

View File

@@ -0,0 +1,14 @@
using ApplicationLayer.DataAccessingServices.Locations.NeededServices;
using Domains.LocationDomain;
namespace ApplicationLayer.DataAccessingServices.Locations.RequestHandlers.ApplicantRequests
{
/// <inheritdoc cref="ILocationRequestsHandler"/>
public class LocationRequestsHandler(ICountriesRepository countries) : ILocationRequestsHandler
{
async Task<List<Country>> ILocationRequestsHandler.HandleGetRequestAsync(CancellationToken cancellationToken)
{
return await countries.GetAllAsync(cancellationToken);
}
}
}

View File

@@ -0,0 +1,4 @@
namespace ApplicationLayer.DataAccessingServices.Locations.Requests
{
public record AddCountryRequest(string CountryName, bool IsSchengen, string[] Cities);
}

View File

@@ -3,9 +3,9 @@ using Domains.VisaApplicationDomain;
namespace ApplicationLayer.DataAccessingServices.VisaApplications.Handlers;
public interface IVisaApplicationsRequestHandler
public interface IVisaApplicationRequestsHandler
{
Task<List<VisaApplication>> Get(CancellationToken cancellationToken);
void HandleCreateRequest(VisaApplicationCreateRequest request, CancellationToken cancellationToken);
void HandleCreateRequest(Guid userId, VisaApplicationCreateRequest request, CancellationToken cancellationToken);
}

View File

@@ -1,8 +1,9 @@
using ApplicationLayer.DataAccessingServices.Locations.NeededServices;
using ApplicationLayer.DataAccessingServices.Applicants.NeededServices;
using ApplicationLayer.DataAccessingServices.Locations.NeededServices;
using ApplicationLayer.DataAccessingServices.VisaApplications.Models;
using ApplicationLayer.DataAccessingServices.VisaApplications.NeededServices;
using ApplicationLayer.DataAccessingServices.VisaApplications.Requests;
using Domains.ApplicantDomain;
using ApplicationLayer.GeneralNeededServices;
using Domains.VisaApplicationDomain;
namespace ApplicationLayer.DataAccessingServices.VisaApplications.Handlers;
@@ -10,42 +11,18 @@ namespace ApplicationLayer.DataAccessingServices.VisaApplications.Handlers;
/// Handles visa requests
public class VisaApplicationRequestsHandler(
IVisaApplicationsRepository applications,
ICitiesRepository cities,
ICountriesRepository countries) : IVisaApplicationsRequestHandler
IApplicantsRepository applicants,
ICountriesRepository countries,
IUnitOfWork unitOfWork) : IVisaApplicationRequestsHandler
{
public async Task<List<VisaApplication>> Get(CancellationToken cancellationToken) => await applications.GetAllAsync(cancellationToken);
public async void HandleCreateRequest(VisaApplicationCreateRequest request, CancellationToken cancellationToken)
public async void HandleCreateRequest(Guid userId, VisaApplicationCreateRequest request, CancellationToken cancellationToken)
{
//TODO fix
//TODO mapper
var cityOfBirth = await cities.GetByIdAsync(request.BirthCityId, cancellationToken);
var cityOfWork = await cities.GetByIdAsync(request.PlaceOfWork.Address.CityId, cancellationToken);
var addressOfWork = new Address
{
City = cityOfWork,
Country = cityOfWork.Country,
Building = request.PlaceOfWork.Address.Building,
Street = request.PlaceOfWork.Address.Street
};
var applicant = new Applicant
{
MaritalStatus = request.MaritalStatus,
Name = request.FullName,
Passport = request.Passport,
Gender = request.Gender,
Citizenship = request.CitizenShip,
BirthDate = request.BirthDate,
FatherName = request.FatherName,
JobTitle = request.JobTitle,
MotherName = request.MotherName,
CitizenshipByBirth = request.CitizenshipByBirth,
CityOfBirth = cityOfBirth,
CountryOfBirth = cityOfBirth.Country,
IsNonResident = request.IsNonResident,
PlaceOfWork = new PlaceOfWork { Address = addressOfWork, Name = request.PlaceOfWork.Name, PhoneNum = request.PlaceOfWork.PhoneNum }
};
var applicant = await applicants.FindByUserIdAsync(userId, cancellationToken);
var pastVisits = request.PastVisits.Select(m => ConvertPastVisitModelToPastVisit(m, cancellationToken).Result).ToList();
var visaApplication = new VisaApplication
@@ -64,7 +41,8 @@ public class VisaApplicationRequestsHandler(
};
await applications.AddAsync(visaApplication, cancellationToken);
await applications.SaveAsync(cancellationToken);
await unitOfWork.SaveAsync(cancellationToken);
}
private async Task<PastVisit> ConvertPastVisitModelToPastVisit(PastVisitModel model, CancellationToken cancellationToken)

View File

@@ -1,25 +1,11 @@
using ApplicationLayer.DataAccessingServices.VisaApplications.Models;
using Domains.ApplicantDomain;
using Domains.VisaApplicationDomain;
namespace ApplicationLayer.DataAccessingServices.VisaApplications.Requests;
/// Model of visa request from user
public record VisaApplicationCreateRequest(
Name FullName,
Passport Passport,
DateTime BirthDate,
Guid BirthCityId,
string CitizenShip,
string CitizenshipByBirth,
Gender Gender,
MaritalStatus MaritalStatus,
Name FatherName,
Name MotherName,
bool IsNonResident,
ReentryPermit ReentryPermit,
string JobTitle,
PlaceOfWorkModel PlaceOfWork,
Guid DestinationCountryId,
VisaCategory VisaCategory,
bool IsForGroup,