diff --git a/src/Senders/FluentEmail.Mailgun/IFluentEmailExtensions.cs b/src/Senders/FluentEmail.Mailgun/IFluentEmailExtensions.cs new file mode 100644 index 00000000..abe065d7 --- /dev/null +++ b/src/Senders/FluentEmail.Mailgun/IFluentEmailExtensions.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; +using FluentEmail.Core; +using FluentEmail.Core.Models; + +namespace FluentEmail.Mailgun +{ + public static class IFluentEmailExtensions + { + public static async Task SendWithTemplateAsync(this IFluentEmail email, string templateName, object templateData) + { + var mailgunSender = email.Sender as IMailgunSender; + return await mailgunSender.SendWithTemplateAsync(email, templateName, templateData); + } + } +} diff --git a/src/Senders/FluentEmail.Mailgun/IMailgunSender.cs b/src/Senders/FluentEmail.Mailgun/IMailgunSender.cs new file mode 100644 index 00000000..5b491a69 --- /dev/null +++ b/src/Senders/FluentEmail.Mailgun/IMailgunSender.cs @@ -0,0 +1,21 @@ +using System.Threading; +using System.Threading.Tasks; +using FluentEmail.Core; +using FluentEmail.Core.Interfaces; +using FluentEmail.Core.Models; + +namespace FluentEmail.Mailgun; + +public interface IMailgunSender : ISender +{ + /// + /// Mailgun specific extension method that allows you to use a template instead of a message body. + /// For more information, see: https://documentation.mailgun.com/en/latest/api-sending.html#sending. + /// + /// Fluent email. + /// Mailgun template name. + /// Mailgun template data. + /// Optional cancellation token. + /// A SendResponse object. + Task SendWithTemplateAsync(IFluentEmail email, string templateName, object templateData, CancellationToken? token = null); +} \ No newline at end of file diff --git a/src/Senders/FluentEmail.Mailgun/MailgunSender.cs b/src/Senders/FluentEmail.Mailgun/MailgunSender.cs index c1f3f359..9e5f9a63 100644 --- a/src/Senders/FluentEmail.Mailgun/MailgunSender.cs +++ b/src/Senders/FluentEmail.Mailgun/MailgunSender.cs @@ -10,10 +10,11 @@ using FluentEmail.Core.Interfaces; using FluentEmail.Core.Models; using FluentEmail.Mailgun.HttpHelpers; +using Newtonsoft.Json; namespace FluentEmail.Mailgun { - public class MailgunSender : ISender + public class MailgunSender : IMailgunSender { private readonly string _apiKey; private readonly string _domainName; @@ -51,46 +52,39 @@ public SendResponse Send(IFluentEmail email, CancellationToken? token = null) public async Task SendAsync(IFluentEmail email, CancellationToken? token = null) { - var parameters = new List>(); - - parameters.Add(new KeyValuePair("from", $"{email.Data.FromAddress.Name} <{email.Data.FromAddress.EmailAddress}>")); - email.Data.ToAddresses.ForEach(x => { - parameters.Add(new KeyValuePair("to", $"{x.Name} <{x.EmailAddress}>")); - }); - email.Data.CcAddresses.ForEach(x => { - parameters.Add(new KeyValuePair("cc", $"{x.Name} <{x.EmailAddress}>")); - }); - email.Data.BccAddresses.ForEach(x => { - parameters.Add(new KeyValuePair("bcc", $"{x.Name} <{x.EmailAddress}>")); - }); - email.Data.ReplyToAddresses.ForEach(x => { - parameters.Add(new KeyValuePair("h:Reply-To", $"{x.Name} <{x.EmailAddress}>")); - }); - parameters.Add(new KeyValuePair("subject", email.Data.Subject)); - + var parameters = BuildMailgunParameters(email); + parameters.Add(new KeyValuePair(email.Data.IsHtml ? "html" : "text", email.Data.Body)); if (!string.IsNullOrEmpty(email.Data.PlaintextAlternativeBody)) { parameters.Add(new KeyValuePair("text", email.Data.PlaintextAlternativeBody)); } + + var files = BuildMailgunFiles(email); - email.Data.Tags.ForEach(x => - { - parameters.Add(new KeyValuePair("o:tag", x)); - }); + return await SendAsync(parameters, files, token); + } - foreach (var emailHeader in email.Data.Headers) - { - var key = emailHeader.Key; - if (!key.StartsWith("h:")) - { - key = "h:" + emailHeader.Key; - } + private async Task SendAsync(List> parameters, List files, CancellationToken? token = null) + { + token?.ThrowIfCancellationRequested(); + + var response = await _httpClient.PostMultipart("messages", parameters, files) + .ConfigureAwait(false); - parameters.Add(new KeyValuePair(key, emailHeader.Value)); + var result = new SendResponse {MessageId = response.Data?.Id}; + if (!response.Success) + { + result.ErrorMessages.AddRange(response.Errors.Select(x => x.ErrorMessage)); + return result; } + return result; + } + + private static List BuildMailgunFiles(IFluentEmail email) + { var files = new List(); email.Data.Attachments.ForEach(x => { @@ -109,17 +103,60 @@ public async Task SendAsync(IFluentEmail email, CancellationToken? ContentType = x.ContentType }); }); + return files; + } + + private static List> BuildMailgunParameters(IFluentEmail email) + { + var parameters = new List>(); + + parameters.Add(new KeyValuePair("from", + $"{email.Data.FromAddress.Name} <{email.Data.FromAddress.EmailAddress}>")); + + email.Data.ToAddresses.ForEach(x => + { + parameters.Add(new KeyValuePair("to", $"{x.Name} <{x.EmailAddress}>")); + }); + email.Data.CcAddresses.ForEach(x => + { + parameters.Add(new KeyValuePair("cc", $"{x.Name} <{x.EmailAddress}>")); + }); + email.Data.BccAddresses.ForEach(x => + { + parameters.Add(new KeyValuePair("bcc", $"{x.Name} <{x.EmailAddress}>")); + }); + email.Data.ReplyToAddresses.ForEach(x => + { + parameters.Add(new KeyValuePair("h:Reply-To", $"{x.Name} <{x.EmailAddress}>")); + }); + parameters.Add(new KeyValuePair("subject", email.Data.Subject)); - var response = await _httpClient.PostMultipart("messages", parameters, files).ConfigureAwait(false); + email.Data.Tags.ForEach(x => { parameters.Add(new KeyValuePair("o:tag", x)); }); - var result = new SendResponse {MessageId = response.Data?.Id}; - if (!response.Success) + foreach (var emailHeader in email.Data.Headers) { - result.ErrorMessages.AddRange(response.Errors.Select(x => x.ErrorMessage)); - return result; + var key = emailHeader.Key; + if (!key.StartsWith("h:")) + { + key = "h:" + emailHeader.Key; + } + + parameters.Add(new KeyValuePair(key, emailHeader.Value)); } + + return parameters; + } - return result; + public async Task SendWithTemplateAsync(IFluentEmail email, string templateName, object templateData, + CancellationToken? token = null) + { + var parameters = BuildMailgunParameters(email); + var files = BuildMailgunFiles(email); + + parameters.Add(new KeyValuePair("template", templateName)); + parameters.Add(new KeyValuePair("h:X-Mailgun-Variables", JsonConvert.SerializeObject(templateData))); + + return await SendAsync(parameters, files, token); } } } diff --git a/test/FluentEmail.Core.Tests/MailgunSenderTests.cs b/test/FluentEmail.Core.Tests/MailgunSenderTests.cs index 40cb8bf9..4af2be2b 100644 --- a/test/FluentEmail.Core.Tests/MailgunSenderTests.cs +++ b/test/FluentEmail.Core.Tests/MailgunSenderTests.cs @@ -134,6 +134,19 @@ public async Task CanSendEmailWithInlineImages() Assert.IsTrue(response.Successful); } } + + // [Test] + // public async Task CanSendEmailWithTemplate() + // { + // var email = Email + // .From(fromEmail) + // .To(toEmail) + // .Subject(subject); + // + // var response = await email.SendWithTemplateAsync("test-template", new { var1 = "Test" }); + // + // Assert.IsTrue(response.Successful); + // } class Variable {