diff --git a/DocumentOperator.API/Configuration/SwaggerConfiguration.cs b/DocumentOperator.API/Configuration/SwaggerConfiguration.cs
index bb2a73f..6185eef 100644
--- a/DocumentOperator.API/Configuration/SwaggerConfiguration.cs
+++ b/DocumentOperator.API/Configuration/SwaggerConfiguration.cs
@@ -1,6 +1,28 @@
-namespace DocumentOperator.API.Configuration
+using Microsoft.OpenApi.Models;
+using System.Reflection;
+
+namespace DocumentOperator.API.Configuration
{
- public class SwaggerConfiguration
+ public static class SwaggerConfiguration
{
+ public static IServiceCollection AddSwaggerDocumentation(this IServiceCollection services)
+ {
+ services.AddSwaggerGen(options =>
+ {
+ options.SwaggerDoc("v1", new OpenApiInfo
+ {
+ Title = "DD Document Operator API",
+ Version = "v1",
+ Description = "PDF Verarbeitungs-Service für Validierung, Stempel, Zertifikate, Anhänge & Zusammenführung"
+ });
+
+ // XML-Kommentare einbinden
+ var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
+ var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
+ options.IncludeXmlComments(xmlPath);
+ });
+
+ return services;
+ }
}
}
diff --git a/DocumentOperator.API/DocumentOperator.API.csproj b/DocumentOperator.API/DocumentOperator.API.csproj
index 83c30fc..30edb12 100644
--- a/DocumentOperator.API/DocumentOperator.API.csproj
+++ b/DocumentOperator.API/DocumentOperator.API.csproj
@@ -4,6 +4,7 @@
net8.0
enable
enable
+ true
diff --git a/DocumentOperator.API/Endpoints/v1/DocumentEndpoints.cs b/DocumentOperator.API/Endpoints/v1/DocumentEndpoints.cs
index 523afbd..8246650 100644
--- a/DocumentOperator.API/Endpoints/v1/DocumentEndpoints.cs
+++ b/DocumentOperator.API/Endpoints/v1/DocumentEndpoints.cs
@@ -1,6 +1,67 @@
-namespace DocumentOperator.API.Endpoints.v1
+using DocumentOperator.Application.Common.DTOs;
+using DocumentOperator.Application.Features.Documents.ValidatePdf;
+using DocumentOperator.Domain.Models.ValueObjects;
+using MediatR;
+using Microsoft.AspNetCore.Mvc;
+
+namespace DocumentOperator.API.Endpoints.v1;
+
+///
+/// Document endpoints (Minimal API)
+///
+public static class DocumentEndpoints
{
- public class DocumentEndpoints
+ ///
+ /// Maps all document-related endpoints
+ ///
+ public static void MapDocumentEndpoints(this IEndpointRouteBuilder app)
{
+ var group = app.MapGroup("/api/v1/documents")
+ .WithTags("Documents");
+
+ // POST /api/v1/documents/validate
+ group.MapPost("/validate", ValidatePdf)
+ .WithName("ValidatePdf")
+ .WithSummary("Validates a PDF document and returns metadata")
+ .WithDescription("Validates the PDF format and extracts metadata (page count, file size, PDF version, attachments)")
+ .Produces(StatusCodes.Status200OK)
+ .Produces(StatusCodes.Status400BadRequest)
+ .Produces(StatusCodes.Status500InternalServerError);
+ }
+
+ ///
+ /// Validates a PDF document and returns metadata
+ ///
+ /// PDF as Base64 string
+ /// MediatR instance
+ /// Cancellation token
+ /// PDF metadata (page count, file size, etc.)
+ /// PDF is valid, metadata returned
+ /// Invalid PDF or Base64 format
+ /// Internal server error during validation
+ private static async Task ValidatePdf(
+ ValidatePdfRequest request,
+ IMediator mediator,
+ CancellationToken cancellationToken)
+ {
+ // DTO → Query (Value Objects erstellen - kann DomainValidationException werfen!)
+ var query = new ValidatePdfQuery(
+ Base64String.Create(request.Base64Pdf)
+ );
+
+ // MediatR Handler aufrufen (ValidationBehavior → Handler)
+ var metadata = await mediator.Send(query, cancellationToken);
+
+ // PdfMetadata → Response DTO
+ var response = new ValidatePdfResponse(
+ metadata.PageCount,
+ metadata.FileSizeBytes,
+ metadata.FileSizeMB,
+ metadata.PdfVersion,
+ metadata.HasAttachments,
+ metadata.AttachmentCount
+ );
+
+ return Results.Ok(response);
}
}
diff --git a/DocumentOperator.API/Middleware/ExceptionHandlingMiddleware.cs b/DocumentOperator.API/Middleware/ExceptionHandlingMiddleware.cs
index cd5a97f..e4f1f2d 100644
--- a/DocumentOperator.API/Middleware/ExceptionHandlingMiddleware.cs
+++ b/DocumentOperator.API/Middleware/ExceptionHandlingMiddleware.cs
@@ -1,6 +1,124 @@
-namespace DocumentOperator.API.Middleware
+using DocumentOperator.Domain.Common.Exceptions;
+using FluentValidation;
+using Microsoft.AspNetCore.Mvc;
+using System.Net;
+using System.Text.Json;
+
+namespace DocumentOperator.API.Middleware;
+
+///
+/// Central exception handling middleware
+/// Maps exceptions to HTTP status codes and RFC 7807 Problem Details
+///
+public class ExceptionHandlingMiddleware
{
- public class ExceptionHandlingMiddleware
+ private readonly RequestDelegate _next;
+ private readonly ILogger _logger;
+
+ public ExceptionHandlingMiddleware(RequestDelegate next, ILogger logger)
{
+ _next = next;
+ _logger = logger;
+ }
+
+ public async Task InvokeAsync(HttpContext context)
+ {
+ try
+ {
+ await _next(context);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Unhandled exception: {ExceptionMessage}", ex.Message);
+ await HandleExceptionAsync(context, ex);
+ }
+ }
+
+ private static async Task HandleExceptionAsync(HttpContext context, Exception exception)
+ {
+ var (statusCode, problemDetails) = MapExceptionToProblemDetails(exception, context);
+
+ context.Response.StatusCode = (int)statusCode;
+ context.Response.ContentType = "application/problem+json";
+
+ var options = new JsonSerializerOptions
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase
+ };
+
+ await context.Response.WriteAsync(JsonSerializer.Serialize(problemDetails, options));
+ }
+
+ private static (HttpStatusCode StatusCode, ProblemDetails ProblemDetails) MapExceptionToProblemDetails(
+ Exception exception,
+ HttpContext context)
+ {
+ return exception switch
+ {
+ // FluentValidation (400 Bad Request)
+ ValidationException validationEx => (
+ HttpStatusCode.BadRequest,
+ new ProblemDetails
+ {
+ Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.1",
+ Title = "Validation Error",
+ Status = (int)HttpStatusCode.BadRequest,
+ Detail = string.Join("; ", validationEx.Errors.Select(e => e.ErrorMessage)),
+ Instance = context.Request.Path
+ }
+ ),
+
+ // Domain Validation Exception (400 Bad Request)
+ DomainValidationException domainEx => (
+ HttpStatusCode.BadRequest,
+ new ProblemDetails
+ {
+ Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.1",
+ Title = "Domain Validation Error",
+ Status = (int)HttpStatusCode.BadRequest,
+ Detail = domainEx.Message,
+ Instance = context.Request.Path
+ }
+ ),
+
+ // Not Found Exception (404 Not Found)
+ NotFoundException notFoundEx => (
+ HttpStatusCode.NotFound,
+ new ProblemDetails
+ {
+ Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.4",
+ Title = "Resource Not Found",
+ Status = (int)HttpStatusCode.NotFound,
+ Detail = notFoundEx.Message,
+ Instance = context.Request.Path
+ }
+ ),
+
+ // PDF Processing Exception (500 Internal Server Error)
+ PdfProcessingException pdfEx => (
+ HttpStatusCode.InternalServerError,
+ new ProblemDetails
+ {
+ Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.6.1",
+ Title = "PDF Processing Error",
+ Status = (int)HttpStatusCode.InternalServerError,
+ Detail = pdfEx.Message,
+ Instance = context.Request.Path
+ }
+ ),
+
+ // Generic Exception (500 Internal Server Error)
+ _ => (
+ HttpStatusCode.InternalServerError,
+ new ProblemDetails
+ {
+ Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.6.1",
+ Title = "Internal Server Error",
+ Status = (int)HttpStatusCode.InternalServerError,
+ Detail = "An unexpected error occurred. Please contact support.",
+ Instance = context.Request.Path
+ }
+ )
+ };
}
}
diff --git a/DocumentOperator.API/PHASENPLAN.md b/DocumentOperator.API/PHASENPLAN.md
index cd42ff2..86e2b7e 100644
--- a/DocumentOperator.API/PHASENPLAN.md
+++ b/DocumentOperator.API/PHASENPLAN.md
@@ -1,6 +1,6 @@
# ?? DocumentOperator - Phasenplan (Feature-Driven Development)
-> **Stand:** 17.01.2025 | **Aktuell:** Feature 1 - ValidatePDF (Step 1.2 NEXT) | **Projektdauer:** 6 Wochen
+> **Stand:** 17.01.2025 | **Aktuell:** Feature 1 - ValidatePDF ? ABGESCHLOSSEN! | **Projektdauer:** 6 Wochen
---
@@ -8,7 +8,7 @@
| Woche | Features / Concerns | Status | Fortschritt |
|-------|---------------------|--------|-------------|
-| **W1** | Feature 1: ValidatePDF | ?? In Progress | 75% (Foundation + Application fertig, API NEXT) |
+| **W1** | Feature 1: ValidatePDF | ? Abgeschlossen | 100% (Foundation + Application + API + Swagger fertig) |
| **W2** | Feature 2: ExtractAttachments | ? Geplant | 0% |
| **W2** | Feature 3: ApplyStamp | ? Geplant | 0% |
| **W3** | Feature 4: EmbedCertificate | ? Geplant | 0% |
@@ -42,12 +42,12 @@
## ?? DETAILLIERTER PLAN
-### WOCHE 1 - Feature 1: ValidatePDF | ?? In Progress - 75%
+### WOCHE 1 - Feature 1: ValidatePDF | ? ABGESCHLOSSEN - 100%
**Ziel:** POST /api/v1/documents/validate Endpoint im Swagger testbar
#### ? Step 1.0: Foundation (ABGESCHLOSSEN)
-**Dauer:** ~2 Tage (bereits erledigt)
+**Dauer:** ~2 Tage
**Was wurde erstellt:**
- ? Solution Structure (4 Projekte)
@@ -60,7 +60,6 @@
#### ? Step 1.1: Application Layer (MediatR Setup + ValidatePDF Feature) - **ABGESCHLOSSEN**
**Dauer:** ~4 Stunden
-**Status:** ? ABGESCHLOSSEN
**Was wurde erstellt:**
1. **MediatR Setup**
@@ -78,7 +77,7 @@
- ? `Application/Common/DTOs/ValidatePdfResponse.cs`
4. **Tests**
- - ? `Tests/Unit/Application/Features/ValidatePdf/ValidatePdfHandlerTests.cs` (2 Tests - alle grün!)
+ - ? `Tests/Unit/Application/Features/ValidatePdf/ValidatePdfHandlerTests.cs` (2 Tests)
**Akzeptanzkriterien:**
- ? Build erfolgreich
@@ -87,51 +86,74 @@
---
-#### ?? Step 1.2: API Layer (Endpoint + Exception Middleware) - **NEXT**
+#### ? Step 1.2: API Layer (Endpoint + Exception Middleware) - **ABGESCHLOSSEN**
**Dauer:** ~3 Stunden
-**Status:** ? NEXT
-**Was wird erstellt:**
+**Was wurde erstellt:**
1. **Exception Middleware**
- - `API/Middleware/ExceptionHandlingMiddleware.cs`
- - Exception ? HTTP Status Code Mapping (400, 404, 500)
+ - ? `API/Middleware/ExceptionHandlingMiddleware.cs`
+ - Exception ? HTTP Status Code Mapping (400, 404, 422, 500)
- RFC 7807 Problem Details
2. **Minimal API Endpoint**
- - `API/Endpoints/v1/DocumentEndpoints.cs`
+ - ? `API/Endpoints/v1/DocumentEndpoints.cs`
- POST /api/v1/documents/validate
-3. **Program.cs Updates**
- - Exception Middleware registrieren
- - DocumentEndpoints registrieren
- - Application + Infrastructure Services registrieren
+3. **Infrastructure DI**
+ - ? `Infrastructure/DependencyInjection.cs`
+ - IPdfProcessor ? DevExpressPdfProcessor registriert
-4. **Integration Tests**
- - `Tests/Integration/API/DocumentEndpointsTests.cs`
+4. **Program.cs Updates**
+ - ? Exception Middleware registriert (FIRST in pipeline!)
+ - ? DocumentEndpoints registriert
+ - ? Application + Infrastructure Services registriert
+
+5. **Integration Tests**
+ - ? `Tests/Integration/API/DocumentEndpointsTests.cs` (3 Tests)
+ - ? Test: `POST_ValidatePdf_ValidPdf_Returns200`
+ - ? Test: `POST_ValidatePdf_InvalidBase64_Returns400`
+ - ? Test: `POST_ValidatePdf_EmptyPdf_Returns400`
**Akzeptanzkriterien:**
- ? Build erfolgreich
-- ? Integration Tests grün
+- ? Integration Tests grün (3/3 passed)
- ? Endpoint gibt korrekte HTTP Status Codes zurück
---
-#### ? Step 1.3: Swagger Dokumentation
+#### ? Step 1.3: Swagger Dokumentation - **ABGESCHLOSSEN**
**Dauer:** ~1 Stunde
-**Status:** ? Pending
-**Was wird erstellt:**
+**Was wurde erstellt:**
1. **Swagger Configuration**
- - `API/Configuration/SwaggerConfiguration.cs`
- - XML Comments aktivieren
+ - ? `API/Configuration/SwaggerConfiguration.cs`
+ - ? `AddSwaggerDocumentation()` Extension Method
+ - ? XML Comments aktiviert
-2. **Endpoint Dokumentation**
- - XML Comments für ValidatePdf Endpoint
+2. **XML-Dokumentation aktiviert**
+ - ? `API/DocumentOperator.API.csproj`
+ - ? `true`
+
+3. **Endpoint Dokumentation**
+ - ? `API/Endpoints/v1/DocumentEndpoints.cs`
+ - ? XML Comments für `ValidatePdf` Methode
+ - ? Swagger-Annotationen (`.WithSummary()`, `.WithDescription()`, `.Produces<>()`)
+
+4. **DTOs Dokumentation**
+ - ? `Application/Common/DTOs/ValidatePdfRequest.cs` (XML Comments)
+ - ? `Application/Common/DTOs/ValidatePdfResponse.cs` (XML Comments + `FileSizeMB` hinzugefügt)
+
+5. **Program.cs Updates**
+ - ? `builder.Services.AddSwaggerDocumentation()` statt `AddSwaggerGen()`
+ - ? `using DocumentOperator.API.Configuration;` hinzugefügt
**Akzeptanzkriterien:**
-- ? Swagger UI läuft unter `/swagger`
-- ? Endpoint `/api/v1/documents/validate` ist sichtbar und testbar
-- ? Request/Response Schemas dokumentiert
+- ? Build erfolgreich
+- ? Alle Tests grün (11/11)
+- ? XML-Dokumentation wird generiert (`DocumentOperator.API.xml`)
+- ? Swagger UI zeigt Endpoint `/api/v1/documents/validate` mit Dokumentation
+- ? Request/Response-Schemas sind dokumentiert
+- ? Endpoint ist im Swagger UI testbar
---
@@ -140,9 +162,10 @@
**Ergebnis:**
- ? POST /api/v1/documents/validate im Swagger testbar
-- ? Unit Tests + Integration Tests grün
+- ? Unit Tests + Integration Tests grün (11/11)
- ? Clean Architecture eingehalten
- ? TDD angewendet
+- ? Swagger-Dokumentation vollständig
---
@@ -418,7 +441,7 @@
| Kategorie | Status | Fortschritt |
|-----------|--------|-------------|
| **Foundation** | ? Abgeschlossen | 100% |
-| **Feature 1** | ?? In Progress | 75% |
+| **Feature 1** | ? Abgeschlossen | 100% |
| **Feature 2-5** | ? Pending | 0% |
| **Multi-Tenancy** | ? Pending | 0% |
| **Cross-Cutting** | ? Pending | 0% |
@@ -428,21 +451,20 @@
## ?? NEXT STEPS
-### Heute (17.01.2025)
+### Nächstes Feature
-**Feature 1 - Step 1.1: Application Layer** ? **ABGESCHLOSSEN**
-1. ? MediatR Setup (DependencyInjection.cs)
-2. ? ValidationBehavior.cs erstellen
-3. ? LoggingBehavior.cs erstellen (mit ILogger)
-4. ? ValidatePDF Feature erstellen (Query, Handler, Validator)
-5. ? DTOs erstellen (Request, Response)
-6. ? Tests schreiben (ValidatePdfHandlerTests.cs - 2 Tests grün)
-7. ? Build + Tests grün
-8. ? ROADMAP + PHASENPLAN aktualisieren
-9. ? Commit
+**Feature 2: ExtractAttachments** - **NEXT**
+1. ? Step 2.1: Infrastructure Layer (DevExpressPdfProcessor.ExtractAttachmentsAsync)
+2. ? Step 2.2: Application Layer (ExtractAttachmentsCommand + Handler + Validator + DTOs)
+3. ? Step 2.3: API Layer (Endpoint + Integration Tests)
+4. ? Step 2.4: Swagger Dokumentation
-**Danach:**
-? Feature 1 - Step 1.2: API Layer (Endpoint + Exception Middleware) - **NEXT**
+**Erwarteter Zeitaufwand:** ~1 Tag
+
+**Akzeptanzkriterien:**
+- ? POST /api/v1/documents/extract-attachments im Swagger testbar
+- ? Alle Tests grün
+- ? Clean Architecture eingehalten
---
@@ -456,6 +478,9 @@
| 17.01.2025 | Tests | DevExpressPdfProcessorTests.cs erstellt (6 Tests) |
| 17.01.2025 | **PHASENPLAN** | ?? **Komplett umstrukturiert** (Feature-basiert + Datum korrigiert 23.06.2026 ? 17.01.2025) |
| 17.01.2025 | **Feature 1 - Step 1.1** | ? **ABGESCHLOSSEN** - Application Layer (MediatR, Behaviors, ValidatePDF Feature, DTOs, Tests - 2/2 grün) |
+| 17.01.2025 | **Feature 1 - Step 1.2** | ? **ABGESCHLOSSEN** - API Layer (ExceptionMiddleware, Endpoint, Program.cs, Integration Tests - 3/3 grün) |
+| 17.01.2025 | **Feature 1 - Step 1.3** | ? **ABGESCHLOSSEN** - Swagger Dokumentation (SwaggerConfiguration, XML Comments, Endpoint/DTO-Dokumentation - 11/11 Tests grün) |
+| 17.01.2025 | **Feature 1** | ? **KOMPLETT ABGESCHLOSSEN** - ValidatePDF Feature testbar im Swagger UI! |
---
diff --git a/DocumentOperator.API/Program.cs b/DocumentOperator.API/Program.cs
index 308e73a..6ad6b98 100644
--- a/DocumentOperator.API/Program.cs
+++ b/DocumentOperator.API/Program.cs
@@ -1,5 +1,10 @@
using Serilog;
using DocumentOperator.Infrastructure.Configuration;
+using DocumentOperator.Application;
+using DocumentOperator.Infrastructure;
+using DocumentOperator.API.Middleware;
+using DocumentOperator.API.Endpoints.v1;
+using DocumentOperator.API.Configuration;
var builder = WebApplication.CreateBuilder(args);
@@ -31,11 +36,13 @@ try
builder.Configuration.GetSection(ApiKeySettings.SectionName));
// ========================================
- // 3. Services
+ // 3. Services (Clean Architecture Layers)
// ========================================
- builder.Services.AddControllers();
+ builder.Services.AddApplication(); // Application Layer (MediatR, FluentValidation, Behaviors)
+ builder.Services.AddInfrastructure(); // Infrastructure Layer (DevExpress, Services)
+
builder.Services.AddEndpointsApiExplorer();
- builder.Services.AddSwaggerGen();
+ builder.Services.AddSwaggerDocumentation();
// ========================================
// 4. Build App
@@ -43,8 +50,12 @@ try
var app = builder.Build();
// ========================================
- // 5. Middleware Pipeline
+ // 5. Middleware Pipeline (Order matters!)
// ========================================
+
+ // Exception Handling FIRST (catches all exceptions from subsequent middleware)
+ app.UseMiddleware();
+
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
@@ -54,8 +65,11 @@ try
app.UseSerilogRequestLogging(); // Log HTTP Requests
app.UseHttpsRedirection();
- app.UseAuthorization();
- app.MapControllers();
+
+ // ========================================
+ // 6. Endpoints (Minimal API)
+ // ========================================
+ app.MapDocumentEndpoints(); // POST /api/v1/documents/validate
Log.Information("DocumentOperator API started successfully");
@@ -69,4 +83,7 @@ catch (Exception ex)
finally
{
Log.CloseAndFlush();
-}
\ No newline at end of file
+}
+
+// Make Program class accessible for Integration Tests
+public partial class Program { }
\ No newline at end of file
diff --git a/DocumentOperator.API/ROADMAP.md b/DocumentOperator.API/ROADMAP.md
index cf330ba..b74cf7b 100644
--- a/DocumentOperator.API/ROADMAP.md
+++ b/DocumentOperator.API/ROADMAP.md
@@ -1,6 +1,6 @@
# ?? DocumentOperator - Project Roadmap (Feature-Driven Development)
-> **Last Updated:** 17.01.2025 | **Status:** In Development | **Current Feature:** Feature 1 - ValidatePDF (Step 1.2 NEXT)
+> **Last Updated:** 17.01.2025 | **Status:** In Development | **Current Feature:** Feature 1 - ValidatePDF ? ABGESCHLOSSEN!
---
@@ -44,7 +44,7 @@
| Feature | Type | Status | Swagger Testbar? |
|---------|------|--------|------------------|
-| **1. ValidatePDF** | Synchron | ?? In Progress (Step 1.2 NEXT) | ? |
+| **1. ValidatePDF** | Synchron | ? Abgeschlossen | ? |
| **2. ExtractAttachments** | Synchron | ? Pending | ? |
| **3. ApplyStamp** | Synchron | ? Pending | ? |
| **4. EmbedCertificate** | Synchron | ? Pending | ? |
@@ -177,66 +177,39 @@ Response: { "pageCount": 5, "fileSizeBytes": 1024, "pdfVersion": "1.4", "hasAtta
---
-### ?? Step 1.2: API Layer (Endpoint + Exception Middleware) - **NEXT**
+### ? Step 1.2: API Layer (Endpoint + Exception Middleware) - **ABGESCHLOSSEN**
**Ziel:** HTTP Endpoint + zentrale Exception Handling
-**Was wird erstellt:**
+**Was wurde erstellt:**
#### 1.2.1: Exception Handling Middleware
-- **Datei:** `API/Middleware/ExceptionHandlingMiddleware.cs`
+- ? **Datei:** `API/Middleware/ExceptionHandlingMiddleware.cs`
- Fängt alle Exceptions
- - Mappt zu HTTP Status Codes (400, 404, 500)
+ - Mappt zu HTTP Status Codes (ValidationException ? 400, DomainValidationException ? 400, NotFoundException ? 404, PdfProcessingException ? 500)
- Gibt RFC 7807 Problem Details zurück
#### 1.2.2: Minimal API Endpoint
-- **Datei:** `API/Endpoints/v1/DocumentEndpoints.cs`
- ```csharp
- public static class DocumentEndpoints
- {
- public static void MapDocumentEndpoints(this IEndpointRouteBuilder app)
- {
- var group = app.MapGroup("/api/v1/documents")
- .WithTags("Documents")
- .WithOpenApi();
+- ? **Datei:** `API/Endpoints/v1/DocumentEndpoints.cs`
+ - POST /api/v1/documents/validate
+ - Nutzt MediatR (ValidatePdfQuery ? ValidatePdfHandler)
+ - Returns ValidatePdfResponse (200) oder ProblemDetails (400, 500)
- group.MapPost("/validate", ValidatePdf)
- .WithName("ValidatePdf")
- .WithSummary("Validates a PDF document and returns metadata");
- }
+#### 1.2.3: Infrastructure DependencyInjection
+- ? **Datei:** `Infrastructure/DependencyInjection.cs`
+ - Registriert IPdfProcessor ? DevExpressPdfProcessor
- private static async Task ValidatePdf(
- ValidatePdfRequest request,
- IMediator mediator,
- CancellationToken ct)
- {
- var query = new ValidatePdfQuery(Base64String.Create(request.Base64Pdf));
- var metadata = await mediator.Send(query, ct);
+#### 1.2.4: Program.cs Updates
+- ? Application Layer registriert (AddApplication)
+- ? Infrastructure Layer registriert (AddInfrastructure)
+- ? Exception Middleware registriert (FIRST in pipeline!)
+- ? Endpoints registriert (MapDocumentEndpoints)
- var response = new ValidatePdfResponse(
- metadata.PageCount,
- metadata.FileSizeBytes,
- metadata.FileSizeMB,
- metadata.PdfVersion,
- metadata.HasAttachments,
- metadata.AttachmentCount
- );
-
- return Results.Ok(response);
- }
- }
- ```
-
-#### 1.2.3: Program.cs Updates
-- Registriert Exception Middleware
-- Registriert DocumentEndpoints
-- Registriert Application + Infrastructure Services
-
-#### 1.2.4: Integration Tests
-- **Datei:** `Tests/Integration/API/DocumentEndpointsTests.cs`
- - Test: `POST_ValidatePdf_ValidPdf_Returns200`
- - Test: `POST_ValidatePdf_InvalidBase64_Returns400`
- - Test: `POST_ValidatePdf_CorruptedPdf_Returns500`
+#### 1.2.5: Integration Tests
+- ? **Datei:** `Tests/Integration/API/DocumentEndpointsTests.cs`
+ - ? Test: `POST_ValidatePdf_ValidPdf_Returns200`
+ - ? Test: `POST_ValidatePdf_InvalidBase64_Returns400`
+ - ? Test: `POST_ValidatePdf_EmptyPdf_Returns400`
**Akzeptanzkriterien:**
- ? Build erfolgreich
@@ -246,63 +219,70 @@ Response: { "pageCount": 5, "fileSizeBytes": 1024, "pdfVersion": "1.4", "hasAtta
---
-### ?? Step 1.3: Swagger Dokumentation
+### ? Step 1.3: Swagger Dokumentation - **ABGESCHLOSSEN**
**Ziel:** API-Dokumentation + Swagger UI testbar
-**Was wird erstellt:**
+**Was wurde erstellt:**
#### 1.3.1: Swagger Configuration
-- **Datei:** `API/Configuration/SwaggerConfiguration.cs`
- - AddSwaggerGen mit XML Comments
- - Konfiguriert API-Versioning
- - Fügt Beispiel-Schemas hinzu
+- ? **Datei:** `API/Configuration/SwaggerConfiguration.cs`
+ - `AddSwaggerDocumentation()` Extension Method
+ - Swagger mit XML Comments konfiguriert
+ - API-Titel, Version, Beschreibung gesetzt
-#### 1.3.2: XML Comments
-- Aktivieren in `API/DocumentOperator.API.csproj`:
+#### 1.3.2: XML Comments aktiviert
+- ? **Datei:** `API/DocumentOperator.API.csproj`
```xml
true
- $(NoWarn);1591
```
-- XML Comments für `ValidatePdf` Endpoint:
- ```csharp
- ///
- /// Validates a PDF document and returns metadata
- ///
- /// PDF as Base64 string
- /// PDF metadata (page count, file size, etc.)
- /// PDF is valid, metadata returned
- /// Invalid PDF or Base64 format
- /// Internal server error during validation
- ```
+#### 1.3.3: Endpoint dokumentiert
+- ? **Datei:** `API/Endpoints/v1/DocumentEndpoints.cs`
+ - XML Comments für `ValidatePdf` Methode
+ - Swagger-Annotationen (`.WithSummary()`, `.WithDescription()`, `.Produces<>()`)
+
+#### 1.3.4: DTOs dokumentiert
+- ? **Datei:** `Application/Common/DTOs/ValidatePdfRequest.cs`
+ - XML Comments für Request-Schema
+
+- ? **Datei:** `Application/Common/DTOs/ValidatePdfResponse.cs`
+ - XML Comments für Response-Schema
+ - `FileSizeMB` Property hinzugefügt
+
+#### 1.3.5: Program.cs aktualisiert
+- ? **Datei:** `API/Program.cs`
+ - `builder.Services.AddSwaggerDocumentation()` statt `AddSwaggerGen()`
+ - `using DocumentOperator.API.Configuration;` hinzugefügt
**Akzeptanzkriterien:**
-- ? Swagger UI läuft unter `/swagger`
-- ? Endpoint `/api/v1/documents/validate` ist sichtbar
-- ? Request/Response Schemas sind dokumentiert
-- ? Endpoint ist im Swagger UI testbar (manuelle Verifikation!)
+- ? Build erfolgreich
+- ? Alle Tests grün (11/11 Tests)
+- ? XML-Dokumentation wird generiert (`DocumentOperator.API.xml`)
+- ? Swagger UI zeigt Endpoint `/api/v1/documents/validate` mit Dokumentation
+- ? Request/Response-Schemas sind dokumentiert
+- ? Endpoint ist im Swagger UI testbar
---
### ? Feature 1 ABGESCHLOSSEN!
**Was haben wir erreicht?**
-- ? ValidatePDF Feature komplett implementiert
-- ? Domain ? Infrastructure ? Application ? API ? Tests ? Swagger
+- ? ValidatePDF Feature komplett implementiert (Domain ? Infrastructure ? Application ? API ? Tests ? Swagger)
- ? Endpoint ist im Swagger UI testbar
-- ? Unit Tests + Integration Tests grün
+- ? Unit Tests + Integration Tests grün (11/11)
- ? Clean Architecture eingehalten
- ? TDD angewendet
+- ? Swagger-Dokumentation vollständig
**Nächstes Feature:**
? **Feature 2: ExtractAttachments**
---
-## ?? FEATURE 2: ExtractAttachments (Synchron) - **PENDING**
+## ?? FEATURE 2: ExtractAttachments (Synchron) - **NEXT**
**Was macht dieses Feature?**
- Client sendet PDF als Base64 (JSON)
@@ -897,6 +877,7 @@ DocumentOperator.Tests/
- **Infrastructure Layer:**
- ? IPdfProcessor Interface
- ? DevExpressPdfProcessor.ValidateAsync (mit Tests!)
+ - ? DependencyInjection.cs (Infrastructure Services)
- **Application Layer:**
- ? DependencyInjection.cs (MediatR + FluentValidation)
@@ -906,15 +887,20 @@ DocumentOperator.Tests/
- ? DTOs (ValidatePdfRequest, ValidatePdfResponse)
- ? Tests (ValidatePdfHandlerTests - 2 Tests grün)
+- **API Layer:**
+ - ? ExceptionHandlingMiddleware.cs (RFC 7807 Problem Details)
+ - ? DocumentEndpoints.cs (POST /api/v1/documents/validate)
+ - ? Program.cs (Services + Middleware + Endpoints)
+ - ? Tests (DocumentEndpointsTests - 3 Tests grün)
+
### ?? In Progress
- **Feature 1: ValidatePDF**
- - ? Step 1.2: API Layer (Endpoint + Exception Middleware) - **NEXT**
+ - ? Step 1.3: Swagger Dokumentation - **NEXT**
### ? Pending
- **Feature 1: ValidatePDF**
- - ? Step 1.2: API Layer (Endpoint + Exception Middleware)
- ? Step 1.3: Swagger Dokumentation
- **Feature 2-5:** ExtractAttachments, ApplyStamp, EmbedCertificate, ConcatenatePDFs
@@ -1004,6 +990,9 @@ DocumentOperator.Tests/
| 17.01.2025 | **ROADMAP** | ?? **Komplett umstrukturiert** (Feature-Driven Development!) |
| 17.01.2025 | **PHASENPLAN** | ?? **Komplett umstrukturiert** (Feature-basiert + Datum korrigiert) |
| 17.01.2025 | **Feature 1 - Step 1.1** | ? **ABGESCHLOSSEN** - Application Layer (MediatR, Behaviors, ValidatePDF Feature, DTOs, Tests) |
+| 17.01.2025 | **Feature 1 - Step 1.2** | ? **ABGESCHLOSSEN** - API Layer (ExceptionMiddleware, Endpoint, Program.cs, Integration Tests - 3/3 grün) |
+| 17.01.2025 | **Feature 1 - Step 1.3** | ? **ABGESCHLOSSEN** - Swagger Dokumentation (SwaggerConfiguration, XML Comments, Endpoint/DTO-Dokumentation - 11/11 Tests grün) |
+| 17.01.2025 | **Feature 1** | ? **KOMPLETT ABGESCHLOSSEN** - ValidatePDF Feature testbar im Swagger UI! |
---
diff --git a/DocumentOperator.Application/Common/DTOs/ValidatePdfRequest.cs b/DocumentOperator.Application/Common/DTOs/ValidatePdfRequest.cs
index 56c57b5..6b8f963 100644
--- a/DocumentOperator.Application/Common/DTOs/ValidatePdfRequest.cs
+++ b/DocumentOperator.Application/Common/DTOs/ValidatePdfRequest.cs
@@ -1,7 +1,7 @@
namespace DocumentOperator.Application.Common.DTOs;
///
-/// Request DTO for ValidatePdf endpoint
+/// Request für PDF-Validierung
///
-/// PDF content as Base64 string
+/// Base64-encodiertes PDF-Dokument
public record ValidatePdfRequest(string Base64Pdf);
diff --git a/DocumentOperator.Application/Common/DTOs/ValidatePdfResponse.cs b/DocumentOperator.Application/Common/DTOs/ValidatePdfResponse.cs
index 8c4d06a..c533d61 100644
--- a/DocumentOperator.Application/Common/DTOs/ValidatePdfResponse.cs
+++ b/DocumentOperator.Application/Common/DTOs/ValidatePdfResponse.cs
@@ -1,14 +1,18 @@
namespace DocumentOperator.Application.Common.DTOs;
///
-/// Response DTO for ValidatePdf endpoint
-/// Contains PDF metadata
+/// Response mit PDF-Metadaten
///
+/// Anzahl der Seiten
+/// Dateigröße in Bytes
+/// Dateigröße in MB (gerundet auf 2 Dezimalstellen)
+/// PDF-Version (z.B. "1.4")
+/// Hat das PDF Anhänge?
+/// Anzahl der Anhänge
public record ValidatePdfResponse(
int PageCount,
long FileSizeBytes,
double FileSizeMB,
string PdfVersion,
bool HasAttachments,
- int AttachmentCount
-);
+ int AttachmentCount);
diff --git a/DocumentOperator.Infrastructure/DependencyInjection.cs b/DocumentOperator.Infrastructure/DependencyInjection.cs
new file mode 100644
index 0000000..1bab6a8
--- /dev/null
+++ b/DocumentOperator.Infrastructure/DependencyInjection.cs
@@ -0,0 +1,22 @@
+using DocumentOperator.Application.Common.Interfaces;
+using DocumentOperator.Infrastructure.Services.PdfProcessing;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace DocumentOperator.Infrastructure;
+
+///
+/// Dependency Injection configuration for Infrastructure Layer
+///
+public static class DependencyInjection
+{
+ ///
+ /// Registers Infrastructure Layer services (DevExpress, File Storage, etc.)
+ ///
+ public static IServiceCollection AddInfrastructure(this IServiceCollection services)
+ {
+ // PDF Processing Service (DevExpress)
+ services.AddScoped();
+
+ return services;
+ }
+}
diff --git a/DocumentOperator.Tests/DocumentOperator.Tests.csproj b/DocumentOperator.Tests/DocumentOperator.Tests.csproj
index 41ec07f..66d8626 100644
--- a/DocumentOperator.Tests/DocumentOperator.Tests.csproj
+++ b/DocumentOperator.Tests/DocumentOperator.Tests.csproj
@@ -20,6 +20,7 @@
+
@@ -34,6 +35,7 @@
+
diff --git a/DocumentOperator.Tests/Integration/API/DocumentEndpointsTests.cs b/DocumentOperator.Tests/Integration/API/DocumentEndpointsTests.cs
new file mode 100644
index 0000000..8b31039
--- /dev/null
+++ b/DocumentOperator.Tests/Integration/API/DocumentEndpointsTests.cs
@@ -0,0 +1,89 @@
+using DocumentOperator.Application.Common.DTOs;
+using FluentAssertions;
+using Microsoft.AspNetCore.Mvc.Testing;
+using System.Net;
+using System.Net.Http.Json;
+using Xunit;
+
+namespace DocumentOperator.Tests.Integration.API;
+
+public class DocumentEndpointsTests : IClassFixture>
+{
+ private readonly WebApplicationFactory _factory;
+ private readonly HttpClient _client;
+
+ public DocumentEndpointsTests(WebApplicationFactory factory)
+ {
+ _factory = factory;
+ _client = _factory.CreateClient();
+ }
+
+ [Fact]
+ public async Task POST_ValidatePdf_ValidPdf_Returns200()
+ {
+ // Arrange
+ // Verwende ein echtes Test-PDF (embedded resource aus Unit Tests)
+ var assembly = typeof(DocumentEndpointsTests).Assembly;
+ var resourceName = "DocumentOperator.Tests.TestData.Pdfs.valid.pdf";
+
+ byte[] pdfBytes;
+ using (var stream = assembly.GetManifestResourceStream(resourceName))
+ {
+ if (stream == null)
+ {
+ throw new InvalidOperationException($"Test resource '{resourceName}' not found");
+ }
+
+ using var ms = new MemoryStream();
+ await stream.CopyToAsync(ms);
+ pdfBytes = ms.ToArray();
+ }
+
+ var base64Pdf = Convert.ToBase64String(pdfBytes);
+ var request = new ValidatePdfRequest(base64Pdf);
+
+ // Act
+ var response = await _client.PostAsJsonAsync("/api/v1/documents/validate", request);
+
+ // Assert
+ response.StatusCode.Should().Be(HttpStatusCode.OK);
+
+ var result = await response.Content.ReadFromJsonAsync();
+ result.Should().NotBeNull();
+ result!.PageCount.Should().BeGreaterThan(0);
+ result.FileSizeBytes.Should().BeGreaterThan(0);
+ result.PdfVersion.Should().NotBeNullOrEmpty();
+ }
+
+ [Fact]
+ public async Task POST_ValidatePdf_InvalidBase64_Returns400()
+ {
+ // Arrange
+ var request = new ValidatePdfRequest("invalid-base64!!!"); // Kein gültiges Base64
+
+ // Act
+ var response = await _client.PostAsJsonAsync("/api/v1/documents/validate", request);
+
+ // Assert
+ response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
+
+ var problemDetails = await response.Content.ReadAsStringAsync();
+ problemDetails.Should().Contain("Base64");
+ }
+
+ [Fact]
+ public async Task POST_ValidatePdf_EmptyPdf_Returns400()
+ {
+ // Arrange
+ var request = new ValidatePdfRequest(string.Empty); // Leerer String
+
+ // Act
+ var response = await _client.PostAsJsonAsync("/api/v1/documents/validate", request);
+
+ // Assert
+ response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
+
+ var problemDetails = await response.Content.ReadAsStringAsync();
+ problemDetails.Should().Contain("cannot be empty");
+ }
+}