Changed exceptions, added action for closing applications by applicant
This commit is contained in:
		| @@ -14,4 +14,8 @@ | |||||||
|       <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0-preview.7.24405.7" /> |       <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0-preview.7.24405.7" /> | ||||||
|     </ItemGroup> |     </ItemGroup> | ||||||
|  |  | ||||||
|  |     <ItemGroup> | ||||||
|  |       <Folder Include="Services\VisaApplications\Handlers\Exceptions\" /> | ||||||
|  |     </ItemGroup> | ||||||
|  |  | ||||||
| </Project> | </Project> | ||||||
|   | |||||||
| @@ -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; |  | ||||||
|   | |||||||
| @@ -14,4 +14,7 @@ public interface IVisaApplicationRequestsHandler | |||||||
|  |  | ||||||
|     /// Creates application for applicant with specific user identifier |     /// Creates application for applicant with specific user identifier | ||||||
|     Task HandleCreateRequest(Guid userId, VisaApplicationCreateRequest request, CancellationToken cancellationToken); |     Task HandleCreateRequest(Guid userId, VisaApplicationCreateRequest request, CancellationToken cancellationToken); | ||||||
|  |  | ||||||
|  |     /// Sets application status to closed | ||||||
|  |     Task HandleCloseRequest(Guid userId, Guid applicationId, CancellationToken cancellationToken); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -114,4 +114,15 @@ public class VisaApplicationRequestsHandler( | |||||||
|  |  | ||||||
|         await unitOfWork.SaveAsync(cancellationToken); |         await unitOfWork.SaveAsync(cancellationToken); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     async Task IVisaApplicationRequestsHandler.HandleCloseRequest(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); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -3,8 +3,5 @@ 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); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -57,4 +57,20 @@ public class VisaApplicationController(IVisaApplicationRequestsHandler visaAppli | |||||||
|         await visaApplicationRequestsHandler.HandleCreateRequest(userId, request, cancellationToken); |         await visaApplicationRequestsHandler.HandleCreateRequest(userId, request, cancellationToken); | ||||||
|         return Ok(); |         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.HandleCloseRequest(userId, applicationId, cancellationToken); | ||||||
|  |         return Ok(); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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"; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user