feat: Add DocumentService.Client base infrastructure
This commit is contained in:
332
DocumentService.Client/README.md
Normal file
332
DocumentService.Client/README.md
Normal file
@@ -0,0 +1,332 @@
|
||||
# DocumentService.Client
|
||||
|
||||
.NET client library for DocumentService API - supports .NET Framework 4.6.2, 4.8, and .NET 8.0.
|
||||
|
||||
## Features
|
||||
|
||||
- **Multi-target support**: .NET Framework 4.6.2, 4.8, and .NET 8.0
|
||||
- **HttpClientFactory integration**: Proper lifecycle management and connection pooling
|
||||
- **Separate clients per controller**: `IPdfValidationClient`, `IPdfAttachmentClient`, `IPdfOperationsClient`, `ISwissQrCodeClient`
|
||||
- **Dual input support**: Multipart (Stream) and Base64 (byte[]) for all endpoints
|
||||
- **Strongly-typed models**: Shared request/response DTOs with XML documentation
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package DocumentService.Client
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### ASP.NET Core / .NET 8.0
|
||||
|
||||
```csharp
|
||||
using DocumentService.Client.Extensions;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Register all DocumentService clients
|
||||
builder.Services.AddDocumentServiceClients(options =>
|
||||
{
|
||||
options.BaseUrl = "https://documentservice.example.com";
|
||||
options.Timeout = TimeSpan.FromMinutes(10);
|
||||
options.MaxRetries = 3;
|
||||
options.ThrowOnError = true;
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
```
|
||||
|
||||
### .NET Framework 4.6.2 / 4.8
|
||||
|
||||
```csharp
|
||||
using DocumentService.Client.Extensions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
var services = new ServiceCollection();
|
||||
|
||||
services.AddDocumentServiceClients(options =>
|
||||
{
|
||||
options.BaseUrl = "https://documentservice.example.com";
|
||||
options.Timeout = TimeSpan.FromMinutes(10);
|
||||
});
|
||||
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### 1. PDF Validation
|
||||
|
||||
```csharp
|
||||
using DocumentService.Client.Interfaces;
|
||||
|
||||
public class PdfService
|
||||
{
|
||||
private readonly IPdfValidationClient _validationClient;
|
||||
|
||||
public PdfService(IPdfValidationClient validationClient)
|
||||
{
|
||||
_validationClient = validationClient;
|
||||
}
|
||||
|
||||
public async Task ValidatePdfAsync(Stream pdfStream)
|
||||
{
|
||||
// Option 1: From Stream (multipart)
|
||||
var result = await _validationClient.ValidatePdfAsync(pdfStream);
|
||||
|
||||
Console.WriteLine($"Pages: {result.PageCount}");
|
||||
Console.WriteLine($"Version: {result.PdfVersion}");
|
||||
Console.WriteLine($"Encrypted: {result.IsEncrypted}");
|
||||
}
|
||||
|
||||
public async Task ValidatePdfFromBytesAsync(byte[] pdfBytes)
|
||||
{
|
||||
// Option 2: From byte array (Base64 JSON)
|
||||
var result = await _validationClient.ValidatePdfAsync(pdfBytes);
|
||||
|
||||
Console.WriteLine($"File Size: {result.FileSizeBytes} bytes");
|
||||
}
|
||||
|
||||
public async Task ValidatePdfAAsync(string filePath)
|
||||
{
|
||||
using var stream = File.OpenRead(filePath);
|
||||
var result = await _validationClient.ValidatePdfAAsync(stream);
|
||||
|
||||
Console.WriteLine($"Valid PDF/A: {result.IsValid}");
|
||||
Console.WriteLine($"PDF/A Version: {result.PdfAVersion}");
|
||||
|
||||
if (result.Errors.Any())
|
||||
{
|
||||
Console.WriteLine("Errors:");
|
||||
foreach (var error in result.Errors)
|
||||
{
|
||||
Console.WriteLine($" - {error}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. PDF Attachments
|
||||
|
||||
```csharp
|
||||
using DocumentService.Client.Interfaces;
|
||||
using DocumentService.Client.Extensions; // For ToBase64StringAsync, ToBytesAsync
|
||||
|
||||
public class AttachmentService
|
||||
{
|
||||
private readonly IPdfAttachmentClient _attachmentClient;
|
||||
|
||||
public AttachmentService(IPdfAttachmentClient attachmentClient)
|
||||
{
|
||||
_attachmentClient = attachmentClient;
|
||||
}
|
||||
|
||||
public async Task CheckAttachmentsAsync(byte[] pdfBytes)
|
||||
{
|
||||
var result = await _attachmentClient.CheckAttachmentsAsync(pdfBytes);
|
||||
|
||||
Console.WriteLine($"Has Attachments: {result.HasAttachments}");
|
||||
Console.WriteLine($"Attachment Count: {result.AttachmentCount}");
|
||||
|
||||
foreach (var attachment in result.Attachments)
|
||||
{
|
||||
Console.WriteLine($" - {attachment.FileName} ({attachment.Size} bytes)");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ExtractAttachmentsAsync(Stream pdfStream, string outputPath)
|
||||
{
|
||||
// Returns ZIP file as Stream (memory efficient!)
|
||||
using var zipStream = await _attachmentClient.ExtractAttachmentsAsync(pdfStream);
|
||||
|
||||
// Option 1: Save directly to file
|
||||
using var fileStream = File.Create(outputPath);
|
||||
await zipStream.CopyToAsync(fileStream);
|
||||
|
||||
Console.WriteLine($"Attachments extracted to: {outputPath}");
|
||||
}
|
||||
|
||||
public async Task ExtractAttachmentsToBase64Async(byte[] pdfBytes)
|
||||
{
|
||||
// Returns ZIP as Stream
|
||||
using var zipStream = await _attachmentClient.ExtractAttachmentsAsync(pdfBytes);
|
||||
|
||||
// Option 2: Convert to Base64 using extension method
|
||||
string base64Zip = await zipStream.ToBase64StringAsync();
|
||||
|
||||
Console.WriteLine($"ZIP as Base64: {base64Zip.Substring(0, 50)}...");
|
||||
}
|
||||
|
||||
public async Task ExtractAttachmentsToBytesAsync(Stream pdfStream)
|
||||
{
|
||||
// Returns ZIP as Stream
|
||||
using var zipStream = await _attachmentClient.ExtractAttachmentsAsync(pdfStream);
|
||||
|
||||
// Option 3: Convert to byte array using extension method
|
||||
byte[] zipBytes = await zipStream.ToBytesAsync();
|
||||
|
||||
Console.WriteLine($"ZIP size: {zipBytes.Length} bytes");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. PDF Operations (Merge, Annotate, Stamp)
|
||||
|
||||
```csharp
|
||||
using DocumentService.Client.Interfaces;
|
||||
using DocumentService.Client.Models.Requests;
|
||||
using DocumentService.Client.Models.ValueObjects;
|
||||
using DocumentService.Client.Extensions; // For Stream extensions
|
||||
|
||||
public class OperationsService
|
||||
{
|
||||
private readonly IPdfOperationsClient _operationsClient;
|
||||
|
||||
public OperationsService(IPdfOperationsClient operationsClient)
|
||||
{
|
||||
_operationsClient = operationsClient;
|
||||
}
|
||||
|
||||
// MERGE
|
||||
public async Task<Stream> MergePdfsAsync(List<string> pdfPaths)
|
||||
{
|
||||
var streams = pdfPaths.Select(File.OpenRead).ToList();
|
||||
|
||||
// Returns merged PDF as Stream
|
||||
var mergedStream = await _operationsClient.MergeAsync(
|
||||
streams,
|
||||
pageRanges: new List<string?> { "1-2", null, "3,5" } // Optional
|
||||
);
|
||||
|
||||
foreach (var stream in streams) stream.Dispose();
|
||||
|
||||
return mergedStream; // Caller responsible for disposing
|
||||
}
|
||||
|
||||
public async Task MergePdfsToFileAsync(List<string> pdfPaths, string outputPath)
|
||||
{
|
||||
using var mergedStream = await MergePdfsAsync(pdfPaths);
|
||||
|
||||
// Save to file
|
||||
using var fileStream = File.Create(outputPath);
|
||||
await mergedStream.CopyToAsync(fileStream);
|
||||
}
|
||||
|
||||
// ANNOTATE
|
||||
public async Task<byte[]> AddHighlightAsync(byte[] pdfBytes)
|
||||
{
|
||||
var request = new AddAnnotationBase64Request
|
||||
{
|
||||
Base64Pdf = Convert.ToBase64String(pdfBytes),
|
||||
AnnotationType = AnnotationType.TextMarkup,
|
||||
PageNumber = 1,
|
||||
X1 = 100,
|
||||
Y1 = 200,
|
||||
Width = 150,
|
||||
Height = 20,
|
||||
Color = "FFFF00", // Yellow
|
||||
TextMarkupStyle = TextMarkupStyle.Highlight,
|
||||
Origin = AnnotationOrigin.TopLeft
|
||||
};
|
||||
|
||||
// Returns Stream - convert to bytes
|
||||
using var annotatedStream = await _operationsClient.AnnotateAsync(pdfBytes, request);
|
||||
return await annotatedStream.ToBytesAsync();
|
||||
}
|
||||
|
||||
// STAMP
|
||||
public async Task<Stream> AddStampAsync(Stream pdfStream)
|
||||
{
|
||||
var request = new AddStampBase64Request
|
||||
{
|
||||
Base64Pdf = string.Empty, // Will be filled by client
|
||||
StampType = StampType.Text,
|
||||
X = 300,
|
||||
Y = 50,
|
||||
Text = "CONFIDENTIAL",
|
||||
FontName = "Arial",
|
||||
FontSize = 24,
|
||||
Color = "FF0000", // Red
|
||||
Opacity = 0.5,
|
||||
Rotation = 45,
|
||||
Placement = StampPlacement.Foreground,
|
||||
Origin = AnnotationOrigin.BottomLeft
|
||||
};
|
||||
|
||||
// Returns stamped PDF as Stream
|
||||
return await _operationsClient.StampAsync(pdfStream, request);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Swiss QR Code Extraction
|
||||
|
||||
```csharp
|
||||
using DocumentService.Client.Interfaces;
|
||||
|
||||
public class QrCodeService
|
||||
{
|
||||
private readonly ISwissQrCodeClient _qrCodeClient;
|
||||
|
||||
public QrCodeService(ISwissQrCodeClient qrCodeClient)
|
||||
{
|
||||
_qrCodeClient = qrCodeClient;
|
||||
}
|
||||
|
||||
public async Task ExtractQrCodeAsync(byte[] pdfBytes)
|
||||
{
|
||||
// Get parsed Bill object
|
||||
var result = await _qrCodeClient.ExtractSwissQrCodeAsync(pdfBytes, raw: false);
|
||||
|
||||
Console.WriteLine($"Bill: {result.Bill}");
|
||||
}
|
||||
|
||||
public async Task ExtractRawQrCodeAsync(Stream pdfStream)
|
||||
{
|
||||
// Get raw QR text lines
|
||||
var result = await _qrCodeClient.ExtractSwissQrCodeAsync(pdfStream, raw: true);
|
||||
|
||||
Console.WriteLine("Raw QR Lines:");
|
||||
foreach (var line in result.RawLines)
|
||||
{
|
||||
Console.WriteLine($" {line}");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Endpoints
|
||||
|
||||
| **Client** | **Method** | **API Endpoint** | **Description** |
|
||||
|---|---|---|---|
|
||||
| **IPdfValidationClient** | `ValidatePdfAsync()` | `POST /api/pdf/validation/validate` | Validates PDF and returns metadata |
|
||||
| **IPdfValidationClient** | `ValidatePdfAAsync()` | `POST /api/pdf/validation/validate-pdfa` | Validates PDF/A conformance |
|
||||
| **IPdfAttachmentClient** | `CheckAttachmentsAsync()` | `POST /api/pdf/attachments/check` | Checks for embedded attachments |
|
||||
| **IPdfAttachmentClient** | `ExtractAttachmentsAsync()` | `POST /api/pdf/attachments/extract` | Extracts attachments as ZIP |
|
||||
| **IPdfOperationsClient** | `MergeAsync()` | `POST /api/pdf/operations/merge` | Merges multiple PDFs |
|
||||
| **IPdfOperationsClient** | `AnnotateAsync()` | `POST /api/pdf/operations/annotate` | Adds annotations (highlight, notes, etc.) |
|
||||
| **IPdfOperationsClient** | `StampAsync()` | `POST /api/pdf/operations/stamp` | Adds text/image stamps |
|
||||
| **ISwissQrCodeClient** | `ExtractSwissQrCodeAsync()` | `POST /api/pdf/qr-code/extract-swiss` | Extracts Swiss QR Code data |
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
try
|
||||
{
|
||||
var result = await validationClient.ValidatePdfAsync(pdfBytes);
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
Console.WriteLine($"HTTP error: {ex.Message}");
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
Console.WriteLine($"API returned null: {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Copyright © 2026 Digital Data GmbH. All rights reserved.
|
||||
Reference in New Issue
Block a user