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