diff --git a/src/core/DigitalData.TaskFlow.Application/Common/Exceptions/ForbiddenApiException.cs b/src/core/DigitalData.TaskFlow.Application/Common/Exceptions/ForbiddenApiException.cs new file mode 100644 index 0000000..09ae1bd --- /dev/null +++ b/src/core/DigitalData.TaskFlow.Application/Common/Exceptions/ForbiddenApiException.cs @@ -0,0 +1,18 @@ +using DigitalData.TaskFlow.Domain.Enums; + +namespace DigitalData.TaskFlow.Application.Common.Exceptions; + +/// +/// Represents a 403 API exception. +/// +public sealed class ForbiddenApiException : ApiException +{ + /// + /// Initializes a new instance of the class. + /// + /// Response message. + public ForbiddenApiException(string message) + : base(403, MetaCode.NoAccess, message) + { + } +} diff --git a/src/core/DigitalData.TaskFlow.Application/Common/Exceptions/InvalidRequestApiException.cs b/src/core/DigitalData.TaskFlow.Application/Common/Exceptions/InvalidRequestApiException.cs new file mode 100644 index 0000000..d93772c --- /dev/null +++ b/src/core/DigitalData.TaskFlow.Application/Common/Exceptions/InvalidRequestApiException.cs @@ -0,0 +1,18 @@ +using DigitalData.TaskFlow.Domain.Enums; + +namespace DigitalData.TaskFlow.Application.Common.Exceptions; + +/// +/// Represents a 400 API exception. +/// +public sealed class InvalidRequestApiException : ApiException +{ + /// + /// Initializes a new instance of the class. + /// + /// Response message. + public InvalidRequestApiException(string message) + : base(400, MetaCode.InvalidRequest, message) + { + } +} diff --git a/src/core/DigitalData.TaskFlow.Application/Common/Interfaces/IDocumentRepository.cs b/src/core/DigitalData.TaskFlow.Application/Common/Interfaces/IDocumentRepository.cs index 058c4fd..c42220e 100644 --- a/src/core/DigitalData.TaskFlow.Application/Common/Interfaces/IDocumentRepository.cs +++ b/src/core/DigitalData.TaskFlow.Application/Common/Interfaces/IDocumentRepository.cs @@ -12,6 +12,6 @@ public interface IDocumentRepository : IRepository /// /// Document identifier. /// Cancellation token. - /// Binary document payload. - Task GetAsync(string id, CancellationToken cancellationToken); + /// Binary document payload when found; otherwise . + Task GetAsync(string id, CancellationToken cancellationToken); } diff --git a/src/core/DigitalData.TaskFlow.Application/Features/Documents/Queries/GetDocument/GetDocumentQuery.cs b/src/core/DigitalData.TaskFlow.Application/Features/Documents/Queries/GetDocument/GetDocumentQuery.cs index 206b481..b48c961 100644 --- a/src/core/DigitalData.TaskFlow.Application/Features/Documents/Queries/GetDocument/GetDocumentQuery.cs +++ b/src/core/DigitalData.TaskFlow.Application/Features/Documents/Queries/GetDocument/GetDocumentQuery.cs @@ -1,4 +1,5 @@ using DigitalData.TaskFlow.Application.Common.Factories; +using DigitalData.TaskFlow.Application.Common.Exceptions; using DigitalData.TaskFlow.Application.Common.Interfaces; using DigitalData.TaskFlow.Domain.Common; using DigitalData.TaskFlow.Domain.Documents; @@ -45,6 +46,11 @@ public sealed class GetDocumentQueryHandler(IDocumentRepository documentReposito { var document = await documentRepository.GetAsync(request.Id, cancellationToken); + if (document is null) + { + throw new NotFoundApiException("Dokument nicht gefunden"); + } + return ApiEnvelopeFactory.Create(document); } } diff --git a/src/core/DigitalData.TaskFlow.Application/Features/Workflows/Commands/CloseWorkflow/CloseWorkflowCommand.cs b/src/core/DigitalData.TaskFlow.Application/Features/Workflows/Commands/CloseWorkflow/CloseWorkflowCommand.cs index 332c77b..94ebd3c 100644 --- a/src/core/DigitalData.TaskFlow.Application/Features/Workflows/Commands/CloseWorkflow/CloseWorkflowCommand.cs +++ b/src/core/DigitalData.TaskFlow.Application/Features/Workflows/Commands/CloseWorkflow/CloseWorkflowCommand.cs @@ -34,6 +34,8 @@ public sealed record CloseWorkflowCommand( public sealed class CloseWorkflowCommandValidator : AbstractValidator { private static readonly string[] AllowedSources = ["Ferdinand", "Contracts"]; + private const int ApproveMethodId = 2; + private const int RejectMethodId = 3; /// /// Initializes validation rules for . @@ -53,6 +55,20 @@ public sealed class CloseWorkflowCommandValidator : AbstractValidator AllowedSources.Contains(source)) .WithMessage("Source must be one of the supported source systems."); + + RuleFor(x => x.ClosingMethodId) + .Must(methodId => methodId is ApproveMethodId or RejectMethodId) + .WithMessage("Unbekannte closing_method_id"); + + RuleFor(x => x.Comment) + .NotEmpty() + .When(x => x.ClosingMethodId == RejectMethodId) + .WithMessage("Kommentar ist fur diese closing_method Pflicht"); + + RuleFor(x => x.RejectionReasonId) + .NotNull() + .When(x => x.ClosingMethodId == RejectMethodId) + .WithMessage("rejection_reason_id ist fur diese closing_method Pflicht"); } } @@ -79,6 +95,16 @@ public sealed class CloseWorkflowCommandHandler(IWorkflowRepository workflowRepo decision, cancellationToken); + if (result.Outcome == CloseWorkflowOutcome.NoAccess) + { + throw new ForbiddenApiException("Kein Zugriff"); + } + + if (result.Outcome == CloseWorkflowOutcome.NotImplemented) + { + throw new InvalidOperationException("Closing integration is not finalized yet."); + } + if (result.ProcessId <= 0) { throw new NotFoundApiException("Vorgang nicht gefunden"); diff --git a/src/core/DigitalData.TaskFlow.Application/Features/Workflows/Queries/GetWorkflows/GetWorkflowsQuery.cs b/src/core/DigitalData.TaskFlow.Application/Features/Workflows/Queries/GetWorkflows/GetWorkflowsQuery.cs index 78a3e16..aa4449e 100644 --- a/src/core/DigitalData.TaskFlow.Application/Features/Workflows/Queries/GetWorkflows/GetWorkflowsQuery.cs +++ b/src/core/DigitalData.TaskFlow.Application/Features/Workflows/Queries/GetWorkflows/GetWorkflowsQuery.cs @@ -45,6 +45,11 @@ public sealed class GetWorkflowsQueryHandler(IWorkflowRepository workflowReposit { var data = await workflowRepository.GetAsync(request.WisagId, cancellationToken); + if (data.Sources.Count == 0) + { + return ApiEnvelopeFactory.Create(data, MetaCode.NoAccess); + } + var hasListingData = data.Sources .SelectMany(source => source switch {