From ed06e460c31bfb107ed706b68aaa6e8aebb43b13 Mon Sep 17 00:00:00 2001 From: TekH Date: Fri, 9 Oct 2026 23:06:05 +0200 Subject: [PATCH] feat(application): add workflow and document request handlers --- .../Queries/GetDocument/GetDocumentQuery.cs | 60 ++++++++++++ .../CloseWorkflow/CloseWorkflowCommand.cs | 97 +++++++++++++++++++ .../Queries/GetWorkflows/GetWorkflowsQuery.cs | 60 ++++++++++++ 3 files changed, 217 insertions(+) create mode 100644 src/core/DigitalData.TaskFlow.Application/Features/Documents/Queries/GetDocument/GetDocumentQuery.cs create mode 100644 src/core/DigitalData.TaskFlow.Application/Features/Workflows/Commands/CloseWorkflow/CloseWorkflowCommand.cs create mode 100644 src/core/DigitalData.TaskFlow.Application/Features/Workflows/Queries/GetWorkflows/GetWorkflowsQuery.cs 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 new file mode 100644 index 0000000..f2dfc33 --- /dev/null +++ b/src/core/DigitalData.TaskFlow.Application/Features/Documents/Queries/GetDocument/GetDocumentQuery.cs @@ -0,0 +1,60 @@ +using DigitalData.TaskFlow.Application.Common.Interfaces; +using DigitalData.TaskFlow.Domain.Common; +using DigitalData.TaskFlow.Domain.Documents; +using DigitalData.TaskFlow.Domain.Enums; +using FluentValidation; +using MediatR; + +namespace DigitalData.TaskFlow.Application.Features.Documents.Queries.GetDocument; + +/// +/// Represents a request to retrieve binary document content by document identifier. +/// +/// Document identifier. +public sealed record GetDocumentQuery(string Id) : IRequest>; + +/// +/// Validates requests. +/// +public sealed class GetDocumentQueryValidator : AbstractValidator +{ + /// + /// Initializes validation rules for . + /// + public GetDocumentQueryValidator() + { + RuleFor(x => x.Id) + .NotEmpty(); + } +} + +/// +/// Handles requests. +/// +public sealed class GetDocumentQueryHandler(IDocumentRepository documentRepository) + : IRequestHandler> +{ + /// + /// Executes the query and returns binary document data in the standard API envelope. + /// + /// Query request. + /// Cancellation token. + /// Document response envelope. + public async Task> Handle(GetDocumentQuery request, CancellationToken cancellationToken) + { + var document = await documentRepository.GetAsync(request.Id, cancellationToken); + + return new ApiEnvelope + { + Meta = + [ + new MetaEntry + { + Code = (int)MetaCode.Ok, + Message = "OK" + } + ], + Data = 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 new file mode 100644 index 0000000..9aa3067 --- /dev/null +++ b/src/core/DigitalData.TaskFlow.Application/Features/Workflows/Commands/CloseWorkflow/CloseWorkflowCommand.cs @@ -0,0 +1,97 @@ +using DigitalData.TaskFlow.Application.Common.Interfaces; +using DigitalData.TaskFlow.Domain.Closing; +using DigitalData.TaskFlow.Domain.Common; +using DigitalData.TaskFlow.Domain.Enums; +using FluentValidation; +using MediatR; + +namespace DigitalData.TaskFlow.Application.Features.Workflows.Commands.CloseWorkflow; + +/// +/// Represents a request to close a workflow process using a selected closing method. +/// +/// WISAG user identifier. +/// Source system name. +/// Process identifier. +/// Selected closing method identifier. +/// Optional user comment. +/// Optional rejection reason identifier. +public sealed record CloseWorkflowCommand( + int WisagId, + string Source, + int ProcessId, + int ClosingMethodId, + string? Comment, + int? RejectionReasonId) + : IRequest>; + +/// +/// Validates requests. +/// +public sealed class CloseWorkflowCommandValidator : AbstractValidator +{ + private static readonly string[] AllowedSources = ["Ferdinand", "Contracts"]; + + /// + /// Initializes validation rules for . + /// + public CloseWorkflowCommandValidator() + { + RuleFor(x => x.WisagId) + .GreaterThan(0); + + RuleFor(x => x.ProcessId) + .GreaterThan(0); + + RuleFor(x => x.ClosingMethodId) + .GreaterThan(0); + + RuleFor(x => x.Source) + .NotEmpty() + .Must(source => AllowedSources.Contains(source)) + .WithMessage("Source must be one of the supported source systems."); + } +} + +/// +/// Handles requests. +/// +public sealed class CloseWorkflowCommandHandler(IWorkflowRepository workflowRepository) + : IRequestHandler> +{ + /// + /// Executes the closing request and returns the operation result in the standard API envelope. + /// + /// Command request. + /// Cancellation token. + /// Closing result envelope. + public async Task> Handle(CloseWorkflowCommand request, CancellationToken cancellationToken) + { + var decision = new CloseWorkflowDecision + { + ClosingMethodId = request.ClosingMethodId, + Comment = request.Comment, + RejectionReasonId = request.RejectionReasonId + }; + + var result = await workflowRepository.CloseAsync( + request.WisagId, + request.Source, + request.ProcessId, + decision, + cancellationToken); + + return new ApiEnvelope + { + Meta = + [ + new MetaEntry + { + Code = (int)MetaCode.Ok, + Message = "OK" + } + ], + Data = result + }; + } +} 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 new file mode 100644 index 0000000..4e84b4d --- /dev/null +++ b/src/core/DigitalData.TaskFlow.Application/Features/Workflows/Queries/GetWorkflows/GetWorkflowsQuery.cs @@ -0,0 +1,60 @@ +using DigitalData.TaskFlow.Application.Common.Interfaces; +using DigitalData.TaskFlow.Domain.Common; +using DigitalData.TaskFlow.Domain.Enums; +using DigitalData.TaskFlow.Domain.Workflows; +using FluentValidation; +using MediatR; + +namespace DigitalData.TaskFlow.Application.Features.Workflows.Queries.GetWorkflows; + +/// +/// Represents a request to fetch grouped workflow data for a WISAG user. +/// +/// WISAG user identifier. +public sealed record GetWorkflowsQuery(int WisagId) : IRequest>; + +/// +/// Validates requests. +/// +public sealed class GetWorkflowsQueryValidator : AbstractValidator +{ + /// + /// Initializes validation rules for . + /// + public GetWorkflowsQueryValidator() + { + RuleFor(x => x.WisagId) + .GreaterThan(0); + } +} + +/// +/// Handles requests. +/// +public sealed class GetWorkflowsQueryHandler(IWorkflowRepository workflowRepository) + : IRequestHandler> +{ + /// + /// Executes the query and returns a workflow payload wrapped in the standard API envelope. + /// + /// Query request. + /// Cancellation token. + /// Workflow response envelope. + public async Task> Handle(GetWorkflowsQuery request, CancellationToken cancellationToken) + { + var data = await workflowRepository.GetAsync(request.WisagId, cancellationToken); + + return new ApiEnvelope + { + Meta = + [ + new MetaEntry + { + Code = (int)MetaCode.Ok, + Message = "OK" + } + ], + Data = data + }; + } +}