diff --git a/SchengenVisaApi/ApplicationLayer/ApplicationLayer.csproj b/SchengenVisaApi/ApplicationLayer/ApplicationLayer.csproj
index bc6298d..001de30 100644
--- a/SchengenVisaApi/ApplicationLayer/ApplicationLayer.csproj
+++ b/SchengenVisaApi/ApplicationLayer/ApplicationLayer.csproj
@@ -14,4 +14,8 @@
+
+
+
+
diff --git a/SchengenVisaApi/ApplicationLayer/Services/GeneralExceptions/EntityNotFoundByIdException.cs b/SchengenVisaApi/ApplicationLayer/Services/GeneralExceptions/EntityNotFoundByIdException.cs
index d3155fe..d7d3e03 100644
--- a/SchengenVisaApi/ApplicationLayer/Services/GeneralExceptions/EntityNotFoundByIdException.cs
+++ b/SchengenVisaApi/ApplicationLayer/Services/GeneralExceptions/EntityNotFoundByIdException.cs
@@ -5,5 +5,5 @@ namespace ApplicationLayer.Services.GeneralExceptions;
/// Exception to throw when entity not found
/// Identifier of entity
/// Type of entity
-public class EntityNotFoundByIdException(Guid id) : EntityNotFoundException($"{typeof(T).Name} with id {id} not found.")
+public class EntityNotFoundByIdException(Guid id) : EntityNotFoundException($"{typeof(T).Name} with id {id} not found.")
where T : class, IEntity;
diff --git a/SchengenVisaApi/ApplicationLayer/Services/GeneralExceptions/EntityNotFoundException.cs b/SchengenVisaApi/ApplicationLayer/Services/GeneralExceptions/EntityNotFoundException.cs
index 4f4acca..c0f4867 100644
--- a/SchengenVisaApi/ApplicationLayer/Services/GeneralExceptions/EntityNotFoundException.cs
+++ b/SchengenVisaApi/ApplicationLayer/Services/GeneralExceptions/EntityNotFoundException.cs
@@ -1,9 +1,6 @@
using ApplicationLayer.GeneralExceptions;
-using Domains;
namespace ApplicationLayer.Services.GeneralExceptions;
/// Exception to throw when entity not found
-/// Not found entity type
-public class EntityNotFoundException(string message) : ApiException(message)
- where T : class, IEntity;
+public class EntityNotFoundException(string message) : ApiException(message);
diff --git a/SchengenVisaApi/ApplicationLayer/Services/VisaApplications/Handlers/IVisaApplicationRequestsHandler.cs b/SchengenVisaApi/ApplicationLayer/Services/VisaApplications/Handlers/IVisaApplicationRequestsHandler.cs
index e92e7d6..fcd2b53 100644
--- a/SchengenVisaApi/ApplicationLayer/Services/VisaApplications/Handlers/IVisaApplicationRequestsHandler.cs
+++ b/SchengenVisaApi/ApplicationLayer/Services/VisaApplications/Handlers/IVisaApplicationRequestsHandler.cs
@@ -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);
}
diff --git a/SchengenVisaApi/ApplicationLayer/Services/VisaApplications/Handlers/VisaApplicationRequestsHandler.cs b/SchengenVisaApi/ApplicationLayer/Services/VisaApplications/Handlers/VisaApplicationRequestsHandler.cs
index 6997425..ab30d22 100644
--- a/SchengenVisaApi/ApplicationLayer/Services/VisaApplications/Handlers/VisaApplicationRequestsHandler.cs
+++ b/SchengenVisaApi/ApplicationLayer/Services/VisaApplications/Handlers/VisaApplicationRequestsHandler.cs
@@ -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);
+ }
}
diff --git a/SchengenVisaApi/ApplicationLayer/Services/VisaApplications/NeededServices/IVisaApplicationsRepository.cs b/SchengenVisaApi/ApplicationLayer/Services/VisaApplications/NeededServices/IVisaApplicationsRepository.cs
index 8904a67..5e132a8 100644
--- a/SchengenVisaApi/ApplicationLayer/Services/VisaApplications/NeededServices/IVisaApplicationsRepository.cs
+++ b/SchengenVisaApi/ApplicationLayer/Services/VisaApplications/NeededServices/IVisaApplicationsRepository.cs
@@ -7,4 +7,7 @@ public interface IVisaApplicationsRepository : IGenericRepository> GetOfApplicantAsync(Guid applicantId, CancellationToken cancellationToken);
+
+ /// Get specific application of specific user
+ Task GetByApplicantAndApplicationIdAsync(Guid applicantId, Guid applicationId, CancellationToken cancellationToken);
}
diff --git a/SchengenVisaApi/Infrastructure/Database/Applicants/Repositories/ApplicantsRepository.cs b/SchengenVisaApi/Infrastructure/Database/Applicants/Repositories/ApplicantsRepository.cs
index a5a9ef5..64f4e7f 100644
--- a/SchengenVisaApi/Infrastructure/Database/Applicants/Repositories/ApplicantsRepository.cs
+++ b/SchengenVisaApi/Infrastructure/Database/Applicants/Repositories/ApplicantsRepository.cs
@@ -21,12 +21,12 @@ public sealed class ApplicantsRepository(IGenericReader reader, IGenericWriter w
async Task 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 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();
}
}
diff --git a/SchengenVisaApi/Infrastructure/Database/Applicants/Repositories/Exceptions/ApplicantNotFoundByUserIdException.cs b/SchengenVisaApi/Infrastructure/Database/Applicants/Repositories/Exceptions/ApplicantNotFoundByUserIdException.cs
index 52e7046..a45f9e8 100644
--- a/SchengenVisaApi/Infrastructure/Database/Applicants/Repositories/Exceptions/ApplicantNotFoundByUserIdException.cs
+++ b/SchengenVisaApi/Infrastructure/Database/Applicants/Repositories/Exceptions/ApplicantNotFoundByUserIdException.cs
@@ -3,8 +3,5 @@ using Domains.ApplicantDomain;
namespace Infrastructure.Database.Applicants.Repositories.Exceptions
{
- public class ApplicantNotFoundByUserIdException(Guid id) : EntityNotFoundException("Applicant not found.")
- {
- public Guid UserId { get; private set; } = id;
- }
+ public class ApplicantNotFoundByUserIdException() : EntityNotFoundException("Applicant not found.");
}
diff --git a/SchengenVisaApi/Infrastructure/Database/VisaApplications/Repositories/Exceptions/ApplicationNotFoundByApplicantAndApplicationIdException.cs b/SchengenVisaApi/Infrastructure/Database/VisaApplications/Repositories/Exceptions/ApplicationNotFoundByApplicantAndApplicationIdException.cs
new file mode 100644
index 0000000..b8d1241
--- /dev/null
+++ b/SchengenVisaApi/Infrastructure/Database/VisaApplications/Repositories/Exceptions/ApplicationNotFoundByApplicantAndApplicationIdException.cs
@@ -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");
+}
diff --git a/SchengenVisaApi/Infrastructure/Database/VisaApplications/Repositories/VisaApplicationsRepository.cs b/SchengenVisaApi/Infrastructure/Database/VisaApplications/Repositories/VisaApplicationsRepository.cs
index cd20971..2a40dd7 100644
--- a/SchengenVisaApi/Infrastructure/Database/VisaApplications/Repositories/VisaApplicationsRepository.cs
+++ b/SchengenVisaApi/Infrastructure/Database/VisaApplications/Repositories/VisaApplicationsRepository.cs
@@ -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> IVisaApplicationsRepository.GetOfApplicantAsync(Guid applicantId, CancellationToken cancellationToken)
=> await LoadDomain().Where(va => va.ApplicantId == applicantId).ToListAsync(cancellationToken);
+
+ async Task 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);
+ }
}
diff --git a/SchengenVisaApi/SchengenVisaApi/Controllers/VisaApplicationController.cs b/SchengenVisaApi/SchengenVisaApi/Controllers/VisaApplicationController.cs
index 03f2a0e..50125fb 100644
--- a/SchengenVisaApi/SchengenVisaApi/Controllers/VisaApplicationController.cs
+++ b/SchengenVisaApi/SchengenVisaApi/Controllers/VisaApplicationController.cs
@@ -57,4 +57,20 @@ public class VisaApplicationController(IVisaApplicationRequestsHandler visaAppli
await visaApplicationRequestsHandler.HandleCreateRequest(userId, request, cancellationToken);
return Ok();
}
+
+ /// Sets application status to closed
+ /// Accessible only for applicant
+ [HttpPatch]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status403Forbidden)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ [Authorize(policy: PolicyConstants.ApplicantPolicy)]
+ [Route("{applicationId:guid}")]
+ public async Task CloseApplication(Guid applicationId, CancellationToken cancellationToken)
+ {
+ var userId = GetUserId();
+ await visaApplicationRequestsHandler.HandleCloseRequest(userId, applicationId, cancellationToken);
+ return Ok();
+ }
}
diff --git a/SchengenVisaApi/SchengenVisaApi/ExceptionFilters/GlobalExceptionsFilter.cs b/SchengenVisaApi/SchengenVisaApi/ExceptionFilters/GlobalExceptionsFilter.cs
index 3bd38a6..d8e7c94 100644
--- a/SchengenVisaApi/SchengenVisaApi/ExceptionFilters/GlobalExceptionsFilter.cs
+++ b/SchengenVisaApi/SchengenVisaApi/ExceptionFilters/GlobalExceptionsFilter.cs
@@ -21,7 +21,7 @@ namespace SchengenVisaApi.ExceptionFilters
problemDetails.Detail = exception.Message;
switch (exception)
{
- case EntityNotFoundException:
+ 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";