using DocumentService.Client.Interfaces; using DocumentService.Client.Models.Requests; using Microsoft.Extensions.Logging; using System.IO.Compression; using System.Net.Http; namespace DocumentService.Client.Clients; /// /// Implementation of PDF attachment client. /// public class PdfAttachmentClient(HttpClient httpClient, ILogger logger) : BaseDocumentClient(httpClient, logger), IPdfAttachmentClient { /// public async Task CheckAttachmentsAsync(Stream pdfStream, CancellationToken cancellationToken = default) { Logger.LogDebug("Checking PDF attachments from stream (multipart)"); var result = await SendMultipartAsync( "/api/pdf/attachments/check", pdfStream, "document.pdf", cancellationToken); return result ?? throw new InvalidOperationException("API returned null response"); } /// public async Task CheckAttachmentsAsync(byte[] pdfBytes, CancellationToken cancellationToken = default) { Logger.LogDebug("Checking PDF attachments from byte array (Base64 JSON)"); var request = new CheckPdfAttachmentsRequest { Base64Pdf = ToBase64(pdfBytes) }; var result = await SendJsonAsync( "/api/pdf/attachments/check", request, cancellationToken); return result ?? throw new InvalidOperationException("API returned null response"); } /// public async Task> ExtractAttachmentsAsync(Stream pdfStream, CancellationToken cancellationToken = default) { Logger.LogDebug("Extracting PDF attachments from stream (multipart)"); var zipStream = await SendMultipartForStreamAsync( "/api/pdf/attachments/extract", pdfStream, "document.pdf", cancellationToken); return UnzipToStreams(zipStream); } /// public async Task> ExtractAttachmentsAsync(byte[] pdfBytes, CancellationToken cancellationToken = default) { Logger.LogDebug("Extracting PDF attachments from byte array (Base64 JSON)"); var request = new ExtractPdfAttachmentsRequest { Base64Pdf = ToBase64(pdfBytes) }; var zipStream = await SendJsonForStreamAsync( "/api/pdf/attachments/extract", request, cancellationToken); return UnzipToStreams(zipStream); } /// [Obsolete("API endpoint not implemented yet")] public Task AddAttachmentsAsync(Stream pdfStream, List attachments, CancellationToken cancellationToken = default) { Logger.LogWarning("AddAttachments endpoint is not implemented yet in API"); throw new NotImplementedException("API endpoint /api/pdf/attachments/add is not implemented yet"); } /// [Obsolete("API endpoint not implemented yet")] public Task AddAttachmentsAsync(byte[] pdfBytes, List attachments, CancellationToken cancellationToken = default) { Logger.LogWarning("AddAttachments endpoint is not implemented yet in API"); throw new NotImplementedException("API endpoint /api/pdf/attachments/add is not implemented yet"); } // ????????????????????????????????????????????????????????????????????????? // Private helpers // ????????????????????????????????????????????????????????????????????????? /// /// Reads a ZIP stream and returns a dictionary mapping each entry's full name /// to an in-memory containing the decompressed bytes. /// The caller owns the returned streams and is responsible for disposing them. /// private static Dictionary UnzipToStreams(Stream zipStream) { var result = new Dictionary(StringComparer.OrdinalIgnoreCase); using var archive = new ZipArchive(zipStream, ZipArchiveMode.Read, leaveOpen: false); foreach (var entry in archive.Entries) { // Skip directory entries (name ends with '/') if (string.IsNullOrEmpty(entry.Name)) continue; var ms = new MemoryStream((int)entry.Length); using (var entryStream = entry.Open()) { entryStream.CopyTo(ms); } ms.Position = 0; result[entry.FullName] = ms; } return result; } }