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 +0,0 @@
namespace ApplicationLayer.AuthServices.LoginService.Exceptions
{
public class IncorrectLoginDataException() : Exception("Incorrect email or password");
}

View File

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

View File

@@ -1,31 +0,0 @@
using ApplicationLayer.AuthServices.NeededServices;
using ApplicationLayer.AuthServices.RegisterService.Exceptions;
using ApplicationLayer.AuthServices.Requests;
using Domains.Users;
namespace ApplicationLayer.AuthServices.RegisterService
{
/// <inheritdoc cref="IRegisterService"/>
public class RegisterService(IUsersRepository users) : 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
};
await users.AddAsync(user, cancellationToken);
await users.SaveAsync(cancellationToken);
users.GetAllAsync(cancellationToken);
}
}
}

View File

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

View File

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

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

@@ -1,6 +1,6 @@
using ApplicationLayer.AuthServices.Requests;
using ApplicationLayer.DataAccessingServices.AuthServices.Requests;
namespace ApplicationLayer.AuthServices.LoginService
namespace ApplicationLayer.DataAccessingServices.AuthServices.LoginService
{
/// Handles <see cref="UserLoginRequest"/>
public interface ILoginService

View File

@@ -1,8 +1,8 @@
using ApplicationLayer.AuthServices.LoginService.Exceptions;
using ApplicationLayer.AuthServices.NeededServices;
using ApplicationLayer.AuthServices.Requests;
using ApplicationLayer.DataAccessingServices.AuthServices.LoginService.Exceptions;
using ApplicationLayer.DataAccessingServices.AuthServices.NeededServices;
using ApplicationLayer.DataAccessingServices.AuthServices.Requests;
namespace ApplicationLayer.AuthServices.LoginService
namespace ApplicationLayer.DataAccessingServices.AuthServices.LoginService
{
/// <inheritdoc cref="ILoginService"/>
public class LoginService(IUsersRepository users, ITokenGenerator tokenGenerator) : ILoginService

View File

@@ -1,6 +1,6 @@
using Domains.Users;
namespace ApplicationLayer.AuthServices.NeededServices
namespace ApplicationLayer.DataAccessingServices.AuthServices.NeededServices
{
public interface ITokenGenerator
{

View File

@@ -1,7 +1,7 @@
using ApplicationLayer.GeneralNeededServices;
using Domains.Users;
namespace ApplicationLayer.AuthServices.NeededServices
namespace ApplicationLayer.DataAccessingServices.AuthServices.NeededServices
{
/// Repository pattern for <see cref="User"/>
public interface IUsersRepository : IGenericRepository<User>

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

@@ -1,6 +1,6 @@
using ApplicationLayer.AuthServices.Requests;
using ApplicationLayer.DataAccessingServices.AuthServices.Requests;
namespace ApplicationLayer.AuthServices.RegisterService
namespace ApplicationLayer.DataAccessingServices.AuthServices.RegisterService
{
/// Handles <see cref="RegisterApplicantRequest"/>
public interface IRegisterService

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,

View File

@@ -1,5 +1,7 @@
using ApplicationLayer.AuthServices.LoginService;
using ApplicationLayer.AuthServices.RegisterService;
using ApplicationLayer.DataAccessingServices.AuthServices.LoginService;
using ApplicationLayer.DataAccessingServices.AuthServices.RegisterService;
using ApplicationLayer.DataAccessingServices.Locations.RequestHandlers.AdminRequests;
using ApplicationLayer.DataAccessingServices.Locations.RequestHandlers.ApplicantRequests;
using ApplicationLayer.DataAccessingServices.VisaApplications.Handlers;
using Microsoft.Extensions.DependencyInjection;
@@ -9,12 +11,22 @@ namespace ApplicationLayer;
public static class DependencyInjection
{
/// Add services for Application layer
public static IServiceCollection AddApplicationLayer(this IServiceCollection services)
public static IServiceCollection AddApplicationLayer(this IServiceCollection services, bool isDevelopment = false)
{
services.AddScoped<IVisaApplicationsRequestHandler, VisaApplicationRequestsHandler>();
services.AddScoped<IVisaApplicationRequestsHandler, VisaApplicationRequestsHandler>();
services.AddScoped<ILocationRequestsHandler, LocationRequestsHandler>();
services.AddScoped<IEditLocationsRequestsHandler, EditLocationsRequestsHandler>();
services.AddScoped<IRegisterService, RegisterService>();
services.AddScoped<ILoginService, LoginService>();
if (isDevelopment)
{
services.AddScoped<ILoginService, DevelopmentLoginService>();
}
else
{
services.AddScoped<ILoginService, LoginService>();
}
return services;
}

View File

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

View File

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

View File

@@ -34,7 +34,4 @@ public interface IGenericRepository<T> where T : class, IEntity
/// </summary>
/// <param name="entity">Entity to remove</param>
void Remove(T entity);
/// Save changes in storage
Task SaveAsync(CancellationToken cancellationToken);
}

View File

@@ -0,0 +1,8 @@
namespace ApplicationLayer.GeneralNeededServices;
public interface IUnitOfWork
{
/// Saves changes in data storage
/// <param name="cancellationToken">Cancellation Token</param>
Task SaveAsync(CancellationToken cancellationToken);
}