Merge pull request #6 from prtsie/visa-applications-controller
Visa applications controller
This commit is contained in:
		| @@ -1,13 +0,0 @@ | |||||||
| namespace ApplicationLayer.Services.Applicants.Models; |  | ||||||
|  |  | ||||||
| public class AddressModel |  | ||||||
| { |  | ||||||
|     /// City part of address |  | ||||||
|     public Guid CityId { get; set; } |  | ||||||
|  |  | ||||||
|     /// Street part of address |  | ||||||
|     public string Street { get; set; } = null!; |  | ||||||
|  |  | ||||||
|     /// Building part of address |  | ||||||
|     public string Building { get; set; } = null!; |  | ||||||
| } |  | ||||||
| @@ -0,0 +1,50 @@ | |||||||
|  | using Domains.ApplicantDomain; | ||||||
|  |  | ||||||
|  | namespace ApplicationLayer.Services.Applicants.Models | ||||||
|  | { | ||||||
|  |     /// Model of <see cref="Applicant"/> | ||||||
|  |     public class ApplicantModel | ||||||
|  |     { | ||||||
|  |         /// <inheritdoc cref="Applicant.Name"/> | ||||||
|  |         public Name Name { get; set; } = null!; | ||||||
|  |  | ||||||
|  |         /// <inheritdoc cref="Applicant.Passport"/> | ||||||
|  |         public Passport Passport { get; set; } = null!; | ||||||
|  |  | ||||||
|  |         /// <inheritdoc cref="Applicant.BirthDate"/> | ||||||
|  |         public DateTime BirthDate { get; set; } | ||||||
|  |  | ||||||
|  |         /// <inheritdoc cref="Applicant.CountryOfBirth"/> | ||||||
|  |         public string CountryOfBirth { get; set; } = null!; | ||||||
|  |  | ||||||
|  |         /// <inheritdoc cref="Applicant.CityOfBirth"/> | ||||||
|  |         public string CityOfBirth { get; set; } = null!; | ||||||
|  |  | ||||||
|  |         /// <inheritdoc cref="Applicant.Citizenship"/> | ||||||
|  |         public string Citizenship { get; set; } = null!; | ||||||
|  |  | ||||||
|  |         /// <inheritdoc cref="Applicant.CitizenshipByBirth"/> | ||||||
|  |         public string CitizenshipByBirth { get; set; } = null!; | ||||||
|  |  | ||||||
|  |         /// <inheritdoc cref="Applicant.Gender"/> | ||||||
|  |         public Gender Gender { get; set; } | ||||||
|  |  | ||||||
|  |         /// <inheritdoc cref="Applicant.MaritalStatus"/> | ||||||
|  |         public MaritalStatus MaritalStatus { get; set; } | ||||||
|  |  | ||||||
|  |         /// <inheritdoc cref="Applicant.FatherName"/> | ||||||
|  |         public Name FatherName { get; set; } = null!; | ||||||
|  |  | ||||||
|  |         /// <inheritdoc cref="Applicant.MotherName"/> | ||||||
|  |         public Name MotherName { get; set; } = null!; | ||||||
|  |  | ||||||
|  |         /// <inheritdoc cref="Applicant.JobTitle"/> | ||||||
|  |         public string JobTitle { get; set; } = null!; | ||||||
|  |  | ||||||
|  |         /// <inheritdoc cref="Applicant.PlaceOfWork"/> | ||||||
|  |         public PlaceOfWork PlaceOfWork { get; set; } = null!; | ||||||
|  |  | ||||||
|  |         /// <inheritdoc cref="Applicant.IsNonResident"/> | ||||||
|  |         public bool IsNonResident { get; set; } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,13 +0,0 @@ | |||||||
| namespace ApplicationLayer.Services.Applicants.Models; |  | ||||||
|  |  | ||||||
| public class PlaceOfWorkModel |  | ||||||
| { |  | ||||||
|     /// Name of hirer |  | ||||||
|     public string Name { get; set; } = null!; |  | ||||||
|  |  | ||||||
|     /// <see cref="AddressModel"/> of hirer |  | ||||||
|     public AddressModel Address { get; set; } = null!; |  | ||||||
|  |  | ||||||
|     /// Phone number of hirer |  | ||||||
|     public string PhoneNum { get; set; } = null!; |  | ||||||
| } |  | ||||||
| @@ -5,5 +5,5 @@ namespace ApplicationLayer.Services.GeneralExceptions; | |||||||
| /// Exception to throw when entity not found | /// Exception to throw when entity not found | ||||||
| /// <param name="id">Identifier of entity</param> | /// <param name="id">Identifier of entity</param> | ||||||
| /// <typeparam name="T">Type of entity</typeparam> | /// <typeparam name="T">Type of entity</typeparam> | ||||||
| public class EntityNotFoundByIdException<T>(Guid id) : EntityNotFoundException<T>($"{typeof(T).Name} with id {id} not found.") | public class EntityNotFoundByIdException<T>(Guid id) : EntityNotFoundException($"{typeof(T).Name} with id {id} not found.") | ||||||
|     where T : class, IEntity; |     where T : class, IEntity; | ||||||
|   | |||||||
| @@ -1,9 +1,6 @@ | |||||||
| using ApplicationLayer.GeneralExceptions; | using ApplicationLayer.GeneralExceptions; | ||||||
| using Domains; |  | ||||||
|  |  | ||||||
| namespace ApplicationLayer.Services.GeneralExceptions; | namespace ApplicationLayer.Services.GeneralExceptions; | ||||||
|  |  | ||||||
| /// Exception to throw when entity not found | /// Exception to throw when entity not found | ||||||
| /// <typeparam name="T">Not found entity type</typeparam> | public class EntityNotFoundException(string message) : ApiException(message); | ||||||
| public class EntityNotFoundException<T>(string message) : ApiException(message) |  | ||||||
|     where T : class, IEntity; |  | ||||||
|   | |||||||
| @@ -0,0 +1,6 @@ | |||||||
|  | using ApplicationLayer.GeneralExceptions; | ||||||
|  |  | ||||||
|  | namespace ApplicationLayer.Services.VisaApplications.Exceptions | ||||||
|  | { | ||||||
|  |     public class ApplicationAlreadyProcessedException() : ApiException("This application already processed or closed by applicant."); | ||||||
|  | } | ||||||
| @@ -1,13 +1,21 @@ | |||||||
| using ApplicationLayer.Services.VisaApplications.Models; | using ApplicationLayer.Services.VisaApplications.Models; | ||||||
| using ApplicationLayer.Services.VisaApplications.Requests; | using ApplicationLayer.Services.VisaApplications.Requests; | ||||||
| using Domains.VisaApplicationDomain; |  | ||||||
|  |  | ||||||
| namespace ApplicationLayer.Services.VisaApplications.Handlers; | namespace ApplicationLayer.Services.VisaApplications.Handlers; | ||||||
|  |  | ||||||
| public interface IVisaApplicationRequestsHandler | public interface IVisaApplicationRequestsHandler | ||||||
| { | { | ||||||
|     Task<List<VisaApplication>> Get(CancellationToken cancellationToken); |     /// Returns all applications for approving authorities | ||||||
|     Task<List<VisaApplicationModelForApplicant>> GetForApplicant(Guid userId, CancellationToken cancellationToken); |     Task<List<VisaApplicationModelForAuthority>> GetAllAsync(CancellationToken cancellationToken); | ||||||
|  |  | ||||||
|     Task HandleCreateRequest(Guid userId, VisaApplicationCreateRequest request, CancellationToken cancellationToken); |     /// Returns all applications of one applicant | ||||||
|  |     Task<List<VisaApplicationModelForApplicant>> GetForApplicantAsync(Guid userId, CancellationToken cancellationToken); | ||||||
|  |  | ||||||
|  |     /// Creates application for applicant with specific user identifier | ||||||
|  |     Task HandleCreateRequestAsync(Guid userId, VisaApplicationCreateRequest request, CancellationToken cancellationToken); | ||||||
|  |  | ||||||
|  |     /// Sets application status to closed | ||||||
|  |     Task HandleCloseRequestAsync(Guid userId, Guid applicationId, CancellationToken cancellationToken); | ||||||
|  |  | ||||||
|  |     Task SetApplicationStatusFromAuthorityAsync(Guid applicationId, AuthorityRequestStatuses status, CancellationToken cancellationToken); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,5 +1,7 @@ | |||||||
| using ApplicationLayer.InfrastructureServicesInterfaces; | using ApplicationLayer.InfrastructureServicesInterfaces; | ||||||
|  | using ApplicationLayer.Services.Applicants.Models; | ||||||
| using ApplicationLayer.Services.Applicants.NeededServices; | using ApplicationLayer.Services.Applicants.NeededServices; | ||||||
|  | using ApplicationLayer.Services.VisaApplications.Exceptions; | ||||||
| using ApplicationLayer.Services.VisaApplications.Models; | using ApplicationLayer.Services.VisaApplications.Models; | ||||||
| using ApplicationLayer.Services.VisaApplications.NeededServices; | using ApplicationLayer.Services.VisaApplications.NeededServices; | ||||||
| using ApplicationLayer.Services.VisaApplications.Requests; | using ApplicationLayer.Services.VisaApplications.Requests; | ||||||
| @@ -13,9 +15,57 @@ public class VisaApplicationRequestsHandler( | |||||||
|     IApplicantsRepository applicants, |     IApplicantsRepository applicants, | ||||||
|     IUnitOfWork unitOfWork) : IVisaApplicationRequestsHandler |     IUnitOfWork unitOfWork) : IVisaApplicationRequestsHandler | ||||||
| { | { | ||||||
|     public async Task<List<VisaApplication>> Get(CancellationToken cancellationToken) => await applications.GetAllAsync(cancellationToken); |     async Task<List<VisaApplicationModelForAuthority>> IVisaApplicationRequestsHandler.GetAllAsync(CancellationToken cancellationToken) | ||||||
|  |     { | ||||||
|  |         var applicationsList = await applications.GetAllAsync(cancellationToken); | ||||||
|  |  | ||||||
|     public async Task<List<VisaApplicationModelForApplicant>> GetForApplicant(Guid userId, CancellationToken cancellationToken) |         //todo mapper | ||||||
|  |         var applicationModels = applicationsList | ||||||
|  |             .Select(a => MapVisaApplicationToModelForAuthorities(a, cancellationToken).Result) | ||||||
|  |             .ToList(); | ||||||
|  |         return applicationModels; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private async Task<VisaApplicationModelForAuthority> MapVisaApplicationToModelForAuthorities(VisaApplication visaApplication, | ||||||
|  |         CancellationToken cancellationToken) | ||||||
|  |     { | ||||||
|  |         var applicant = await applicants.GetByIdAsync(visaApplication.ApplicantId, cancellationToken); | ||||||
|  |         var applicantModel = new ApplicantModel | ||||||
|  |         { | ||||||
|  |             Citizenship = applicant.Citizenship, | ||||||
|  |             Gender = applicant.Gender, | ||||||
|  |             Name = applicant.Name, | ||||||
|  |             Passport = applicant.Passport, | ||||||
|  |             BirthDate = applicant.BirthDate, | ||||||
|  |             FatherName = applicant.FatherName, | ||||||
|  |             JobTitle = applicant.JobTitle, | ||||||
|  |             MaritalStatus = applicant.MaritalStatus, | ||||||
|  |             MotherName = applicant.MotherName, | ||||||
|  |             CitizenshipByBirth = applicant.CitizenshipByBirth, | ||||||
|  |             CityOfBirth = applicant.CityOfBirth, | ||||||
|  |             CountryOfBirth = applicant.CountryOfBirth, | ||||||
|  |             IsNonResident = applicant.IsNonResident, | ||||||
|  |             PlaceOfWork = applicant.PlaceOfWork, | ||||||
|  |         }; | ||||||
|  |         return new VisaApplicationModelForAuthority | ||||||
|  |         { | ||||||
|  |             PastVisits = visaApplication.PastVisits, | ||||||
|  |             ReentryPermit = visaApplication.ReentryPermit, | ||||||
|  |             VisaCategory = visaApplication.VisaCategory, | ||||||
|  |             PermissionToDestCountry = visaApplication.PermissionToDestCountry, | ||||||
|  |             DestinationCountry = visaApplication.DestinationCountry, | ||||||
|  |             PastVisas = visaApplication.PastVisas, | ||||||
|  |             RequestDate = visaApplication.RequestDate, | ||||||
|  |             ValidDaysRequested = visaApplication.ValidDaysRequested, | ||||||
|  |             RequestedNumberOfEntries = visaApplication.RequestedNumberOfEntries, | ||||||
|  |             ForGroup = visaApplication.ForGroup, | ||||||
|  |             Applicant = applicantModel, | ||||||
|  |             Id = visaApplication.Id, | ||||||
|  |             Status = visaApplication.Status | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public async Task<List<VisaApplicationModelForApplicant>> GetForApplicantAsync(Guid userId, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         //todo mapper |         //todo mapper | ||||||
|         var applicantId = await applicants.GetApplicantIdByUserId(userId, cancellationToken); |         var applicantId = await applicants.GetApplicantIdByUserId(userId, cancellationToken); | ||||||
| @@ -31,11 +81,14 @@ public class VisaApplicationRequestsHandler( | |||||||
|                 ForGroup = va.ForGroup, |                 ForGroup = va.ForGroup, | ||||||
|                 PastVisas = va.PastVisas, |                 PastVisas = va.PastVisas, | ||||||
|                 RequestDate = va.RequestDate, |                 RequestDate = va.RequestDate, | ||||||
|                 PastVisits = va.PastVisits |                 PastVisits = va.PastVisits, | ||||||
|             }).ToList(); |                 Id = va.Id, | ||||||
|  |                 Status = va.Status | ||||||
|  |             }) | ||||||
|  |             .ToList(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public async Task HandleCreateRequest(Guid userId, VisaApplicationCreateRequest request, CancellationToken cancellationToken) |     public async Task HandleCreateRequestAsync(Guid userId, VisaApplicationCreateRequest request, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         //TODO mapper |         //TODO mapper | ||||||
|  |  | ||||||
| @@ -53,11 +106,49 @@ public class VisaApplicationRequestsHandler( | |||||||
|             PastVisas = request.PastVisas.ToList(), |             PastVisas = request.PastVisas.ToList(), | ||||||
|             PastVisits = request.PastVisits.ToList(), |             PastVisits = request.PastVisits.ToList(), | ||||||
|             ForGroup = request.IsForGroup, |             ForGroup = request.IsForGroup, | ||||||
|             RequestDate = DateTime.Today |             RequestDate = DateTime.Today, | ||||||
|  |             Status = ApplicationStatus.Pending | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         await applications.AddAsync(visaApplication, cancellationToken); |         await applications.AddAsync(visaApplication, cancellationToken); | ||||||
|  |  | ||||||
|         await unitOfWork.SaveAsync(cancellationToken); |         await unitOfWork.SaveAsync(cancellationToken); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     async Task IVisaApplicationRequestsHandler.HandleCloseRequestAsync(Guid userId, Guid applicationId, CancellationToken cancellationToken) | ||||||
|  |     { | ||||||
|  |         var applicantId = await applicants.GetApplicantIdByUserId(userId, cancellationToken); | ||||||
|  |         var application = await applications.GetByApplicantAndApplicationIdAsync(applicantId, applicationId, cancellationToken); | ||||||
|  |  | ||||||
|  |         application.Status = ApplicationStatus.Closed; | ||||||
|  |         await applications.UpdateAsync(application, cancellationToken); | ||||||
|  |  | ||||||
|  |         await unitOfWork.SaveAsync(cancellationToken); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async Task IVisaApplicationRequestsHandler.SetApplicationStatusFromAuthorityAsync( | ||||||
|  |         Guid applicationId, | ||||||
|  |         AuthorityRequestStatuses status, | ||||||
|  |         CancellationToken cancellationToken) | ||||||
|  |     { | ||||||
|  |         var application = await applications.GetByIdAsync(applicationId, cancellationToken); | ||||||
|  |         if (application.Status != ApplicationStatus.Pending) | ||||||
|  |         { | ||||||
|  |             //todo refactor exceptions | ||||||
|  |             throw new ApplicationAlreadyProcessedException(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         //todo mapper | ||||||
|  |         ApplicationStatus statusToSet = status switch | ||||||
|  |         { | ||||||
|  |             AuthorityRequestStatuses.Approved => ApplicationStatus.Approved, | ||||||
|  |             AuthorityRequestStatuses.Rejected => ApplicationStatus.Rejected, | ||||||
|  |             _ => throw new ArgumentOutOfRangeException(nameof(status), status, null) | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         application.Status = statusToSet; | ||||||
|  |         await applications.UpdateAsync(application, cancellationToken); | ||||||
|  |  | ||||||
|  |         await unitOfWork.SaveAsync(cancellationToken); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,8 @@ | |||||||
|  | namespace ApplicationLayer.Services.VisaApplications.Models | ||||||
|  | { | ||||||
|  |     public enum AuthorityRequestStatuses | ||||||
|  |     { | ||||||
|  |         Approved, | ||||||
|  |         Rejected | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -5,6 +5,11 @@ namespace ApplicationLayer.Services.VisaApplications.Models | |||||||
|     /// Model of <see cref="VisaApplication"/> |     /// Model of <see cref="VisaApplication"/> | ||||||
|     public class VisaApplicationModelForApplicant |     public class VisaApplicationModelForApplicant | ||||||
|     { |     { | ||||||
|  |         /// <inheritdoc cref="VisaApplication.Id"/> | ||||||
|  |         public Guid Id { get; set; } | ||||||
|  |  | ||||||
|  |         /// <inheritdoc cref="VisaApplication.Status"/> | ||||||
|  |         public ApplicationStatus Status { get; set; } | ||||||
|  |  | ||||||
|         /// <inheritdoc cref="VisaApplication.ReentryPermit"/> |         /// <inheritdoc cref="VisaApplication.ReentryPermit"/> | ||||||
|         public ReentryPermit? ReentryPermit { get; set; } |         public ReentryPermit? ReentryPermit { get; set; } | ||||||
| @@ -18,6 +23,7 @@ namespace ApplicationLayer.Services.VisaApplications.Models | |||||||
|         /// <inheritdoc cref="VisaApplication.PermissionToDestCountry"/> |         /// <inheritdoc cref="VisaApplication.PermissionToDestCountry"/> | ||||||
|         public PermissionToDestCountry? PermissionToDestCountry { get; set; } |         public PermissionToDestCountry? PermissionToDestCountry { get; set; } | ||||||
|  |  | ||||||
|  |         /// <inheritdoc cref="VisaApplication.PastVisits"/> | ||||||
|         public List<PastVisit> PastVisits { get; set; } = null!; |         public List<PastVisit> PastVisits { get; set; } = null!; | ||||||
|  |  | ||||||
|         /// <inheritdoc cref="VisaApplication.VisaCategory"/> |         /// <inheritdoc cref="VisaApplication.VisaCategory"/> | ||||||
|   | |||||||
| @@ -0,0 +1,47 @@ | |||||||
|  | using ApplicationLayer.Services.Applicants.Models; | ||||||
|  | using Domains.VisaApplicationDomain; | ||||||
|  |  | ||||||
|  | namespace ApplicationLayer.Services.VisaApplications.Models | ||||||
|  | { | ||||||
|  |     /// Model of <see cref="VisaApplication"/> with applicant property | ||||||
|  |     public class VisaApplicationModelForAuthority | ||||||
|  |     { | ||||||
|  |         /// <inheritdoc cref="VisaApplication.Id"/> | ||||||
|  |         public Guid Id { get; set; } | ||||||
|  |  | ||||||
|  |         /// Applicant of application | ||||||
|  |         public ApplicantModel Applicant { get; set; } = null!; | ||||||
|  |  | ||||||
|  |         /// <inheritdoc cref="VisaApplication.Status"/> | ||||||
|  |         public ApplicationStatus Status { get; set; } | ||||||
|  |  | ||||||
|  |         /// <inheritdoc cref="VisaApplication.ReentryPermit"/> | ||||||
|  |         public ReentryPermit? ReentryPermit { get; set; } | ||||||
|  |  | ||||||
|  |         /// <inheritdoc cref="VisaApplication.DestinationCountry"/> | ||||||
|  |         public string DestinationCountry { get; set; } = null!; | ||||||
|  |  | ||||||
|  |         /// <inheritdoc cref="VisaApplication.PastVisas"/> | ||||||
|  |         public List<PastVisa> PastVisas { get; set; } = null!; | ||||||
|  |  | ||||||
|  |         /// <inheritdoc cref="VisaApplication.PermissionToDestCountry"/> | ||||||
|  |         public PermissionToDestCountry? PermissionToDestCountry { get; set; } | ||||||
|  |  | ||||||
|  |         public List<PastVisit> PastVisits { get; set; } = null!; | ||||||
|  |  | ||||||
|  |         /// <inheritdoc cref="VisaApplication.VisaCategory"/> | ||||||
|  |         public VisaCategory VisaCategory { get; set; } | ||||||
|  |  | ||||||
|  |         /// <inheritdoc cref="VisaApplication.ForGroup"/> | ||||||
|  |         public bool ForGroup { get; set; } | ||||||
|  |  | ||||||
|  |         /// <inheritdoc cref="VisaApplication.RequestedNumberOfEntries"/> | ||||||
|  |         public RequestedNumberOfEntries RequestedNumberOfEntries { get; set; } | ||||||
|  |  | ||||||
|  |         /// <inheritdoc cref="VisaApplication.RequestDate"/> | ||||||
|  |         public DateTime RequestDate { get; set; } | ||||||
|  |  | ||||||
|  |         /// <inheritdoc cref="VisaApplication.ValidDaysRequested"/> | ||||||
|  |         public int ValidDaysRequested { get; set; } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -7,4 +7,7 @@ public interface IVisaApplicationsRepository : IGenericRepository<VisaApplicatio | |||||||
| { | { | ||||||
|     /// Get applications of one applicant |     /// Get applications of one applicant | ||||||
|     Task<List<VisaApplication>> GetOfApplicantAsync(Guid applicantId, CancellationToken cancellationToken); |     Task<List<VisaApplication>> GetOfApplicantAsync(Guid applicantId, CancellationToken cancellationToken); | ||||||
|  |  | ||||||
|  |     /// Get specific application of specific user | ||||||
|  |     Task<VisaApplication> GetByApplicantAndApplicationIdAsync(Guid applicantId, Guid applicationId, CancellationToken cancellationToken); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,12 @@ | |||||||
|  | namespace Domains.VisaApplicationDomain | ||||||
|  | { | ||||||
|  |     public enum ApplicationStatus | ||||||
|  |     { | ||||||
|  |         /// Waits for approve | ||||||
|  |         Pending, | ||||||
|  |         Approved, | ||||||
|  |         Rejected, | ||||||
|  |         /// Closed by applicant | ||||||
|  |         Closed | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -11,6 +11,9 @@ public class VisaApplication : IEntity | |||||||
|     /// Identifier of the <see cref="Applicant"/> |     /// Identifier of the <see cref="Applicant"/> | ||||||
|     public Guid ApplicantId { get; set; } |     public Guid ApplicantId { get; set; } | ||||||
|  |  | ||||||
|  |     /// Status of application | ||||||
|  |     public ApplicationStatus Status { get; set; } | ||||||
|  |  | ||||||
|     /// <inheritdoc cref="Domains.VisaApplicationDomain.ReentryPermit"/> |     /// <inheritdoc cref="Domains.VisaApplicationDomain.ReentryPermit"/> | ||||||
|     /// <remarks>always null if <see cref="Applicant"/> is not a non-resident</remarks> |     /// <remarks>always null if <see cref="Applicant"/> is not a non-resident</remarks> | ||||||
|     public ReentryPermit? ReentryPermit { get; set; } |     public ReentryPermit? ReentryPermit { get; set; } | ||||||
| @@ -18,9 +21,7 @@ public class VisaApplication : IEntity | |||||||
|     /// Country that <see cref="Applicant"/> wants to visit |     /// Country that <see cref="Applicant"/> wants to visit | ||||||
|     public string DestinationCountry { get; set; } = null!; |     public string DestinationCountry { get; set; } = null!; | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     /// List of <see cref="PastVisa"/> that applicant had before |     /// List of <see cref="PastVisa"/> that applicant had before | ||||||
|     /// </summary> |  | ||||||
|     public List<PastVisa> PastVisas { get; set; } = null!; |     public List<PastVisa> PastVisas { get; set; } = null!; | ||||||
|  |  | ||||||
|     /// Permission to enter the destination country of <see cref="Applicant"/> |     /// Permission to enter the destination country of <see cref="Applicant"/> | ||||||
|   | |||||||
| @@ -21,12 +21,12 @@ public sealed class ApplicantsRepository(IGenericReader reader, IGenericWriter w | |||||||
|     async Task<Applicant> IApplicantsRepository.FindByUserIdAsync(Guid userId, CancellationToken cancellationToken) |     async Task<Applicant> IApplicantsRepository.FindByUserIdAsync(Guid userId, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         var result = await LoadDomain().SingleOrDefaultAsync(a => a.UserId == userId, cancellationToken); |         var result = await LoadDomain().SingleOrDefaultAsync(a => a.UserId == userId, cancellationToken); | ||||||
|         return result ?? throw new ApplicantNotFoundByUserIdException(userId); |         return result ?? throw new ApplicantNotFoundByUserIdException(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async Task<Guid> IApplicantsRepository.GetApplicantIdByUserId(Guid userId, CancellationToken cancellationToken) |     async Task<Guid> IApplicantsRepository.GetApplicantIdByUserId(Guid userId, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         var result = await base.LoadDomain().SingleOrDefaultAsync(a => a.UserId == userId, cancellationToken); |         var result = await base.LoadDomain().SingleOrDefaultAsync(a => a.UserId == userId, cancellationToken); | ||||||
|         return result?.Id ?? throw new ApplicantNotFoundByUserIdException(userId); |         return result?.Id ?? throw new ApplicantNotFoundByUserIdException(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,10 +1,6 @@ | |||||||
| using ApplicationLayer.Services.GeneralExceptions; | using ApplicationLayer.Services.GeneralExceptions; | ||||||
| using Domains.ApplicantDomain; |  | ||||||
|  |  | ||||||
| namespace Infrastructure.Database.Applicants.Repositories.Exceptions | namespace Infrastructure.Database.Applicants.Repositories.Exceptions | ||||||
| { | { | ||||||
|     public class ApplicantNotFoundByUserIdException(Guid id) : EntityNotFoundException<Applicant>("Applicant not found.") |     public class ApplicantNotFoundByUserIdException() : EntityNotFoundException("Applicant not found."); | ||||||
|     { |  | ||||||
|         public Guid UserId { get; private set; } = id; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,7 @@ | |||||||
|  | using ApplicationLayer.Services.GeneralExceptions; | ||||||
|  |  | ||||||
|  | namespace Infrastructure.Database.VisaApplications.Repositories.Exceptions | ||||||
|  | { | ||||||
|  |     public class ApplicationNotFoundByApplicantAndApplicationIdException(Guid applicationId) | ||||||
|  |         : EntityNotFoundException($"Application with id {applicationId} not found for authenticated user"); | ||||||
|  | } | ||||||
| @@ -1,6 +1,7 @@ | |||||||
| using ApplicationLayer.Services.VisaApplications.NeededServices; | using ApplicationLayer.Services.VisaApplications.NeededServices; | ||||||
| using Domains.VisaApplicationDomain; | using Domains.VisaApplicationDomain; | ||||||
| using Infrastructure.Database.Generic; | using Infrastructure.Database.Generic; | ||||||
|  | using Infrastructure.Database.VisaApplications.Repositories.Exceptions; | ||||||
| using Microsoft.EntityFrameworkCore; | using Microsoft.EntityFrameworkCore; | ||||||
|  |  | ||||||
| namespace Infrastructure.Database.VisaApplications.Repositories; | namespace Infrastructure.Database.VisaApplications.Repositories; | ||||||
| @@ -16,4 +17,14 @@ public sealed class VisaApplicationsRepository(IGenericReader reader, IGenericWr | |||||||
|  |  | ||||||
|     async Task<List<VisaApplication>> IVisaApplicationsRepository.GetOfApplicantAsync(Guid applicantId, CancellationToken cancellationToken) |     async Task<List<VisaApplication>> IVisaApplicationsRepository.GetOfApplicantAsync(Guid applicantId, CancellationToken cancellationToken) | ||||||
|         => await LoadDomain().Where(va => va.ApplicantId == applicantId).ToListAsync(cancellationToken); |         => await LoadDomain().Where(va => va.ApplicantId == applicantId).ToListAsync(cancellationToken); | ||||||
|  |  | ||||||
|  |     async Task<VisaApplication> IVisaApplicationsRepository.GetByApplicantAndApplicationIdAsync( | ||||||
|  |         Guid applicantId, | ||||||
|  |         Guid applicationId, | ||||||
|  |         CancellationToken cancellationToken) | ||||||
|  |     { | ||||||
|  |         var result = await LoadDomain() | ||||||
|  |             .SingleOrDefaultAsync(va => va.Id == applicationId && va.ApplicantId == applicantId, cancellationToken); | ||||||
|  |         return result ?? throw new ApplicationNotFoundByApplicantAndApplicationIdException(applicationId); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -35,7 +35,7 @@ namespace SchengenVisaApi.Controllers | |||||||
|         [ProducesResponseType(StatusCodes.Status409Conflict)] |         [ProducesResponseType(StatusCodes.Status409Conflict)] | ||||||
|         [ProducesResponseType(StatusCodes.Status403Forbidden)] |         [ProducesResponseType(StatusCodes.Status403Forbidden)] | ||||||
|         [ProducesResponseType(StatusCodes.Status401Unauthorized)] |         [ProducesResponseType(StatusCodes.Status401Unauthorized)] | ||||||
|         [Route("authority")] |         [Route("authorities")] | ||||||
|         [Authorize(policy: PolicyConstants.AdminPolicy)] |         [Authorize(policy: PolicyConstants.AdminPolicy)] | ||||||
|         public async Task<IActionResult> RegisterAuthority(RegisterRequest request, CancellationToken cancellationToken) |         public async Task<IActionResult> RegisterAuthority(RegisterRequest request, CancellationToken cancellationToken) | ||||||
|         { |         { | ||||||
| @@ -60,7 +60,7 @@ namespace SchengenVisaApi.Controllers | |||||||
|         [ProducesResponseType<List<User>>(StatusCodes.Status200OK)] |         [ProducesResponseType<List<User>>(StatusCodes.Status200OK)] | ||||||
|         [ProducesResponseType(StatusCodes.Status403Forbidden)] |         [ProducesResponseType(StatusCodes.Status403Forbidden)] | ||||||
|         [ProducesResponseType(StatusCodes.Status401Unauthorized)] |         [ProducesResponseType(StatusCodes.Status401Unauthorized)] | ||||||
|         [Route("authority")] |         [Route("authorities")] | ||||||
|         [Authorize(policy: PolicyConstants.AdminPolicy)] |         [Authorize(policy: PolicyConstants.AdminPolicy)] | ||||||
|         public async Task<IActionResult> GetAuthorityAccounts(CancellationToken cancellationToken) |         public async Task<IActionResult> GetAuthorityAccounts(CancellationToken cancellationToken) | ||||||
|         { |         { | ||||||
| @@ -75,8 +75,9 @@ namespace SchengenVisaApi.Controllers | |||||||
|         [ProducesResponseType(StatusCodes.Status404NotFound)] |         [ProducesResponseType(StatusCodes.Status404NotFound)] | ||||||
|         [ProducesResponseType(StatusCodes.Status403Forbidden)] |         [ProducesResponseType(StatusCodes.Status403Forbidden)] | ||||||
|         [ProducesResponseType(StatusCodes.Status401Unauthorized)] |         [ProducesResponseType(StatusCodes.Status401Unauthorized)] | ||||||
|         [Route("authority/{authorityAccountId:guid}")] |         [Route("authorities/{authorityAccountId:guid}")] | ||||||
|         [Authorize(policy: PolicyConstants.AdminPolicy)] |         [Authorize(policy: PolicyConstants.AdminPolicy)] | ||||||
|  |         //todo replace args with ChangeAuthorityAuthDataRequest or something | ||||||
|         public async Task<IActionResult> ChangeAuthorityAuthData(Guid authorityAccountId, RegisterRequest authData, CancellationToken cancellationToken) |         public async Task<IActionResult> ChangeAuthorityAuthData(Guid authorityAccountId, RegisterRequest authData, CancellationToken cancellationToken) | ||||||
|         { |         { | ||||||
|             await authorityService.ChangeAccountAuthDataAsync(authorityAccountId, authData, cancellationToken); |             await authorityService.ChangeAccountAuthDataAsync(authorityAccountId, authData, cancellationToken); | ||||||
| @@ -90,7 +91,7 @@ namespace SchengenVisaApi.Controllers | |||||||
|         [ProducesResponseType(StatusCodes.Status404NotFound)] |         [ProducesResponseType(StatusCodes.Status404NotFound)] | ||||||
|         [ProducesResponseType(StatusCodes.Status403Forbidden)] |         [ProducesResponseType(StatusCodes.Status403Forbidden)] | ||||||
|         [ProducesResponseType(StatusCodes.Status401Unauthorized)] |         [ProducesResponseType(StatusCodes.Status401Unauthorized)] | ||||||
|         [Route("authority/{authorityAccountId:guid}")] |         [Route("authorities/{authorityAccountId:guid}")] | ||||||
|         [Authorize(policy: PolicyConstants.AdminPolicy)] |         [Authorize(policy: PolicyConstants.AdminPolicy)] | ||||||
|         public async Task<IActionResult> RemoveAuthorityAccount(Guid authorityAccountId, CancellationToken cancellationToken) |         public async Task<IActionResult> RemoveAuthorityAccount(Guid authorityAccountId, CancellationToken cancellationToken) | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -1,8 +1,6 @@ | |||||||
| using System.Security.Claims; |  | ||||||
| using ApplicationLayer.Services.VisaApplications.Handlers; | using ApplicationLayer.Services.VisaApplications.Handlers; | ||||||
| using ApplicationLayer.Services.VisaApplications.Models; | using ApplicationLayer.Services.VisaApplications.Models; | ||||||
| using ApplicationLayer.Services.VisaApplications.Requests; | using ApplicationLayer.Services.VisaApplications.Requests; | ||||||
| using Domains.VisaApplicationDomain; |  | ||||||
| using Microsoft.AspNetCore.Authorization; | using Microsoft.AspNetCore.Authorization; | ||||||
| using Microsoft.AspNetCore.Mvc; | using Microsoft.AspNetCore.Mvc; | ||||||
| using SchengenVisaApi.Common; | using SchengenVisaApi.Common; | ||||||
| @@ -11,21 +9,19 @@ namespace SchengenVisaApi.Controllers; | |||||||
|  |  | ||||||
| /// <summary> Controller for <see cref="Domains.VisaApplicationDomain"/> </summary> | /// <summary> Controller for <see cref="Domains.VisaApplicationDomain"/> </summary> | ||||||
| [ApiController] | [ApiController] | ||||||
| [Route("visaApplication")] | [Route("visaApplications")] | ||||||
| public class VisaApplicationController(IVisaApplicationRequestsHandler visaApplicationRequestsHandler) : VisaApiControllerBase | public class VisaApplicationController(IVisaApplicationRequestsHandler visaApplicationRequestsHandler) : VisaApiControllerBase | ||||||
| { | { | ||||||
|     //todo should return only pending applications |  | ||||||
|     //todo should return model |  | ||||||
|     /// <summary> Returns all applications from DB </summary> |     /// <summary> Returns all applications from DB </summary> | ||||||
|     /// <remarks> Accessible only for approving authorities </remarks> |     /// <remarks> Accessible only for approving authorities </remarks> | ||||||
|     [HttpGet] |     [HttpGet] | ||||||
|     [ProducesResponseType<List<VisaApplication>>(StatusCodes.Status200OK)] |     [ProducesResponseType<List<VisaApplicationModelForAuthority>>(StatusCodes.Status200OK)] | ||||||
|     [ProducesResponseType(StatusCodes.Status403Forbidden)] |     [ProducesResponseType(StatusCodes.Status403Forbidden)] | ||||||
|     [ProducesResponseType(StatusCodes.Status401Unauthorized)] |     [ProducesResponseType(StatusCodes.Status401Unauthorized)] | ||||||
|     [Authorize(policy: PolicyConstants.ApprovingAuthorityPolicy)] |     [Authorize(policy: PolicyConstants.ApprovingAuthorityPolicy)] | ||||||
|     public async Task<IActionResult> Get(CancellationToken cancellationToken) |     public async Task<IActionResult> Get(CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         var result = await visaApplicationRequestsHandler.Get(cancellationToken); |         var result = await visaApplicationRequestsHandler.GetAllAsync(cancellationToken); | ||||||
|         return Ok(result); |         return Ok(result); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -41,7 +37,7 @@ public class VisaApplicationController(IVisaApplicationRequestsHandler visaAppli | |||||||
|     public async Task<IActionResult> GetForApplicant(CancellationToken cancellationToken) |     public async Task<IActionResult> GetForApplicant(CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         var userId = GetUserId(); |         var userId = GetUserId(); | ||||||
|         var result = await visaApplicationRequestsHandler.GetForApplicant(userId, cancellationToken); |         var result = await visaApplicationRequestsHandler.GetForApplicantAsync(userId, cancellationToken); | ||||||
|         return Ok(result); |         return Ok(result); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -56,7 +52,38 @@ public class VisaApplicationController(IVisaApplicationRequestsHandler visaAppli | |||||||
|     public async Task<IActionResult> Create(VisaApplicationCreateRequest request, CancellationToken cancellationToken) |     public async Task<IActionResult> Create(VisaApplicationCreateRequest request, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|         var userId = GetUserId(); |         var userId = GetUserId(); | ||||||
|         await visaApplicationRequestsHandler.HandleCreateRequest(userId, request, cancellationToken); |         await visaApplicationRequestsHandler.HandleCreateRequestAsync(userId, request, cancellationToken); | ||||||
|  |         return Ok(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> Sets application status to closed</summary> | ||||||
|  |     /// <remarks> Accessible only for applicant</remarks> | ||||||
|  |     [HttpPatch] | ||||||
|  |     [ProducesResponseType(StatusCodes.Status200OK)] | ||||||
|  |     [ProducesResponseType(StatusCodes.Status403Forbidden)] | ||||||
|  |     [ProducesResponseType(StatusCodes.Status401Unauthorized)] | ||||||
|  |     [ProducesResponseType(StatusCodes.Status404NotFound)] | ||||||
|  |     [Authorize(policy: PolicyConstants.ApplicantPolicy)] | ||||||
|  |     [Route("{applicationId:guid}")] | ||||||
|  |     public async Task<IActionResult> CloseApplication(Guid applicationId, CancellationToken cancellationToken) | ||||||
|  |     { | ||||||
|  |         var userId = GetUserId(); | ||||||
|  |         await visaApplicationRequestsHandler.HandleCloseRequestAsync(userId, applicationId, cancellationToken); | ||||||
|  |         return Ok(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> Allows approving authorities approve or reject applications</summary> | ||||||
|  |     /// <remarks> Accessible only for authorities</remarks> | ||||||
|  |     [HttpPatch] | ||||||
|  |     [ProducesResponseType(StatusCodes.Status200OK)] | ||||||
|  |     [ProducesResponseType(StatusCodes.Status403Forbidden)] | ||||||
|  |     [ProducesResponseType(StatusCodes.Status401Unauthorized)] | ||||||
|  |     [ProducesResponseType(StatusCodes.Status404NotFound)] | ||||||
|  |     [Authorize(policy: PolicyConstants.ApprovingAuthorityPolicy)] | ||||||
|  |     [Route("approving/{applicationId:guid}")] | ||||||
|  |     public async Task<IActionResult> SetStatusFromAuthority(Guid applicationId, AuthorityRequestStatuses status, CancellationToken cancellationToken) | ||||||
|  |     { | ||||||
|  |         await visaApplicationRequestsHandler.SetApplicationStatusFromAuthorityAsync(applicationId, status, cancellationToken); | ||||||
|         return Ok(); |         return Ok(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| using ApplicationLayer.GeneralExceptions; | using ApplicationLayer.GeneralExceptions; | ||||||
| using ApplicationLayer.Services.AuthServices.LoginService.Exceptions; | using ApplicationLayer.Services.AuthServices.LoginService.Exceptions; | ||||||
| using ApplicationLayer.Services.GeneralExceptions; | using ApplicationLayer.Services.GeneralExceptions; | ||||||
| using Domains; | using ApplicationLayer.Services.VisaApplications.Exceptions; | ||||||
| using Microsoft.AspNetCore.Mvc; | using Microsoft.AspNetCore.Mvc; | ||||||
| using Microsoft.AspNetCore.Mvc.Filters; | using Microsoft.AspNetCore.Mvc.Filters; | ||||||
|  |  | ||||||
| @@ -21,7 +21,7 @@ namespace SchengenVisaApi.ExceptionFilters | |||||||
|                 problemDetails.Detail = exception.Message; |                 problemDetails.Detail = exception.Message; | ||||||
|                 switch (exception) |                 switch (exception) | ||||||
|                 { |                 { | ||||||
|                     case EntityNotFoundException<IEntity>: |                     case EntityNotFoundException: | ||||||
|                         problemDetails.Status = StatusCodes.Status404NotFound; |                         problemDetails.Status = StatusCodes.Status404NotFound; | ||||||
|                         problemDetails.Title = "Requested entity not found"; |                         problemDetails.Title = "Requested entity not found"; | ||||||
|                         problemDetails.Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.4"; |                         problemDetails.Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.4"; | ||||||
| @@ -36,6 +36,11 @@ namespace SchengenVisaApi.ExceptionFilters | |||||||
|                         problemDetails.Title = "Already exists"; |                         problemDetails.Title = "Already exists"; | ||||||
|                         problemDetails.Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.8"; |                         problemDetails.Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.8"; | ||||||
|                         break; |                         break; | ||||||
|  |                     case ApplicationAlreadyProcessedException: | ||||||
|  |                         problemDetails.Status = StatusCodes.Status409Conflict; | ||||||
|  |                         problemDetails.Title = "Already processed"; | ||||||
|  |                         problemDetails.Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.8"; | ||||||
|  |                         break; | ||||||
|                     default: |                     default: | ||||||
|                         problemDetails.Status = StatusCodes.Status400BadRequest; |                         problemDetails.Status = StatusCodes.Status400BadRequest; | ||||||
|                         problemDetails.Title = "Bad request"; |                         problemDetails.Title = "Bad request"; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 prtsie
					prtsie