using DocumentOperator.Application.Common.Interfaces; using FluentValidation; using MediatR; namespace DocumentOperator.Application.AddAttachments; /// /// Command to add one or more attachments to a PDF document (supports PDF/A-3) /// public record AddAttachmentsCommand : IRequest { /// /// PDF document stream. Must be positioned at the beginning (Position = 0). /// public required Stream PdfStream { get; init; } /// /// List of attachments to embed (filename, content, optional MIME type) /// public required IReadOnlyList Attachments { get; init; } } /// /// Represents a file to be attached to a PDF /// public record AttachmentFile { /// /// File name (e.g., "invoice.xml", "document.pdf") /// public required string FileName { get; init; } /// /// File content as byte array /// public required byte[] Content { get; init; } /// /// MIME type (optional, e.g., "application/xml", "application/pdf") /// If not provided, will be inferred from file extension /// public string? MimeType { get; init; } } /// /// Handler for AddAttachmentsCommand /// public class AddAttachmentsCommandHandler(IPdfProcessor pdfProcessor) : IRequestHandler { public async Task Handle(AddAttachmentsCommand request, CancellationToken cancellationToken) { // Convert to tuple list for IPdfProcessor var attachmentTuples = request.Attachments .Select(a => (a.FileName, a.Content, a.MimeType)) .ToList(); return await pdfProcessor.AddAttachmentsAsync(request.PdfStream, attachmentTuples); } } /// /// Validator for AddAttachmentsCommand /// public class AddAttachmentsCommandValidator : AbstractValidator { public AddAttachmentsCommandValidator() { RuleFor(x => x.PdfStream) .NotNull() .WithMessage("PDF stream is required"); RuleFor(x => x.Attachments) .NotNull() .NotEmpty() .WithMessage("At least one attachment is required"); RuleForEach(x => x.Attachments).ChildRules(attachment => { attachment.RuleFor(a => a.FileName) .NotEmpty() .WithMessage("Attachment file name is required"); attachment.RuleFor(a => a.Content) .NotNull() .NotEmpty() .WithMessage("Attachment content is required"); }); } }