From 890c32f1c855a609f6a88d361afe5a4e83f8ef9e Mon Sep 17 00:00:00 2001 From: TekH Date: Wed, 5 Aug 2026 16:00:55 +0200 Subject: [PATCH] Add support for email attachments in messaging service Introduced the `EmailAttachmentContext` class to represent email attachments, with properties for file name, content, content type, inline display behavior, and content ID. Used conditional compilation to support both .NET Framework and .NET versions. Updated the `EmailContext` class to include an `Attachments` property, enabling emails to include attachments as byte arrays or file paths. Enhanced the `LimilabsEmailService` class to handle attachments: - Added the `AddAttachments` method to process inline and regular attachments. - Integrated attachment handling into the email-building process. --- .../EmailAttachmentContext.cs | 45 +++++++++++++++++++ .../EmailContext.cs | 7 +++ .../Services/LimilabsEmailService.cs | 31 +++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 src/core/DigitalData.MessagingService.Abstraction/EmailAttachmentContext.cs diff --git a/src/core/DigitalData.MessagingService.Abstraction/EmailAttachmentContext.cs b/src/core/DigitalData.MessagingService.Abstraction/EmailAttachmentContext.cs new file mode 100644 index 0000000..720d87d --- /dev/null +++ b/src/core/DigitalData.MessagingService.Abstraction/EmailAttachmentContext.cs @@ -0,0 +1,45 @@ +namespace DigitalData.MessagingService.Abstraction; + +/// +/// Represents a single email attachment. +/// +public sealed class EmailAttachmentContext +{ + /// + /// Display name of the attachment (e.g. "invoice.pdf"). + /// +#if NETFRAMEWORK + public string FileName { get; set; } = null!; +#else + public required string FileName { get; init; } +#endif + + /// + /// Raw content of the attachment. + /// +#if NETFRAMEWORK + public byte[] Content { get; set; } = null!; +#else + public required byte[] Content { get; init; } +#endif + + /// + /// MIME content-type (e.g. "application/pdf", "image/png"). + /// Defaults to "application/octet-stream" when not specified. + /// + public string ContentType { get; set; } = "application/octet-stream"; + + /// + /// When the attachment is embedded inline and displayed + /// directly inside the email body via a CID reference (e.g. <img src="cid:logo">). + /// When (default) it appears as a regular downloadable attachment. + /// + public bool IsInline { get; set; } = false; + + /// + /// Content-ID used when is . + /// Reference it in HTML body as cid:{ContentId}. + /// Auto-generated from when left empty. + /// + public string? ContentId { get; set; } +} diff --git a/src/core/DigitalData.MessagingService.Abstraction/EmailContext.cs b/src/core/DigitalData.MessagingService.Abstraction/EmailContext.cs index 65d6127..919e444 100644 --- a/src/core/DigitalData.MessagingService.Abstraction/EmailContext.cs +++ b/src/core/DigitalData.MessagingService.Abstraction/EmailContext.cs @@ -45,4 +45,11 @@ public record EmailContext #else public bool IsHtml { get; init; } = true; #endif + + /// + /// Optional list of attachments to include with the email. + /// Each entry may carry its content as a byte array () + /// or reference a file on disk via . + /// + public IEnumerable Attachments { get; set; } = []; } \ No newline at end of file diff --git a/src/infrastructure/DigitalData.MessagingService.Infrastructure/Services/LimilabsEmailService.cs b/src/infrastructure/DigitalData.MessagingService.Infrastructure/Services/LimilabsEmailService.cs index 17e1afb..7616fbc 100644 --- a/src/infrastructure/DigitalData.MessagingService.Infrastructure/Services/LimilabsEmailService.cs +++ b/src/infrastructure/DigitalData.MessagingService.Infrastructure/Services/LimilabsEmailService.cs @@ -46,6 +46,8 @@ public class LimilabsEmailService( else builder.Text = context.Body; + AddAttachments(builder, context.Attachments); + var mail = builder.Create(); result = await smtp.SendMessageAsync(mail, cancellationToken); @@ -108,4 +110,33 @@ public class LimilabsEmailService( return message.ToString(); } + + private static void AddAttachments(MailBuilder builder, IEnumerable attachments) + { + foreach (var attachment in attachments) + { + if (attachment.IsInline) + { + var visual = builder.AddVisual(attachment.Content); + visual.FileName = attachment.FileName; + visual.ContentId = string.IsNullOrWhiteSpace(attachment.ContentId) + ? attachment.FileName + : attachment.ContentId; + + if (!string.IsNullOrWhiteSpace(attachment.ContentType)) + visual.ContentType = ContentType.Parse(attachment.ContentType); + } + else + { + var part = builder.AddAttachment(attachment.Content); + part.FileName = attachment.FileName; + + if (!string.IsNullOrWhiteSpace(attachment.ContentType)) + part.ContentType = ContentType.Parse(attachment.ContentType); + + if (!string.IsNullOrWhiteSpace(attachment.ContentId)) + part.ContentId = attachment.ContentId; + } + } + } } \ No newline at end of file