Changed exceptions, added action for closing applications by applicant

This commit is contained in:
2024-08-22 11:43:55 +03:00
parent 99625c957e
commit 7b7ca6ae36
12 changed files with 61 additions and 12 deletions

View File

@@ -14,4 +14,8 @@
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0-preview.7.24405.7" />
</ItemGroup>
<ItemGroup>
<Folder Include="Services\VisaApplications\Handlers\Exceptions\" />
</ItemGroup>
</Project>

View File

@@ -5,5 +5,5 @@ namespace ApplicationLayer.Services.GeneralExceptions;
/// Exception to throw when entity not found
/// <param name="id">Identifier of entity</param>
/// <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;

View File

@@ -1,9 +1,6 @@
using ApplicationLayer.GeneralExceptions;
using Domains;
namespace ApplicationLayer.Services.GeneralExceptions;
/// Exception to throw when entity not found
/// <typeparam name="T">Not found entity type</typeparam>
public class EntityNotFoundException<T>(string message) : ApiException(message)
where T : class, IEntity;
public class EntityNotFoundException(string message) : ApiException(message);

View File

@@ -14,4 +14,7 @@ public interface IVisaApplicationRequestsHandler
/// Creates application for applicant with specific user identifier
Task HandleCreateRequest(Guid userId, VisaApplicationCreateRequest request, CancellationToken cancellationToken);
/// Sets application status to closed
Task HandleCloseRequest(Guid userId, Guid applicationId, CancellationToken cancellationToken);
}

View File

@@ -114,4 +114,15 @@ public class VisaApplicationRequestsHandler(
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);
}
}

View File

@@ -7,4 +7,7 @@ public interface IVisaApplicationsRepository : IGenericRepository<VisaApplicatio
{
/// Get applications of one applicant
Task<List<VisaApplication>> GetOfApplicantAsync(Guid applicantId, CancellationToken cancellationToken);
/// Get specific application of specific user
Task<VisaApplication> GetByApplicantAndApplicationIdAsync(Guid applicantId, Guid applicationId, CancellationToken cancellationToken);
}

View File

@@ -21,12 +21,12 @@ public sealed class ApplicantsRepository(IGenericReader reader, IGenericWriter w
async Task<Applicant> IApplicantsRepository.FindByUserIdAsync(Guid userId, CancellationToken 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)
{
var result = await base.LoadDomain().SingleOrDefaultAsync(a => a.UserId == userId, cancellationToken);
return result?.Id ?? throw new ApplicantNotFoundByUserIdException(userId);
return result?.Id ?? throw new ApplicantNotFoundByUserIdException();
}
}

View File

@@ -3,8 +3,5 @@ using Domains.ApplicantDomain;
namespace Infrastructure.Database.Applicants.Repositories.Exceptions
{
public class ApplicantNotFoundByUserIdException(Guid id) : EntityNotFoundException<Applicant>("Applicant not found.")
{
public Guid UserId { get; private set; } = id;
}
public class ApplicantNotFoundByUserIdException() : EntityNotFoundException("Applicant not found.");
}

View File

@@ -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");
}

View File

@@ -1,6 +1,7 @@
using ApplicationLayer.Services.VisaApplications.NeededServices;
using Domains.VisaApplicationDomain;
using Infrastructure.Database.Generic;
using Infrastructure.Database.VisaApplications.Repositories.Exceptions;
using Microsoft.EntityFrameworkCore;
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)
=> 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);
}
}

View File

@@ -57,4 +57,20 @@ public class VisaApplicationController(IVisaApplicationRequestsHandler visaAppli
await visaApplicationRequestsHandler.HandleCreateRequest(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.HandleCloseRequest(userId, applicationId, cancellationToken);
return Ok();
}
}

View File

@@ -21,7 +21,7 @@ namespace SchengenVisaApi.ExceptionFilters
problemDetails.Detail = exception.Message;
switch (exception)
{
case EntityNotFoundException<IEntity>:
case EntityNotFoundException:
problemDetails.Status = StatusCodes.Status404NotFound;
problemDetails.Title = "Requested entity not found";
problemDetails.Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.4";