diff --git a/SchengenVisaApi/ApplicationLayer/AuthServices/LoginService/Exceptions/IncorrectLoginDataException.cs b/SchengenVisaApi/ApplicationLayer/AuthServices/LoginService/Exceptions/IncorrectLoginDataException.cs
deleted file mode 100644
index d5ee16d..0000000
--- a/SchengenVisaApi/ApplicationLayer/AuthServices/LoginService/Exceptions/IncorrectLoginDataException.cs
+++ /dev/null
@@ -1,4 +0,0 @@
-namespace ApplicationLayer.AuthServices.LoginService.Exceptions
-{
- public class IncorrectLoginDataException() : Exception("Incorrect email or password");
-}
diff --git a/SchengenVisaApi/ApplicationLayer/AuthServices/RegisterService/Exceptions/UserAlreadyExistsException.cs b/SchengenVisaApi/ApplicationLayer/AuthServices/RegisterService/Exceptions/UserAlreadyExistsException.cs
deleted file mode 100644
index bd32eb1..0000000
--- a/SchengenVisaApi/ApplicationLayer/AuthServices/RegisterService/Exceptions/UserAlreadyExistsException.cs
+++ /dev/null
@@ -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");
-}
diff --git a/SchengenVisaApi/ApplicationLayer/AuthServices/RegisterService/RegisterService.cs b/SchengenVisaApi/ApplicationLayer/AuthServices/RegisterService/RegisterService.cs
deleted file mode 100644
index 83ab4cc..0000000
--- a/SchengenVisaApi/ApplicationLayer/AuthServices/RegisterService/RegisterService.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using ApplicationLayer.AuthServices.NeededServices;
-using ApplicationLayer.AuthServices.RegisterService.Exceptions;
-using ApplicationLayer.AuthServices.Requests;
-using Domains.Users;
-
-namespace ApplicationLayer.AuthServices.RegisterService
-{
- ///
- 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);
- }
- }
-}
diff --git a/SchengenVisaApi/ApplicationLayer/AuthServices/Requests/RegisterApplicantRequest.cs b/SchengenVisaApi/ApplicationLayer/AuthServices/Requests/RegisterApplicantRequest.cs
deleted file mode 100644
index e919545..0000000
--- a/SchengenVisaApi/ApplicationLayer/AuthServices/Requests/RegisterApplicantRequest.cs
+++ /dev/null
@@ -1,4 +0,0 @@
-namespace ApplicationLayer.AuthServices.Requests
-{
- public record RegisterApplicantRequest(string Email, string Password);
-}
diff --git a/SchengenVisaApi/ApplicationLayer/AuthServices/Requests/UserLoginRequest.cs b/SchengenVisaApi/ApplicationLayer/AuthServices/Requests/UserLoginRequest.cs
deleted file mode 100644
index a1ed5cf..0000000
--- a/SchengenVisaApi/ApplicationLayer/AuthServices/Requests/UserLoginRequest.cs
+++ /dev/null
@@ -1,4 +0,0 @@
-namespace ApplicationLayer.AuthServices.Requests
-{
- public record UserLoginRequest(string Email, string Password);
-}
diff --git a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/VisaApplications/Models/AddressModel.cs b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Applicants/Models/AddressModel.cs
similarity index 77%
rename from SchengenVisaApi/ApplicationLayer/DataAccessingServices/VisaApplications/Models/AddressModel.cs
rename to SchengenVisaApi/ApplicationLayer/DataAccessingServices/Applicants/Models/AddressModel.cs
index 5e6909b..fb9cfc2 100644
--- a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/VisaApplications/Models/AddressModel.cs
+++ b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Applicants/Models/AddressModel.cs
@@ -1,4 +1,4 @@
-namespace ApplicationLayer.DataAccessingServices.VisaApplications.Models;
+namespace ApplicationLayer.DataAccessingServices.Applicants.Models;
public class AddressModel
{
diff --git a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/VisaApplications/Models/PlaceOfWorkModel.cs b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Applicants/Models/PlaceOfWorkModel.cs
similarity index 78%
rename from SchengenVisaApi/ApplicationLayer/DataAccessingServices/VisaApplications/Models/PlaceOfWorkModel.cs
rename to SchengenVisaApi/ApplicationLayer/DataAccessingServices/Applicants/Models/PlaceOfWorkModel.cs
index 42b6b1d..a69fc82 100644
--- a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/VisaApplications/Models/PlaceOfWorkModel.cs
+++ b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Applicants/Models/PlaceOfWorkModel.cs
@@ -1,4 +1,4 @@
-namespace ApplicationLayer.DataAccessingServices.VisaApplications.Models;
+namespace ApplicationLayer.DataAccessingServices.Applicants.Models;
public class PlaceOfWorkModel
{
diff --git a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Applicants/NeededServices/IApplicantsRepository.cs b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Applicants/NeededServices/IApplicantsRepository.cs
index 1ac3667..a355353 100644
--- a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Applicants/NeededServices/IApplicantsRepository.cs
+++ b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Applicants/NeededServices/IApplicantsRepository.cs
@@ -4,4 +4,8 @@ using Domains.ApplicantDomain;
namespace ApplicationLayer.DataAccessingServices.Applicants.NeededServices;
/// Repository pattern for
-public interface IApplicantsRepository : IGenericRepository;
+public interface IApplicantsRepository : IGenericRepository
+{
+ /// Find by Identifier
+ Task FindByUserIdAsync(Guid userId, CancellationToken cancellationToken);
+}
diff --git a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/LoginService/DevelopmentLoginService.cs b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/LoginService/DevelopmentLoginService.cs
new file mode 100644
index 0000000..b7e2fa3
--- /dev/null
+++ b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/LoginService/DevelopmentLoginService.cs
@@ -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 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);
+ }
+ }
+}
diff --git a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/LoginService/Exceptions/IncorrectLoginDataException.cs b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/LoginService/Exceptions/IncorrectLoginDataException.cs
new file mode 100644
index 0000000..4c2f887
--- /dev/null
+++ b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/LoginService/Exceptions/IncorrectLoginDataException.cs
@@ -0,0 +1,6 @@
+using ApplicationLayer.GeneralExceptions;
+
+namespace ApplicationLayer.DataAccessingServices.AuthServices.LoginService.Exceptions
+{
+ public class IncorrectLoginDataException() : ApiException("Incorrect email or password");
+}
diff --git a/SchengenVisaApi/ApplicationLayer/AuthServices/LoginService/ILoginService.cs b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/LoginService/ILoginService.cs
similarity index 66%
rename from SchengenVisaApi/ApplicationLayer/AuthServices/LoginService/ILoginService.cs
rename to SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/LoginService/ILoginService.cs
index b9f26c5..0ca04ea 100644
--- a/SchengenVisaApi/ApplicationLayer/AuthServices/LoginService/ILoginService.cs
+++ b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/LoginService/ILoginService.cs
@@ -1,6 +1,6 @@
-using ApplicationLayer.AuthServices.Requests;
+using ApplicationLayer.DataAccessingServices.AuthServices.Requests;
-namespace ApplicationLayer.AuthServices.LoginService
+namespace ApplicationLayer.DataAccessingServices.AuthServices.LoginService
{
/// Handles
public interface ILoginService
diff --git a/SchengenVisaApi/ApplicationLayer/AuthServices/LoginService/LoginService.cs b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/LoginService/LoginService.cs
similarity index 66%
rename from SchengenVisaApi/ApplicationLayer/AuthServices/LoginService/LoginService.cs
rename to SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/LoginService/LoginService.cs
index d0279a9..e662d2e 100644
--- a/SchengenVisaApi/ApplicationLayer/AuthServices/LoginService/LoginService.cs
+++ b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/LoginService/LoginService.cs
@@ -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
{
///
public class LoginService(IUsersRepository users, ITokenGenerator tokenGenerator) : ILoginService
diff --git a/SchengenVisaApi/ApplicationLayer/AuthServices/NeededServices/ITokenGenerator.cs b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/NeededServices/ITokenGenerator.cs
similarity index 60%
rename from SchengenVisaApi/ApplicationLayer/AuthServices/NeededServices/ITokenGenerator.cs
rename to SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/NeededServices/ITokenGenerator.cs
index 1c9b2fa..bb2525d 100644
--- a/SchengenVisaApi/ApplicationLayer/AuthServices/NeededServices/ITokenGenerator.cs
+++ b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/NeededServices/ITokenGenerator.cs
@@ -1,6 +1,6 @@
using Domains.Users;
-namespace ApplicationLayer.AuthServices.NeededServices
+namespace ApplicationLayer.DataAccessingServices.AuthServices.NeededServices
{
public interface ITokenGenerator
{
diff --git a/SchengenVisaApi/ApplicationLayer/AuthServices/NeededServices/IUsersRepository.cs b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/NeededServices/IUsersRepository.cs
similarity index 87%
rename from SchengenVisaApi/ApplicationLayer/AuthServices/NeededServices/IUsersRepository.cs
rename to SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/NeededServices/IUsersRepository.cs
index 3f1f206..4f74f88 100644
--- a/SchengenVisaApi/ApplicationLayer/AuthServices/NeededServices/IUsersRepository.cs
+++ b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/NeededServices/IUsersRepository.cs
@@ -1,7 +1,7 @@
using ApplicationLayer.GeneralNeededServices;
using Domains.Users;
-namespace ApplicationLayer.AuthServices.NeededServices
+namespace ApplicationLayer.DataAccessingServices.AuthServices.NeededServices
{
/// Repository pattern for
public interface IUsersRepository : IGenericRepository
diff --git a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/RegisterService/Exceptions/UserAlreadyExistsException.cs b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/RegisterService/Exceptions/UserAlreadyExistsException.cs
new file mode 100644
index 0000000..9a115be
--- /dev/null
+++ b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/RegisterService/Exceptions/UserAlreadyExistsException.cs
@@ -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");
+}
diff --git a/SchengenVisaApi/ApplicationLayer/AuthServices/RegisterService/IRegisterService.cs b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/RegisterService/IRegisterService.cs
similarity index 63%
rename from SchengenVisaApi/ApplicationLayer/AuthServices/RegisterService/IRegisterService.cs
rename to SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/RegisterService/IRegisterService.cs
index 54c0e0c..be331be 100644
--- a/SchengenVisaApi/ApplicationLayer/AuthServices/RegisterService/IRegisterService.cs
+++ b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/RegisterService/IRegisterService.cs
@@ -1,6 +1,6 @@
-using ApplicationLayer.AuthServices.Requests;
+using ApplicationLayer.DataAccessingServices.AuthServices.Requests;
-namespace ApplicationLayer.AuthServices.RegisterService
+namespace ApplicationLayer.DataAccessingServices.AuthServices.RegisterService
{
/// Handles
public interface IRegisterService
diff --git a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/RegisterService/RegisterService.cs b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/RegisterService/RegisterService.cs
new file mode 100644
index 0000000..9611916
--- /dev/null
+++ b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/RegisterService/RegisterService.cs
@@ -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
+{
+ ///
+ 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);
+ }
+ }
+}
diff --git a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/Requests/RegisterApplicantRequest.cs b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/Requests/RegisterApplicantRequest.cs
new file mode 100644
index 0000000..bbaa0da
--- /dev/null
+++ b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/Requests/RegisterApplicantRequest.cs
@@ -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);
+}
diff --git a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/Requests/UserLoginRequest.cs b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/Requests/UserLoginRequest.cs
new file mode 100644
index 0000000..1cf039b
--- /dev/null
+++ b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/AuthServices/Requests/UserLoginRequest.cs
@@ -0,0 +1,4 @@
+namespace ApplicationLayer.DataAccessingServices.AuthServices.Requests
+{
+ public record UserLoginRequest(string Email, string Password);
+}
diff --git a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Locations/NeededServices/ICountriesRepository.cs b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Locations/NeededServices/ICountriesRepository.cs
index ca86c9e..3de0b05 100644
--- a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Locations/NeededServices/ICountriesRepository.cs
+++ b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Locations/NeededServices/ICountriesRepository.cs
@@ -3,4 +3,11 @@ using Domains.LocationDomain;
namespace ApplicationLayer.DataAccessingServices.Locations.NeededServices;
-public interface ICountriesRepository : IGenericRepository;
+public interface ICountriesRepository : IGenericRepository
+{
+ /// Gets country by name
+ /// Name of country to seek
+ /// Cancellation Token
+ /// Country or null if not found
+ Task FindByName(string countryName, CancellationToken cancellationToken);
+}
diff --git a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Locations/RequestHandlers/AdminRequests/EditLocationsRequestsHandler.cs b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Locations/RequestHandlers/AdminRequests/EditLocationsRequestsHandler.cs
new file mode 100644
index 0000000..e84e975
--- /dev/null
+++ b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Locations/RequestHandlers/AdminRequests/EditLocationsRequestsHandler.cs
@@ -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
+{
+ ///
+ 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);
+ }
+ }
+}
diff --git a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Locations/RequestHandlers/AdminRequests/Exceptions/CountryAlreadyExists.cs b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Locations/RequestHandlers/AdminRequests/Exceptions/CountryAlreadyExists.cs
new file mode 100644
index 0000000..b7e6d36
--- /dev/null
+++ b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Locations/RequestHandlers/AdminRequests/Exceptions/CountryAlreadyExists.cs
@@ -0,0 +1,6 @@
+using ApplicationLayer.GeneralExceptions;
+
+namespace ApplicationLayer.DataAccessingServices.Locations.RequestHandlers.AdminRequests.Exceptions
+{
+ public class CountryAlreadyExists(string countryName) : AlreadyExistsException($"{countryName} already exists.");
+}
diff --git a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Locations/RequestHandlers/AdminRequests/Exceptions/MultipleIdenticalCitiesInCountry.cs b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Locations/RequestHandlers/AdminRequests/Exceptions/MultipleIdenticalCitiesInCountry.cs
new file mode 100644
index 0000000..a32da0a
--- /dev/null
+++ b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Locations/RequestHandlers/AdminRequests/Exceptions/MultipleIdenticalCitiesInCountry.cs
@@ -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.");
+}
diff --git a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Locations/RequestHandlers/AdminRequests/IEditLocationsRequestsHandler.cs b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Locations/RequestHandlers/AdminRequests/IEditLocationsRequestsHandler.cs
new file mode 100644
index 0000000..d8c36a9
--- /dev/null
+++ b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Locations/RequestHandlers/AdminRequests/IEditLocationsRequestsHandler.cs
@@ -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);
+ }
+}
diff --git a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Locations/RequestHandlers/ApplicantRequests/ILocationRequestsHandler.cs b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Locations/RequestHandlers/ApplicantRequests/ILocationRequestsHandler.cs
new file mode 100644
index 0000000..7299308
--- /dev/null
+++ b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Locations/RequestHandlers/ApplicantRequests/ILocationRequestsHandler.cs
@@ -0,0 +1,12 @@
+using Domains.LocationDomain;
+
+namespace ApplicationLayer.DataAccessingServices.Locations.RequestHandlers.ApplicantRequests
+{
+ /// Handles location requests
+ public interface ILocationRequestsHandler
+ {
+ /// Handle get request
+ /// List of available countries
+ Task> HandleGetRequestAsync(CancellationToken cancellationToken);
+ }
+}
diff --git a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Locations/RequestHandlers/ApplicantRequests/LocationRequestsHandler.cs b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Locations/RequestHandlers/ApplicantRequests/LocationRequestsHandler.cs
new file mode 100644
index 0000000..f69951c
--- /dev/null
+++ b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Locations/RequestHandlers/ApplicantRequests/LocationRequestsHandler.cs
@@ -0,0 +1,14 @@
+using ApplicationLayer.DataAccessingServices.Locations.NeededServices;
+using Domains.LocationDomain;
+
+namespace ApplicationLayer.DataAccessingServices.Locations.RequestHandlers.ApplicantRequests
+{
+ ///
+ public class LocationRequestsHandler(ICountriesRepository countries) : ILocationRequestsHandler
+ {
+ async Task> ILocationRequestsHandler.HandleGetRequestAsync(CancellationToken cancellationToken)
+ {
+ return await countries.GetAllAsync(cancellationToken);
+ }
+ }
+}
diff --git a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Locations/Requests/AddCountryRequest.cs b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Locations/Requests/AddCountryRequest.cs
new file mode 100644
index 0000000..902e6e1
--- /dev/null
+++ b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/Locations/Requests/AddCountryRequest.cs
@@ -0,0 +1,4 @@
+namespace ApplicationLayer.DataAccessingServices.Locations.Requests
+{
+ public record AddCountryRequest(string CountryName, bool IsSchengen, string[] Cities);
+}
diff --git a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/VisaApplications/Handlers/IVisaApplicationsRequestHandler.cs b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/VisaApplications/Handlers/IVisaApplicationRequestsHandler.cs
similarity index 61%
rename from SchengenVisaApi/ApplicationLayer/DataAccessingServices/VisaApplications/Handlers/IVisaApplicationsRequestHandler.cs
rename to SchengenVisaApi/ApplicationLayer/DataAccessingServices/VisaApplications/Handlers/IVisaApplicationRequestsHandler.cs
index 7ef57b2..d5b52c5 100644
--- a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/VisaApplications/Handlers/IVisaApplicationsRequestHandler.cs
+++ b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/VisaApplications/Handlers/IVisaApplicationRequestsHandler.cs
@@ -3,9 +3,9 @@ using Domains.VisaApplicationDomain;
namespace ApplicationLayer.DataAccessingServices.VisaApplications.Handlers;
-public interface IVisaApplicationsRequestHandler
+public interface IVisaApplicationRequestsHandler
{
Task> Get(CancellationToken cancellationToken);
- void HandleCreateRequest(VisaApplicationCreateRequest request, CancellationToken cancellationToken);
+ void HandleCreateRequest(Guid userId, VisaApplicationCreateRequest request, CancellationToken cancellationToken);
}
diff --git a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/VisaApplications/Handlers/VisaApplicationRequestsHandler.cs b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/VisaApplications/Handlers/VisaApplicationRequestsHandler.cs
index dc25a53..4c99f20 100644
--- a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/VisaApplications/Handlers/VisaApplicationRequestsHandler.cs
+++ b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/VisaApplications/Handlers/VisaApplicationRequestsHandler.cs
@@ -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> 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 ConvertPastVisitModelToPastVisit(PastVisitModel model, CancellationToken cancellationToken)
diff --git a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/VisaApplications/Requests/VisaApplicationCreateRequest.cs b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/VisaApplications/Requests/VisaApplicationCreateRequest.cs
index 4a5bf1a..2127cc3 100644
--- a/SchengenVisaApi/ApplicationLayer/DataAccessingServices/VisaApplications/Requests/VisaApplicationCreateRequest.cs
+++ b/SchengenVisaApi/ApplicationLayer/DataAccessingServices/VisaApplications/Requests/VisaApplicationCreateRequest.cs
@@ -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,
diff --git a/SchengenVisaApi/ApplicationLayer/DependencyInjection.cs b/SchengenVisaApi/ApplicationLayer/DependencyInjection.cs
index 8fa5014..00977ec 100644
--- a/SchengenVisaApi/ApplicationLayer/DependencyInjection.cs
+++ b/SchengenVisaApi/ApplicationLayer/DependencyInjection.cs
@@ -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();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
services.AddScoped();
- services.AddScoped();
+
+ if (isDevelopment)
+ {
+ services.AddScoped();
+ }
+ else
+ {
+ services.AddScoped();
+ }
return services;
}
diff --git a/SchengenVisaApi/ApplicationLayer/GeneralExceptions/AlreadyExistsException.cs b/SchengenVisaApi/ApplicationLayer/GeneralExceptions/AlreadyExistsException.cs
new file mode 100644
index 0000000..51daa4e
--- /dev/null
+++ b/SchengenVisaApi/ApplicationLayer/GeneralExceptions/AlreadyExistsException.cs
@@ -0,0 +1,4 @@
+namespace ApplicationLayer.GeneralExceptions
+{
+ public class AlreadyExistsException(string message) : ApiException(message);
+}
diff --git a/SchengenVisaApi/ApplicationLayer/GeneralExceptions/ApiException.cs b/SchengenVisaApi/ApplicationLayer/GeneralExceptions/ApiException.cs
new file mode 100644
index 0000000..8f7ed6a
--- /dev/null
+++ b/SchengenVisaApi/ApplicationLayer/GeneralExceptions/ApiException.cs
@@ -0,0 +1,4 @@
+namespace ApplicationLayer.GeneralExceptions
+{
+ public class ApiException(string message) : Exception(message);
+}
diff --git a/SchengenVisaApi/ApplicationLayer/GeneralNeededServices/IGenericRepository.cs b/SchengenVisaApi/ApplicationLayer/GeneralNeededServices/IGenericRepository.cs
index d8474f7..e0d84ca 100644
--- a/SchengenVisaApi/ApplicationLayer/GeneralNeededServices/IGenericRepository.cs
+++ b/SchengenVisaApi/ApplicationLayer/GeneralNeededServices/IGenericRepository.cs
@@ -34,7 +34,4 @@ public interface IGenericRepository where T : class, IEntity
///
/// Entity to remove
void Remove(T entity);
-
- /// Save changes in storage
- Task SaveAsync(CancellationToken cancellationToken);
}
diff --git a/SchengenVisaApi/Infrastructure/Database/IUnitOfWork.cs b/SchengenVisaApi/ApplicationLayer/GeneralNeededServices/IUnitOfWork.cs
similarity index 77%
rename from SchengenVisaApi/Infrastructure/Database/IUnitOfWork.cs
rename to SchengenVisaApi/ApplicationLayer/GeneralNeededServices/IUnitOfWork.cs
index 821c4f1..cf7fef7 100644
--- a/SchengenVisaApi/Infrastructure/Database/IUnitOfWork.cs
+++ b/SchengenVisaApi/ApplicationLayer/GeneralNeededServices/IUnitOfWork.cs
@@ -1,8 +1,8 @@
-namespace Infrastructure.Database;
+namespace ApplicationLayer.GeneralNeededServices;
public interface IUnitOfWork
{
/// Saves changes in data storage
/// Cancellation Token
Task SaveAsync(CancellationToken cancellationToken);
-}
\ No newline at end of file
+}
diff --git a/SchengenVisaApi/Domains/ApplicantDomain/Applicant.cs b/SchengenVisaApi/Domains/ApplicantDomain/Applicant.cs
index dc2efa4..91404c2 100644
--- a/SchengenVisaApi/Domains/ApplicantDomain/Applicant.cs
+++ b/SchengenVisaApi/Domains/ApplicantDomain/Applicant.cs
@@ -6,7 +6,9 @@ namespace Domains.ApplicantDomain;
public class Applicant : IEntity
{
/// Unique identifier of the
- public Guid Id { get; set; }
+ public Guid Id { get; private set; } = Guid.NewGuid();
+
+ public Guid UserId { get; set; }
/// Full name of the
public Name Name { get; set; } = null!;
diff --git a/SchengenVisaApi/Domains/LocationDomain/City.cs b/SchengenVisaApi/Domains/LocationDomain/City.cs
index ebc0e31..1a646f6 100644
--- a/SchengenVisaApi/Domains/LocationDomain/City.cs
+++ b/SchengenVisaApi/Domains/LocationDomain/City.cs
@@ -1,4 +1,6 @@
-namespace Domains.LocationDomain;
+using System.Text.Json.Serialization;
+
+namespace Domains.LocationDomain;
/// Model of a city
public class City : IEntity
@@ -10,5 +12,6 @@ public class City : IEntity
public string Name { get; set; } = null!;
/// in which the city is located
+ [JsonIgnore]
public Country Country { get; set; } = null!;
-}
\ No newline at end of file
+}
diff --git a/SchengenVisaApi/Infrastructure/Auth/ServiceCollectionsExtensions.cs b/SchengenVisaApi/Infrastructure/Auth/ServiceCollectionsExtensions.cs
index 1138163..f72fc22 100644
--- a/SchengenVisaApi/Infrastructure/Auth/ServiceCollectionsExtensions.cs
+++ b/SchengenVisaApi/Infrastructure/Auth/ServiceCollectionsExtensions.cs
@@ -1,5 +1,5 @@
using System.IdentityModel.Tokens.Jwt;
-using ApplicationLayer.AuthServices.NeededServices;
+using ApplicationLayer.DataAccessingServices.AuthServices.NeededServices;
using ApplicationLayer.GeneralNeededServices;
using Microsoft.Extensions.DependencyInjection;
diff --git a/SchengenVisaApi/Infrastructure/Auth/TokenGenerator.cs b/SchengenVisaApi/Infrastructure/Auth/TokenGenerator.cs
index 54363b4..a7e5627 100644
--- a/SchengenVisaApi/Infrastructure/Auth/TokenGenerator.cs
+++ b/SchengenVisaApi/Infrastructure/Auth/TokenGenerator.cs
@@ -1,6 +1,6 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
-using ApplicationLayer.AuthServices.NeededServices;
+using ApplicationLayer.DataAccessingServices.AuthServices.NeededServices;
using ApplicationLayer.GeneralNeededServices;
using Domains.Users;
@@ -14,7 +14,7 @@ namespace Infrastructure.Auth
var claims = new List
{
new(ClaimTypes.Role, user.Role.ToString()),
- new(ClaimTypes.Email, user.Email)
+ new(ClaimTypes.NameIdentifier, user.Id.ToString())
};
var token = new JwtSecurityToken(
diff --git a/SchengenVisaApi/Infrastructure/Database/Applicants/Configuration/ApplicantConfiguration.cs b/SchengenVisaApi/Infrastructure/Database/Applicants/Configuration/ApplicantConfiguration.cs
index cadf620..96f6fe7 100644
--- a/SchengenVisaApi/Infrastructure/Database/Applicants/Configuration/ApplicantConfiguration.cs
+++ b/SchengenVisaApi/Infrastructure/Database/Applicants/Configuration/ApplicantConfiguration.cs
@@ -1,4 +1,5 @@
using Domains.ApplicantDomain;
+using Domains.Users;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
@@ -16,6 +17,7 @@ public class ApplicantConfiguration : IEntityTypeConfiguration
entity.HasOne(a => a.CityOfBirth).WithMany().OnDelete(DeleteBehavior.Restrict);
entity.HasOne(a => a.CountryOfBirth).WithMany().OnDelete(DeleteBehavior.Restrict);
+ entity.HasOne().WithOne().HasForeignKey(a => a.UserId);
entity.Property(p => p.Citizenship)
.IsUnicode(false)
diff --git a/SchengenVisaApi/Infrastructure/Database/Applicants/Repositories/ApplicantsRepository.cs b/SchengenVisaApi/Infrastructure/Database/Applicants/Repositories/ApplicantsRepository.cs
index aaf0ece..1d47955 100644
--- a/SchengenVisaApi/Infrastructure/Database/Applicants/Repositories/ApplicantsRepository.cs
+++ b/SchengenVisaApi/Infrastructure/Database/Applicants/Repositories/ApplicantsRepository.cs
@@ -1,5 +1,6 @@
using ApplicationLayer.DataAccessingServices.Applicants.NeededServices;
using Domains.ApplicantDomain;
+using Infrastructure.Database.Applicants.Repositories.Exceptions;
using Infrastructure.Database.Generic;
using Microsoft.EntityFrameworkCore;
@@ -8,9 +9,8 @@ namespace Infrastructure.Database.Applicants.Repositories;
/// Repository pattern for
///
///
-///
-public sealed class ApplicantsRepository(IGenericReader reader, IGenericWriter writer, IUnitOfWork unitOfWork)
- : GenericRepository(reader, writer, unitOfWork), IApplicantsRepository
+public sealed class ApplicantsRepository(IGenericReader reader, IGenericWriter writer)
+ : GenericRepository(reader, writer), IApplicantsRepository
{
protected override IQueryable LoadDomain()
{
@@ -19,4 +19,10 @@ public sealed class ApplicantsRepository(IGenericReader reader, IGenericWriter w
.Include(a => a.CityOfBirth)
.Include(a => a.PlaceOfWork);
}
+
+ async Task IApplicantsRepository.FindByUserIdAsync(Guid userId, CancellationToken cancellationToken)
+ {
+ var result = await LoadDomain().SingleOrDefaultAsync(a => a.UserId == userId, cancellationToken);
+ return result ?? throw new ApplicantNotFoundByUserIdException(userId);
+ }
}
diff --git a/SchengenVisaApi/Infrastructure/Database/Applicants/Repositories/Exceptions/ApplicantNotFoundByUserIdException.cs b/SchengenVisaApi/Infrastructure/Database/Applicants/Repositories/Exceptions/ApplicantNotFoundByUserIdException.cs
new file mode 100644
index 0000000..9e8d6a1
--- /dev/null
+++ b/SchengenVisaApi/Infrastructure/Database/Applicants/Repositories/Exceptions/ApplicantNotFoundByUserIdException.cs
@@ -0,0 +1,10 @@
+using Domains.ApplicantDomain;
+using Infrastructure.Database.GeneralExceptions;
+
+namespace Infrastructure.Database.Applicants.Repositories.Exceptions
+{
+ public class ApplicantNotFoundByUserIdException(Guid id) : EntityNotFoundException("Applicant not found.")
+ {
+ public Guid UserId { get; private set; } = id;
+ }
+}
diff --git a/SchengenVisaApi/Infrastructure/Database/DbContext.cs b/SchengenVisaApi/Infrastructure/Database/DbContext.cs
index 021bc66..f4fa8fb 100644
--- a/SchengenVisaApi/Infrastructure/Database/DbContext.cs
+++ b/SchengenVisaApi/Infrastructure/Database/DbContext.cs
@@ -1,4 +1,5 @@
using System.Reflection;
+using ApplicationLayer.GeneralNeededServices;
using Infrastructure.Database.Generic;
using Microsoft.EntityFrameworkCore;
diff --git a/SchengenVisaApi/Infrastructure/Database/GeneralExceptions/EntityNotFoundException.cs b/SchengenVisaApi/Infrastructure/Database/GeneralExceptions/EntityNotFoundException.cs
index 3d6ce3d..37de24f 100644
--- a/SchengenVisaApi/Infrastructure/Database/GeneralExceptions/EntityNotFoundException.cs
+++ b/SchengenVisaApi/Infrastructure/Database/GeneralExceptions/EntityNotFoundException.cs
@@ -1,8 +1,9 @@
-using Domains;
+using ApplicationLayer.GeneralExceptions;
+using Domains;
namespace Infrastructure.Database.GeneralExceptions;
/// Exception to throw when entity not found
/// Not found entity type
-public class EntityNotFoundException(string message) : Exception(message)
+public class EntityNotFoundException(string message) : ApiException(message)
where T : class, IEntity;
diff --git a/SchengenVisaApi/Infrastructure/Database/Generic/GenericRepository.cs b/SchengenVisaApi/Infrastructure/Database/Generic/GenericRepository.cs
index 48d716d..0a9fc3a 100644
--- a/SchengenVisaApi/Infrastructure/Database/Generic/GenericRepository.cs
+++ b/SchengenVisaApi/Infrastructure/Database/Generic/GenericRepository.cs
@@ -7,10 +7,9 @@ namespace Infrastructure.Database.Generic;
/// Generic repository pattern
///
-///
/// Type of entity
/// Should be inherited to create typed repositories
-public abstract class GenericRepository(IGenericReader reader, IGenericWriter writer, IUnitOfWork unitOfWork) : IGenericRepository
+public abstract class GenericRepository(IGenericReader reader, IGenericWriter writer) : IGenericRepository
where T : class, IEntity
{
///
@@ -41,10 +40,6 @@ public abstract class GenericRepository(IGenericReader reader, IGenericWriter
writer.Remove(entity);
}
- ///
- public async Task SaveAsync(CancellationToken cancellationToken)
- => await unitOfWork.SaveAsync(cancellationToken);
-
/// Should be overriden to load navigation properties of entity
protected virtual IQueryable LoadDomain()
{
diff --git a/SchengenVisaApi/Infrastructure/Database/Generic/IGenericWriter.cs b/SchengenVisaApi/Infrastructure/Database/Generic/IGenericWriter.cs
index 77db3c2..d84a68e 100644
--- a/SchengenVisaApi/Infrastructure/Database/Generic/IGenericWriter.cs
+++ b/SchengenVisaApi/Infrastructure/Database/Generic/IGenericWriter.cs
@@ -1,4 +1,5 @@
-using Domains;
+using ApplicationLayer.GeneralNeededServices;
+using Domains;
namespace Infrastructure.Database.Generic;
diff --git a/SchengenVisaApi/Infrastructure/Database/Locations/Configuration/CountryConfiguration.cs b/SchengenVisaApi/Infrastructure/Database/Locations/Configuration/CountryConfiguration.cs
index 5f0459e..6ef5bf2 100644
--- a/SchengenVisaApi/Infrastructure/Database/Locations/Configuration/CountryConfiguration.cs
+++ b/SchengenVisaApi/Infrastructure/Database/Locations/Configuration/CountryConfiguration.cs
@@ -8,8 +8,10 @@ public class CountryConfiguration : IEntityTypeConfiguration
{
public void Configure(EntityTypeBuilder entity)
{
- entity.Property(p => p.Name)
+ entity.Property(c => c.Name)
.IsUnicode(false)
.HasMaxLength(70);
+
+ entity.HasIndex(c => c.Name).IsUnique();
}
-}
\ No newline at end of file
+}
diff --git a/SchengenVisaApi/Infrastructure/Database/Locations/Repositories/Cities/CitiesRepository.cs b/SchengenVisaApi/Infrastructure/Database/Locations/Repositories/Cities/CitiesRepository.cs
index 953a0d6..5c52bb6 100644
--- a/SchengenVisaApi/Infrastructure/Database/Locations/Repositories/Cities/CitiesRepository.cs
+++ b/SchengenVisaApi/Infrastructure/Database/Locations/Repositories/Cities/CitiesRepository.cs
@@ -5,8 +5,8 @@ using Microsoft.EntityFrameworkCore;
namespace Infrastructure.Database.Locations.Repositories.Cities;
-public sealed class CitiesRepository(IGenericReader reader, IGenericWriter writer, IUnitOfWork unitOfWork)
- : GenericRepository(reader, writer, unitOfWork), ICitiesRepository
+public sealed class CitiesRepository(IGenericReader reader, IGenericWriter writer)
+ : GenericRepository(reader, writer), ICitiesRepository
{
protected override IQueryable LoadDomain()
{
diff --git a/SchengenVisaApi/Infrastructure/Database/Locations/Repositories/Countries/CountriesRepository.cs b/SchengenVisaApi/Infrastructure/Database/Locations/Repositories/Countries/CountriesRepository.cs
index e17f5d8..e291da7 100644
--- a/SchengenVisaApi/Infrastructure/Database/Locations/Repositories/Countries/CountriesRepository.cs
+++ b/SchengenVisaApi/Infrastructure/Database/Locations/Repositories/Countries/CountriesRepository.cs
@@ -5,11 +5,17 @@ using Microsoft.EntityFrameworkCore;
namespace Infrastructure.Database.Locations.Repositories.Countries;
-public sealed class CountriesRepository(IGenericReader reader, IGenericWriter writer, IUnitOfWork unitOfWork)
- : GenericRepository(reader, writer, unitOfWork), ICountriesRepository
+public sealed class CountriesRepository(IGenericReader reader, IGenericWriter writer)
+ : GenericRepository(reader, writer), ICountriesRepository
{
protected override IQueryable LoadDomain()
{
return base.LoadDomain().Include(c => c.Cities);
}
-}
\ No newline at end of file
+
+ async Task ICountriesRepository.FindByName(string countryName, CancellationToken cancellationToken)
+ {
+ var result = await LoadDomain().SingleOrDefaultAsync(c => c.Name == countryName, cancellationToken);
+ return result;
+ }
+}
diff --git a/SchengenVisaApi/Infrastructure/Database/Users/Repositories/UsersRepository.cs b/SchengenVisaApi/Infrastructure/Database/Users/Repositories/UsersRepository.cs
index 665fbc7..6ca9c1a 100644
--- a/SchengenVisaApi/Infrastructure/Database/Users/Repositories/UsersRepository.cs
+++ b/SchengenVisaApi/Infrastructure/Database/Users/Repositories/UsersRepository.cs
@@ -1,4 +1,4 @@
-using ApplicationLayer.AuthServices.NeededServices;
+using ApplicationLayer.DataAccessingServices.AuthServices.NeededServices;
using Domains.Users;
using Infrastructure.Database.Generic;
using Microsoft.EntityFrameworkCore;
@@ -6,8 +6,8 @@ using Microsoft.EntityFrameworkCore;
namespace Infrastructure.Database.Users.Repositories
{
///
- public class UsersRepository(IGenericReader reader, IGenericWriter writer, IUnitOfWork unitOfWork)
- : GenericRepository(reader, writer, unitOfWork), IUsersRepository
+ public class UsersRepository(IGenericReader reader, IGenericWriter writer)
+ : GenericRepository(reader, writer), IUsersRepository
{
async Task IUsersRepository.FindByEmailAsync(string email, CancellationToken cancellationToken)
{
diff --git a/SchengenVisaApi/Infrastructure/Database/VisaApplications/Repositories/VisaApplicationsRepository.cs b/SchengenVisaApi/Infrastructure/Database/VisaApplications/Repositories/VisaApplicationsRepository.cs
index a287b82..9363762 100644
--- a/SchengenVisaApi/Infrastructure/Database/VisaApplications/Repositories/VisaApplicationsRepository.cs
+++ b/SchengenVisaApi/Infrastructure/Database/VisaApplications/Repositories/VisaApplicationsRepository.cs
@@ -5,8 +5,8 @@ using Microsoft.EntityFrameworkCore;
namespace Infrastructure.Database.VisaApplications.Repositories;
-public sealed class VisaApplicationsRepository(IGenericReader reader, IGenericWriter writer, IUnitOfWork unitOfWork)
- : GenericRepository(reader, writer, unitOfWork), IVisaApplicationsRepository
+public sealed class VisaApplicationsRepository(IGenericReader reader, IGenericWriter writer)
+ : GenericRepository(reader, writer), IVisaApplicationsRepository
{
protected override IQueryable LoadDomain()
{
@@ -15,4 +15,4 @@ public sealed class VisaApplicationsRepository(IGenericReader reader, IGenericWr
.Include(a => a.PastVisas)
.Include(a => a.PastVisits);
}
-}
\ No newline at end of file
+}
diff --git a/SchengenVisaApi/Infrastructure/DependencyInjection.cs b/SchengenVisaApi/Infrastructure/DependencyInjection.cs
index ac1e261..dc53b94 100644
--- a/SchengenVisaApi/Infrastructure/DependencyInjection.cs
+++ b/SchengenVisaApi/Infrastructure/DependencyInjection.cs
@@ -1,11 +1,9 @@
-using ApplicationLayer.AuthServices.NeededServices;
-using ApplicationLayer.DataAccessingServices.Applicants.NeededServices;
+using ApplicationLayer.DataAccessingServices.Applicants.NeededServices;
+using ApplicationLayer.DataAccessingServices.AuthServices.NeededServices;
using ApplicationLayer.DataAccessingServices.Locations.NeededServices;
using ApplicationLayer.DataAccessingServices.VisaApplications.NeededServices;
using ApplicationLayer.GeneralNeededServices;
-using Infrastructure.Auth;
using Infrastructure.Common;
-using Infrastructure.Database;
using Infrastructure.Database.Applicants.Repositories;
using Infrastructure.Database.Generic;
using Infrastructure.Database.Locations.Repositories.Cities;
diff --git a/SchengenVisaApi/SchengenVisaApi/Common/ConfigureSwaggerOptions.cs b/SchengenVisaApi/SchengenVisaApi/Common/ConfigureSwaggerOptions.cs
new file mode 100644
index 0000000..51aa125
--- /dev/null
+++ b/SchengenVisaApi/SchengenVisaApi/Common/ConfigureSwaggerOptions.cs
@@ -0,0 +1,37 @@
+using Microsoft.Extensions.Options;
+using Microsoft.OpenApi.Models;
+using Swashbuckle.AspNetCore.SwaggerGen;
+
+namespace SchengenVisaApi.Common
+{
+ public class ConfigureSwaggerOptions : IConfigureOptions
+ {
+ void IConfigureOptions.Configure(SwaggerGenOptions options)
+ {
+ options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
+ {
+ In = ParameterLocation.Header,
+ Description = "Provide a JWT-token.",
+ Name = "Authorization",
+ Type = SecuritySchemeType.Http,
+ BearerFormat = "JWT",
+ Scheme = "Bearer"
+ });
+
+ options.AddSecurityRequirement(new OpenApiSecurityRequirement
+ {
+ {
+ new OpenApiSecurityScheme
+ {
+ Reference = new OpenApiReference
+ {
+ Type = ReferenceType.SecurityScheme,
+ Id = "Bearer"
+ }
+ },
+ Array.Empty()
+ }
+ });
+ }
+ }
+}
diff --git a/SchengenVisaApi/SchengenVisaApi/Common/PolicyConstants.cs b/SchengenVisaApi/SchengenVisaApi/Common/PolicyConstants.cs
new file mode 100644
index 0000000..111b5bc
--- /dev/null
+++ b/SchengenVisaApi/SchengenVisaApi/Common/PolicyConstants.cs
@@ -0,0 +1,12 @@
+namespace SchengenVisaApi.Common
+{
+#pragma warning disable CS1591
+ public static class PolicyConstants
+ {
+ public const string AdminPolicy = "AdminPolicy";
+ public const string ApplicantPolicy = "ApplicantPolicy";
+ public const string ApprovingAuthorityPolicy = "ApprovingAuthorityPolicy";
+ }
+#pragma warning enable CS1591
+
+}
diff --git a/SchengenVisaApi/SchengenVisaApi/Controllers/AdminController.cs b/SchengenVisaApi/SchengenVisaApi/Controllers/AdminController.cs
new file mode 100644
index 0000000..09b1bd4
--- /dev/null
+++ b/SchengenVisaApi/SchengenVisaApi/Controllers/AdminController.cs
@@ -0,0 +1,22 @@
+using ApplicationLayer.DataAccessingServices.Locations.RequestHandlers.AdminRequests;
+using ApplicationLayer.DataAccessingServices.Locations.Requests;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using SchengenVisaApi.Common;
+
+namespace SchengenVisaApi.Controllers
+{
+ [ApiController]
+ [Route("admin")]
+ [Authorize(policy: PolicyConstants.AdminPolicy)]
+ public class AdminController(IEditLocationsRequestsHandler requestsHandler) : ControllerBase
+ {
+ [HttpPost]
+ [Route("country")]
+ public async Task AddCountry(AddCountryRequest request, CancellationToken cancellationToken)
+ {
+ await requestsHandler.AddCountryAsync(request, cancellationToken);
+ return Ok();
+ }
+ }
+}
diff --git a/SchengenVisaApi/SchengenVisaApi/Controllers/LocationsController.cs b/SchengenVisaApi/SchengenVisaApi/Controllers/LocationsController.cs
new file mode 100644
index 0000000..f2fbde6
--- /dev/null
+++ b/SchengenVisaApi/SchengenVisaApi/Controllers/LocationsController.cs
@@ -0,0 +1,16 @@
+using ApplicationLayer.DataAccessingServices.Locations.RequestHandlers.ApplicantRequests;
+using Microsoft.AspNetCore.Mvc;
+
+namespace SchengenVisaApi.Controllers
+{
+ [ApiController]
+ [Route("countries")]
+ public class LocationsController(ILocationRequestsHandler requestsHandler) : ControllerBase
+ {
+ [HttpGet]
+ public async Task GetAvailableLocations(CancellationToken cancellationToken)
+ {
+ return Ok(await requestsHandler.HandleGetRequestAsync(cancellationToken));
+ }
+ }
+}
diff --git a/SchengenVisaApi/SchengenVisaApi/Controllers/UsersController.cs b/SchengenVisaApi/SchengenVisaApi/Controllers/UsersController.cs
index 237b64c..4bc546d 100644
--- a/SchengenVisaApi/SchengenVisaApi/Controllers/UsersController.cs
+++ b/SchengenVisaApi/SchengenVisaApi/Controllers/UsersController.cs
@@ -1,14 +1,13 @@
-using ApplicationLayer.AuthServices.LoginService;
-using ApplicationLayer.AuthServices.RegisterService;
-using ApplicationLayer.AuthServices.Requests;
-using Microsoft.AspNetCore.Identity.Data;
+using ApplicationLayer.DataAccessingServices.AuthServices.LoginService;
+using ApplicationLayer.DataAccessingServices.AuthServices.RegisterService;
+using ApplicationLayer.DataAccessingServices.AuthServices.Requests;
using Microsoft.AspNetCore.Mvc;
namespace SchengenVisaApi.Controllers
{
[ApiController]
[Route("auth")]
- public class UsersController(IRegisterService registerService, ILoginService loginService) : Controller
+ public class UsersController(IRegisterService registerService, ILoginService loginService) : ControllerBase
{
[HttpPost]
public async Task Register(RegisterApplicantRequest request, CancellationToken cancellationToken)
diff --git a/SchengenVisaApi/SchengenVisaApi/Controllers/VisaApplicationController.cs b/SchengenVisaApi/SchengenVisaApi/Controllers/VisaApplicationController.cs
index 80cb645..c85664b 100644
--- a/SchengenVisaApi/SchengenVisaApi/Controllers/VisaApplicationController.cs
+++ b/SchengenVisaApi/SchengenVisaApi/Controllers/VisaApplicationController.cs
@@ -1,23 +1,29 @@
+using System.Security.Claims;
using ApplicationLayer.DataAccessingServices.VisaApplications.Handlers;
using ApplicationLayer.DataAccessingServices.VisaApplications.Requests;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
+using SchengenVisaApi.Common;
namespace SchengenVisaApi.Controllers;
[ApiController]
[Route("[controller]")]
-public class VisaApplicationController(IVisaApplicationsRequestHandler visaApplicationsRequestHandler) : ControllerBase
+[Authorize(policy: PolicyConstants.ApplicantPolicy)]
+public class VisaApplicationController(IVisaApplicationRequestsHandler visaApplicationRequestsHandler) : ControllerBase
{
+ //TODO remove
[HttpGet]
public async Task Get(CancellationToken cancellationToken)
{
- var result = await visaApplicationsRequestHandler.Get(cancellationToken);
+ var result = await visaApplicationRequestsHandler.Get(cancellationToken);
return Ok(result);
}
[HttpPost]
public void Create(VisaApplicationCreateRequest request, CancellationToken cancellationToken)
{
- visaApplicationsRequestHandler.HandleCreateRequest(request, cancellationToken);
+ var userId = Guid.Parse(HttpContext.User.Claims.First(c => c.Type == ClaimTypes.NameIdentifier).Value);
+ visaApplicationRequestsHandler.HandleCreateRequest(userId, request, cancellationToken);
}
}
diff --git a/SchengenVisaApi/SchengenVisaApi/DependencyInjection.cs b/SchengenVisaApi/SchengenVisaApi/DependencyInjection.cs
index 1c27bbc..f1e759b 100644
--- a/SchengenVisaApi/SchengenVisaApi/DependencyInjection.cs
+++ b/SchengenVisaApi/SchengenVisaApi/DependencyInjection.cs
@@ -1,10 +1,16 @@
using System.Reflection;
+using System.Security.Claims;
using System.Text;
using ApplicationLayer;
+using Domains.Users;
using Infrastructure;
using Infrastructure.Auth;
using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
+using SchengenVisaApi.Common;
+using Swashbuckle.AspNetCore.SwaggerGen;
namespace SchengenVisaApi;
@@ -19,7 +25,7 @@ public static class DependencyInjection
builder.Services
.AddInfrastructure(config, environment.IsDevelopment())
- .AddApplicationLayer()
+ .AddApplicationLayer(environment.IsDevelopment())
.AddAuth(config)
.AddPresentation(environment);
}
@@ -52,7 +58,7 @@ public static class DependencyInjection
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(opts => opts.TokenValidationParameters = parameters);
- services.AddAuthorization();
+ services.AddAuthorizationBuilder().ConfigureAuthorizationPolicies();
services.AddTokenGenerator(new TokenGeneratorOptions(
Issuer: parameters.ValidIssuer!,
@@ -64,9 +70,24 @@ public static class DependencyInjection
return services;
}
+ /// Configure roles
+ private static void ConfigureAuthorizationPolicies(this AuthorizationBuilder builder)
+ {
+ builder.AddPolicy(
+ PolicyConstants.AdminPolicy,
+ p => p.RequireClaim(ClaimTypes.Role, Role.Admin.ToString()))
+ .AddPolicy(
+ PolicyConstants.ApprovingAuthorityPolicy,
+ p => p.RequireClaim(ClaimTypes.Role, Role.ApprovingAuthority.ToString()))
+ .AddPolicy(
+ PolicyConstants.ApplicantPolicy,
+ p => p.RequireClaim(ClaimTypes.Role, Role.Applicant.ToString()));
+ }
+
/// Add swagger
private static void AddSwagger(this IServiceCollection services)
{
+ services.AddTransient, ConfigureSwaggerOptions>();
services.AddSwaggerGen(options =>
{
var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";