diff --git a/src/core/DigitalData.TaskFlow.Application/Common/Exceptions/ApiException.cs b/src/core/DigitalData.TaskFlow.Application/Common/Exceptions/ApiException.cs
deleted file mode 100644
index 7e88849..0000000
--- a/src/core/DigitalData.TaskFlow.Application/Common/Exceptions/ApiException.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using DigitalData.TaskFlow.Domain.Enums;
-
-namespace DigitalData.TaskFlow.Application.Common.Exceptions;
-
-///
-/// Represents an API-facing exception that carries HTTP and domain meta information.
-///
-public class ApiException : Exception
-{
- ///
- /// Initializes a new instance of the class.
- ///
- /// HTTP status code to return.
- /// Domain meta code to return.
- /// Response message.
- public ApiException(int statusCode, MetaCode metaCode, string message)
- : base(message)
- {
- StatusCode = statusCode;
- MetaCode = metaCode;
- }
-
- ///
- /// Gets the HTTP status code.
- ///
- public int StatusCode { get; }
-
- ///
- /// Gets the domain meta code.
- ///
- public MetaCode MetaCode { get; }
-}
diff --git a/src/core/DigitalData.TaskFlow.Application/Common/Exceptions/ConflictApiException.cs b/src/core/DigitalData.TaskFlow.Application/Common/Exceptions/ConflictApiException.cs
deleted file mode 100644
index 937e56d..0000000
--- a/src/core/DigitalData.TaskFlow.Application/Common/Exceptions/ConflictApiException.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using DigitalData.TaskFlow.Domain.Enums;
-
-namespace DigitalData.TaskFlow.Application.Common.Exceptions;
-
-///
-/// Represents a 409 API exception.
-///
-public sealed class ConflictApiException : ApiException
-{
- ///
- /// Initializes a new instance of the class.
- ///
- /// Domain meta code to return.
- /// Response message.
- public ConflictApiException(MetaCode metaCode, string message)
- : base(409, metaCode, message)
- {
- }
-}
diff --git a/src/core/DigitalData.TaskFlow.Application/Common/Exceptions/ForbiddenApiException.cs b/src/core/DigitalData.TaskFlow.Application/Common/Exceptions/ForbiddenApiException.cs
deleted file mode 100644
index 09ae1bd..0000000
--- a/src/core/DigitalData.TaskFlow.Application/Common/Exceptions/ForbiddenApiException.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-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
deleted file mode 100644
index d93772c..0000000
--- a/src/core/DigitalData.TaskFlow.Application/Common/Exceptions/InvalidRequestApiException.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-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/Exceptions/InvalidWorkflowPayloadException.cs b/src/core/DigitalData.TaskFlow.Application/Common/Exceptions/InvalidWorkflowPayloadException.cs
new file mode 100644
index 0000000..1e161ea
--- /dev/null
+++ b/src/core/DigitalData.TaskFlow.Application/Common/Exceptions/InvalidWorkflowPayloadException.cs
@@ -0,0 +1,5 @@
+namespace DigitalData.TaskFlow.Application.Common.Exceptions;
+
+public sealed class InvalidWorkflowPayloadException(string message) : Exception(message)
+{
+}
diff --git a/src/core/DigitalData.TaskFlow.Application/Common/Exceptions/NotFoundApiException.cs b/src/core/DigitalData.TaskFlow.Application/Common/Exceptions/NotFoundApiException.cs
deleted file mode 100644
index bbc75c7..0000000
--- a/src/core/DigitalData.TaskFlow.Application/Common/Exceptions/NotFoundApiException.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using DigitalData.TaskFlow.Domain.Enums;
-
-namespace DigitalData.TaskFlow.Application.Common.Exceptions;
-
-///
-/// Represents a 404 API exception.
-///
-public sealed class NotFoundApiException : ApiException
-{
- ///
- /// Initializes a new instance of the class.
- ///
- /// Response message.
- public NotFoundApiException(string message)
- : base(404, MetaCode.NotFound, message)
- {
- }
-}
diff --git a/src/core/DigitalData.TaskFlow.Application/Common/Factories/ApiEnvelopeFactory.cs b/src/core/DigitalData.TaskFlow.Application/Common/Factories/ApiEnvelopeFactory.cs
deleted file mode 100644
index 362fa90..0000000
--- a/src/core/DigitalData.TaskFlow.Application/Common/Factories/ApiEnvelopeFactory.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using DigitalData.TaskFlow.Domain.Common;
-using DigitalData.TaskFlow.Domain.Enums;
-
-namespace DigitalData.TaskFlow.Application.Common.Factories;
-
-///
-/// Creates standard API envelope instances with centralized meta code/message handling.
-///
-public static class ApiEnvelopeFactory
-{
- ///
- /// Creates a successful or business-result response envelope.
- ///
- /// Payload type.
- /// Payload data.
- /// Business result code.
- /// Optional custom message. If not supplied, a default message is resolved from .
- /// Response envelope containing meta and payload.
- public static ApiEnvelope Create(T data, MetaCode code = MetaCode.Ok, string? message = null)
- {
- return new ApiEnvelope
- {
- Meta =
- [
- new MetaEntry
- {
- Code = (int)code,
- Message = message ?? ResolveDefaultMessage(code)
- }
- ],
- Data = data
- };
- }
-
- private static string ResolveDefaultMessage(MetaCode code)
- {
- return code switch
- {
- MetaCode.Ok => "OK",
- MetaCode.NoOpenWorkflows => "Keine offenen Workflows",
- MetaCode.NoAccess => "Kein Zugriff",
- MetaCode.InvalidRequest => "Ungultiger Request",
- MetaCode.InternalError => "Interner Fehler",
- MetaCode.NotFound => "Vorgang nicht gefunden",
- MetaCode.AlreadyClosed => "Vorgang bereits abgeschlossen",
- _ => "Interner Fehler"
- };
- }
-}
diff --git a/src/core/DigitalData.TaskFlow.Application/Common/Interfaces/IDocumentRepository.cs b/src/core/DigitalData.TaskFlow.Application/Common/Interfaces/IDocumentRepository.cs
index c42220e..2a52fa1 100644
--- a/src/core/DigitalData.TaskFlow.Application/Common/Interfaces/IDocumentRepository.cs
+++ b/src/core/DigitalData.TaskFlow.Application/Common/Interfaces/IDocumentRepository.cs
@@ -1,17 +1,13 @@
-using DigitalData.TaskFlow.Domain.Documents;
+using DigitalData.TaskFlow.Domain.ProcedureResults;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
namespace DigitalData.TaskFlow.Application.Common.Interfaces;
-///
-/// Defines document-specific data access operations.
-///
-public interface IDocumentRepository : IRepository
+public interface IDocumentRepository
{
- ///
- /// Gets document binary content for the given document identifier.
- ///
- /// Document identifier.
- /// Cancellation token.
- /// Binary document payload when found; otherwise .
- Task GetAsync(string id, CancellationToken cancellationToken);
+ Task GetAsync(int userId, long processId, long docId, CancellationToken cancellationToken);
}
diff --git a/src/core/DigitalData.TaskFlow.Application/Common/Interfaces/IWorkflowRepository.cs b/src/core/DigitalData.TaskFlow.Application/Common/Interfaces/IWorkflowRepository.cs
index 87fb6ec..5f122a3 100644
--- a/src/core/DigitalData.TaskFlow.Application/Common/Interfaces/IWorkflowRepository.cs
+++ b/src/core/DigitalData.TaskFlow.Application/Common/Interfaces/IWorkflowRepository.cs
@@ -1,5 +1,4 @@
-using DigitalData.TaskFlow.Domain.Closing;
-using DigitalData.TaskFlow.Domain.Workflows;
+using DigitalData.TaskFlow.Domain.ProcedureResults;
namespace DigitalData.TaskFlow.Application.Common.Interfaces;
@@ -14,21 +13,5 @@ public interface IWorkflowRepository : IRepository
/// WISAG user identifier.
/// Cancellation token.
/// Workflow data grouped by source.
- Task GetAsync(int wisagId, CancellationToken cancellationToken);
-
- ///
- /// Applies a closing decision to a workflow process.
- ///
- /// WISAG user identifier.
- /// Source system name.
- /// Process identifier.
- /// Closing decision payload.
- /// Cancellation token.
- /// Closing operation result.
- Task CloseAsync(
- int wisagId,
- string source,
- int processId,
- CloseWorkflowDecision decision,
- CancellationToken cancellationToken);
+ Task GetByWisagIdAsync(int wisagId, CancellationToken cancellationToken);
}
diff --git a/src/core/DigitalData.TaskFlow.Application/Common/Mappings/ApplicationMappingProfile.cs b/src/core/DigitalData.TaskFlow.Application/Common/Mappings/ApplicationMappingProfile.cs
deleted file mode 100644
index b9ed651..0000000
--- a/src/core/DigitalData.TaskFlow.Application/Common/Mappings/ApplicationMappingProfile.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using AutoMapper;
-using DigitalData.TaskFlow.Application.Features.Workflows.Commands.CloseWorkflow;
-using DigitalData.TaskFlow.Domain.Closing;
-
-namespace DigitalData.TaskFlow.Application.Common.Mappings;
-
-///
-/// Defines AutoMapper configuration for application-level mappings.
-///
-public sealed class ApplicationMappingProfile : Profile
-{
- ///
- /// Initializes mapping configuration.
- ///
- public ApplicationMappingProfile()
- {
- CreateMap();
- }
-}
diff --git a/src/core/DigitalData.TaskFlow.Application/DependencyInjection.cs b/src/core/DigitalData.TaskFlow.Application/DependencyInjection.cs
index 59c36d5..4f34d51 100644
--- a/src/core/DigitalData.TaskFlow.Application/DependencyInjection.cs
+++ b/src/core/DigitalData.TaskFlow.Application/DependencyInjection.cs
@@ -1,5 +1,4 @@
-using DigitalData.TaskFlow.Application.Options;
-using FluentValidation;
+using FluentValidation;
using MediatR;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@@ -10,29 +9,30 @@ namespace DigitalData.TaskFlow.Application;
public static class DependencyInjection
{
+ public const string LuckyPennySoftLicenseKeySection = "LuckyPennySoftLicenseKey";
+
public static IServiceCollection AddApplicationServices(this IServiceCollection services, IConfiguration configuration)
{
- var mediatROptions = configuration
- .GetSection(MediatROptions.SectionName)
- .Get();
+ var lpSoftLicenseKey = configuration[LuckyPennySoftLicenseKeySection];
+ var hasLpSoftLicenseKey = !string.IsNullOrWhiteSpace(lpSoftLicenseKey);
- services
- .AddOptions()
- .Bind(configuration.GetSection(MediatROptions.SectionName));
+ var assembly = Assembly.GetExecutingAssembly();
services.AddMediatR(config =>
{
- config.RegisterServicesFromAssembly(typeof(DependencyInjection).Assembly);
+ config.RegisterServicesFromAssembly(assembly);
- if (!string.IsNullOrWhiteSpace(mediatROptions?.LuckyPennySoftLicenseKey))
- {
- config.LicenseKey = mediatROptions.LuckyPennySoftLicenseKey;
- }
+ if (hasLpSoftLicenseKey)
+ config.LicenseKey = lpSoftLicenseKey;
});
- services.AddValidatorsFromAssembly(typeof(DependencyInjection).Assembly);
+ services.AddValidatorsFromAssembly(assembly);
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
- services.AddAutoMapper(_ => { }, Assembly.GetExecutingAssembly());
+ services.AddAutoMapper(config =>
+ {
+ if (hasLpSoftLicenseKey)
+ config.LicenseKey = lpSoftLicenseKey;
+ }, assembly);
return services;
}
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
deleted file mode 100644
index b48c961..0000000
--- a/src/core/DigitalData.TaskFlow.Application/Features/Documents/Queries/GetDocument/GetDocumentQuery.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-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;
-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);
-
- if (document is null)
- {
- throw new NotFoundApiException("Dokument nicht gefunden");
- }
-
- return ApiEnvelopeFactory.Create(document);
- }
-}
diff --git a/src/core/DigitalData.TaskFlow.Application/Features/Documents/Queries/GetDocumentQuery.cs b/src/core/DigitalData.TaskFlow.Application/Features/Documents/Queries/GetDocumentQuery.cs
new file mode 100644
index 0000000..c4c11c3
--- /dev/null
+++ b/src/core/DigitalData.TaskFlow.Application/Features/Documents/Queries/GetDocumentQuery.cs
@@ -0,0 +1,37 @@
+using DigitalData.TaskFlow.Application.Common.Exceptions;
+using DigitalData.TaskFlow.Application.Common.Interfaces;
+using MediatR;
+using System.Text.Json;
+
+namespace DigitalData.TaskFlow.Application.Features.Documents.Queries;
+
+///
+///
+///
+///
+///
+///
+public sealed record GetDocumentQuery(int UserId, long ProcessId, long Id) : IRequest;
+
+public sealed class GetDocumentQueryHandler(IDocumentRepository Repo) : IRequestHandler
+{
+ public async Task Handle(GetDocumentQuery request, CancellationToken cancellationToken)
+ {
+ var document = await Repo.GetAsync(userId: request.UserId, processId: request.ProcessId, docId: request.Id, cancellationToken);
+
+ if (!string.IsNullOrWhiteSpace(document?.Result))
+ {
+ using var doc = JsonDocument.Parse(document.Result);
+ var root = doc.RootElement;
+
+ if (root.ValueKind == JsonValueKind.Object &&
+ root.TryGetProperty("response", out var response))
+ {
+ return response.Clone();
+ }
+ }
+
+ throw new InvalidWorkflowPayloadException(
+ "Stored procedure returned an invalid payload. Expected a JSON object with a 'response' property.");
+ }
+}
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
deleted file mode 100644
index bc2e451..0000000
--- a/src/core/DigitalData.TaskFlow.Application/Features/Workflows/Commands/CloseWorkflow/CloseWorkflowCommand.cs
+++ /dev/null
@@ -1,126 +0,0 @@
-using DigitalData.TaskFlow.Application.Common.Exceptions;
-using DigitalData.TaskFlow.Application.Common.Interfaces;
-using DigitalData.TaskFlow.Application.Common.Factories;
-using AutoMapper;
-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"];
- private const int ApproveMethodId = 2;
- private const int RejectMethodId = 3;
- private const int BackToPerMethodId = 4;
-
- ///
- /// 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.");
-
- RuleFor(x => x.ClosingMethodId)
- .Must(methodId => methodId is ApproveMethodId or RejectMethodId or BackToPerMethodId)
- .WithMessage("Unbekannte closing_method_id");
-
- RuleFor(x => x.Comment)
- .NotEmpty()
- .When(x => x.ClosingMethodId is RejectMethodId or BackToPerMethodId)
- .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");
- }
-}
-
-///
-/// Handles requests.
-///
-public sealed class CloseWorkflowCommandHandler(IWorkflowRepository workflowRepository, IMapper mapper)
- : 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)
- {
- if (string.Equals(request.Source, "Contracts", StringComparison.OrdinalIgnoreCase))
- {
- throw new InvalidRequestApiException("Quelle 'Contracts' wird fur Closing aktuell nicht unterstutzt");
- }
-
- var decision = mapper.Map(request);
-
- var result = await workflowRepository.CloseAsync(
- request.WisagId,
- request.Source,
- request.ProcessId,
- 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");
- }
-
- if (!result.IsClosed)
- {
- throw new ConflictApiException(MetaCode.AlreadyClosed, "Vorgang bereits abgeschlossen");
- }
-
- return ApiEnvelopeFactory.Create(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
deleted file mode 100644
index 271bc88..0000000
--- a/src/core/DigitalData.TaskFlow.Application/Features/Workflows/Queries/GetWorkflows/GetWorkflowsQuery.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-using DigitalData.TaskFlow.Application.Common.Factories;
-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);
-
- if (data.Sources.Count == 0)
- {
- return ApiEnvelopeFactory.Create(data, MetaCode.NoAccess);
- }
-
- var hasListingData = data.Sources.Any(source => source switch
- {
- FerdinandSourceBlock ferdinand => ferdinand.Listing.Count > 0,
- ContractsSourceBlock contracts => contracts.Listing.Count > 0,
- _ => false
- });
-
- if (!hasListingData)
- {
- return ApiEnvelopeFactory.Create(data, MetaCode.NoOpenWorkflows);
- }
-
- return ApiEnvelopeFactory.Create(data);
- }
-}
diff --git a/src/core/DigitalData.TaskFlow.Application/Features/Workflows/Queries/GetWorkflowsQuery.cs b/src/core/DigitalData.TaskFlow.Application/Features/Workflows/Queries/GetWorkflowsQuery.cs
new file mode 100644
index 0000000..890485e
--- /dev/null
+++ b/src/core/DigitalData.TaskFlow.Application/Features/Workflows/Queries/GetWorkflowsQuery.cs
@@ -0,0 +1,35 @@
+using DigitalData.TaskFlow.Application.Common.Exceptions;
+using DigitalData.TaskFlow.Application.Common.Interfaces;
+using MediatR;
+using System.Text.Json;
+
+namespace DigitalData.TaskFlow.Application.Features.Workflows.Queries;
+
+///
+/// Represents a request to fetch grouped workflow data for a WISAG user.
+///
+/// WISAG user identifier.
+public sealed record GetWorkflowsQuery(int WisagId) : IRequest;
+
+public sealed class GetWorkflowsQueryHandler(IWorkflowRepository Repo) : IRequestHandler
+{
+ public async Task Handle(GetWorkflowsQuery request, CancellationToken cancellationToken)
+ {
+ var workflow = await Repo.GetByWisagIdAsync(request.WisagId, cancellationToken);
+
+ if (!string.IsNullOrWhiteSpace(workflow?.Result))
+ {
+ using var document = JsonDocument.Parse(workflow.Result);
+ var root = document.RootElement;
+
+ if (root.ValueKind == JsonValueKind.Object &&
+ root.TryGetProperty("response", out var response))
+ {
+ return response.Clone();
+ }
+ }
+
+ throw new InvalidWorkflowPayloadException(
+ "Stored procedure returned an invalid payload. Expected a JSON object with a 'response' property.");
+ }
+}
diff --git a/src/core/DigitalData.TaskFlow.Application/Options/MediatROptions.cs b/src/core/DigitalData.TaskFlow.Application/Options/MediatROptions.cs
deleted file mode 100644
index a11efff..0000000
--- a/src/core/DigitalData.TaskFlow.Application/Options/MediatROptions.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace DigitalData.TaskFlow.Application.Options;
-
-public sealed class MediatROptions
-{
- public const string SectionName = "MediatR";
-
- public string? LuckyPennySoftLicenseKey { get; init; }
-}
diff --git a/src/core/DigitalData.TaskFlow.Application/README.md b/src/core/DigitalData.TaskFlow.Application/README.md
deleted file mode 100644
index d57e531..0000000
--- a/src/core/DigitalData.TaskFlow.Application/README.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# DigitalData.TaskFlow.Application
-
-Dieses Projekt enthaelt die Use-Case-Schicht der Anwendung.
-
-## Zweck
-
-Die Application-Schicht orchestriert fachliche Anwendungsfaelle. Sie nimmt Requests entgegen, validiert diese, ruft Domain-nahe Abstraktionen auf und liefert strukturierte Ergebnisse zurueck.
-
-## Aktueller Inhalt
-
-- `DependencyInjection.cs`
- - Registriert zentrale Application-Dienste:
- - MediatR
- - FluentValidation
- - AutoMapper
- - Bindet `MediatR`-Optionen inklusive Lizenzschluessel.
-- `Options/`
- - `MediatROptions`: Konfiguration fuer MediatR (z. B. `LuckyPennySoftLicenseKey`).
-
-## Geplante Inhalte (naechste Phasen)
-
-- `Features/`
- - Queries/Commands fuer Workflows, Closing und Documents.
-- `Validators/`
- - FluentValidation-Regeln pro Request.
-- `Common/Behaviors/`
- - Pipeline-Behavior (z. B. zentrale Request-Validierung).
-- `Common/Interfaces/`
- - Repository-Abstraktionen fuer Datenzugriff.
-- `Mappings/`
- - AutoMapper-Profile zwischen Infrastrukturmodellen und Domainmodellen.
-
-## Architektur-Regeln
-
-- Keine direkten SQL- oder EF-Core-Aufrufe.
-- Keine HTTP-spezifischen Controller-Abhaengigkeiten.
-- Kommunikation nach aussen nur ueber Interfaces.
-
-## Rolle im Gesamtsystem
-
-Diese Schicht ist der zentrale Ausfuehrungspunkt fuer Use-Cases und verbindet die API-Schicht mit Domain und Infrastructure, ohne Technologie-Details nach oben durchzureichen.