diff --git a/Correo.sln b/Correo.sln
index e701e8f..6c2f4c1 100644
--- a/Correo.sln
+++ b/Correo.sln
@@ -28,7 +28,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Correo.NetSmtpClient", "src
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Correo.Abstractions", "src\Correo.Abstractions\Correo.Abstractions.csproj", "{ED8048DC-8509-4085-97F0-F9D9A59A689A}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Correo.MailKit", "src\Correo.MailKit\Correo.MailKit.csproj", "{325B77E1-D752-4578-8BF7-793905C38DCD}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Correo.MailKit", "src\Correo.MailKit\Correo.MailKit.csproj", "{325B77E1-D752-4578-8BF7-793905C38DCD}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Correo.SendGrid", "src\Correo.SendGrid\Correo.SendGrid.csproj", "{1457426A-CD47-4201-BE3C-A40B38FCB1FA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -64,6 +66,10 @@ Global
{325B77E1-D752-4578-8BF7-793905C38DCD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{325B77E1-D752-4578-8BF7-793905C38DCD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{325B77E1-D752-4578-8BF7-793905C38DCD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1457426A-CD47-4201-BE3C-A40B38FCB1FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1457426A-CD47-4201-BE3C-A40B38FCB1FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1457426A-CD47-4201-BE3C-A40B38FCB1FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1457426A-CD47-4201-BE3C-A40B38FCB1FA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -76,6 +82,7 @@ Global
{76793477-8219-4FFB-AA08-3F2224EC4968} = {245E2FBE-DFDF-40B4-94B7-5DDA216E58AD}
{ED8048DC-8509-4085-97F0-F9D9A59A689A} = {245E2FBE-DFDF-40B4-94B7-5DDA216E58AD}
{325B77E1-D752-4578-8BF7-793905C38DCD} = {245E2FBE-DFDF-40B4-94B7-5DDA216E58AD}
+ {1457426A-CD47-4201-BE3C-A40B38FCB1FA} = {245E2FBE-DFDF-40B4-94B7-5DDA216E58AD}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {86FCF989-26FC-41E9-8A23-9485606D619D}
diff --git a/dependencies.props b/dependencies.props
index 03a5ea3..2480ea9 100644
--- a/dependencies.props
+++ b/dependencies.props
@@ -9,5 +9,6 @@
9.0.0
6.0.30
3.4.3
+ 9.28.1
\ No newline at end of file
diff --git a/src/Correo.SendGrid/Correo.SendGrid.csproj b/src/Correo.SendGrid/Correo.SendGrid.csproj
new file mode 100644
index 0000000..92a48f4
--- /dev/null
+++ b/src/Correo.SendGrid/Correo.SendGrid.csproj
@@ -0,0 +1,18 @@
+
+
+
+ net6.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Correo.SendGrid/DependencyInjectionExtensions.cs b/src/Correo.SendGrid/DependencyInjectionExtensions.cs
new file mode 100644
index 0000000..c6b1fbe
--- /dev/null
+++ b/src/Correo.SendGrid/DependencyInjectionExtensions.cs
@@ -0,0 +1,16 @@
+using Correo.Abstractions;
+using Correo.SendGrid.Models;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Correo.SendGrid
+{
+ public static class DependencyInjectionExtensions
+ {
+ public static void AddSendGridService(this IServiceCollection services, IConfigurationSection configuration)
+ {
+ services.Configure(configuration);
+ services.AddTransient();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Correo.SendGrid/Extensions/ModelExtensions.cs b/src/Correo.SendGrid/Extensions/ModelExtensions.cs
new file mode 100644
index 0000000..85e2645
--- /dev/null
+++ b/src/Correo.SendGrid/Extensions/ModelExtensions.cs
@@ -0,0 +1,40 @@
+using Correo.Abstractions;
+using SendGrid.Helpers.Mail;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Correo.SendGrid.Extensions
+{
+ internal static class ModelExtensions
+ {
+ private static EmailAddress ToEmailAddress(this EmailMessage.MailAddress address)
+ => new EmailAddress(address.Address, address.DisplayName);
+
+ private static List ToEmailAddressList(this IEnumerable addresses)
+ => addresses.Select(z => new EmailAddress(z.Address, z.DisplayName)).ToList();
+
+ public static SendGridMessage ToSendGridMessage(this EmailMessage message)
+ {
+ var subject = message.Subject;
+ var plainContent = !message.IsBodyHtml ? message.Body : null;
+ var htmlContent = message.IsBodyHtml ? message.Body : null;
+ var from = message.From.ToEmailAddress();
+ var to = message.To.First().ToEmailAddress();
+ var mailMessage = MailHelper.CreateSingleEmail(from, to, subject, plainContent, htmlContent);
+
+ if (message.To.Count() > 1)
+ {
+ var tos = message.To.Skip(1).Select(z => new EmailAddress(z.Address, z.DisplayName)).ToList();
+ mailMessage.AddTos(tos);
+ }
+
+ if (message.Cc != null && message.Cc.Any())
+ mailMessage.AddCcs(message.Cc.ToEmailAddressList());
+
+ if (message.Bcc != null && message.Bcc.Any())
+ mailMessage.AddBccs(message.Bcc.ToEmailAddressList());
+
+ return mailMessage;
+ }
+ }
+}
diff --git a/src/Correo.SendGrid/Models/ErrorModels.cs b/src/Correo.SendGrid/Models/ErrorModels.cs
new file mode 100644
index 0000000..b950e2b
--- /dev/null
+++ b/src/Correo.SendGrid/Models/ErrorModels.cs
@@ -0,0 +1,9 @@
+namespace Correo.SendGrid.Models
+{
+ internal record Error
+ {
+ public string Message { get; init; }
+ public string Field { get; init; }
+ public string Help { get; init; }
+ }
+}
diff --git a/src/Correo.SendGrid/Models/SendGridOptions.cs b/src/Correo.SendGrid/Models/SendGridOptions.cs
new file mode 100644
index 0000000..028be47
--- /dev/null
+++ b/src/Correo.SendGrid/Models/SendGridOptions.cs
@@ -0,0 +1,7 @@
+namespace Correo.SendGrid.Models
+{
+ internal record SendGridOptions
+ {
+ public string ApiKey { get; init; }
+ }
+}
diff --git a/src/Correo.SendGrid/SendGridService.cs b/src/Correo.SendGrid/SendGridService.cs
new file mode 100644
index 0000000..e111fdb
--- /dev/null
+++ b/src/Correo.SendGrid/SendGridService.cs
@@ -0,0 +1,61 @@
+using Correo.Abstractions;
+using Correo.Abstractions.Extensions;
+using Correo.SendGrid.Extensions;
+using Correo.SendGrid.Models;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Newtonsoft.Json;
+using SendGrid;
+using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Correo.SendGrid
+{
+ internal class SendGridService : IMailer
+ {
+ private readonly IOptions _optionsAccessor;
+ private readonly SendGridClient _client;
+ private readonly ILogger _logger;
+
+ public SendGridService(IOptions optionsAccessor, ILogger logger)
+ {
+ _optionsAccessor = optionsAccessor;
+ _logger = logger;
+ _client = new SendGridClient(_optionsAccessor.Value.ApiKey);
+ }
+
+ public void SendEmail(EmailMessage message)
+ {
+ var mailMessage = message.ToSendGridMessage();
+ _client.SendEmailAsync(mailMessage).GetAwaiter().GetResult();
+ Log(message);
+ }
+
+ public async Task SendEmailAsync(EmailMessage message, CancellationToken token = default)
+ {
+ var mailMessage = message.ToSendGridMessage();
+ var response = await _client.SendEmailAsync(mailMessage, token);
+ if (response.IsSuccessStatusCode)
+ Log(message);
+ else
+ {
+ var body = await response.DeserializeResponseBodyAsync();
+ var found = body.TryGetValue("errors", out var jsonData);
+ if (found)
+ {
+ string errorsString = Convert.ToString(jsonData);
+ var errors = JsonConvert.DeserializeObject(errorsString);
+ var errorMessage = string.Join("; ", errors.Select(z => z.Message));
+ throw new Exception(errorMessage);
+ }
+
+ throw new Exception(response.StatusCode.ToString());
+ }
+ }
+
+ private void Log(EmailMessage message)
+ => _logger.LogInformation($"Email sent: Subject: {message.Subject}; From: {message.From.Address}; To: {message.To.Log()}; Cc: {message.Cc.Log()}; Bcc: {message.Bcc.Log()};");
+ }
+}
diff --git a/src/Correo/Correo.csproj b/src/Correo/Correo.csproj
index 1bf5f31..d51adaa 100644
--- a/src/Correo/Correo.csproj
+++ b/src/Correo/Correo.csproj
@@ -18,6 +18,7 @@
+
diff --git a/src/Correo/Extensions/SmtpExtensions.cs b/src/Correo/Extensions/SmtpExtensions.cs
index 3910f33..cee12ee 100644
--- a/src/Correo/Extensions/SmtpExtensions.cs
+++ b/src/Correo/Extensions/SmtpExtensions.cs
@@ -1,5 +1,6 @@
using Correo.MailKit;
using Correo.NetSmtpClient;
+using Correo.SendGrid;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
@@ -8,22 +9,24 @@ namespace Correo.Extensions
{
public static class SmtpExtensions
{
- public static void AddSmtpClient(this IServiceCollection services, IConfiguration configuration)
+ public static void AddSmtpService(this IServiceCollection services, IConfiguration configuration)
{
- var configSection = configuration.GetSection("SmtpClient");
- var mediator = configSection.GetValue("Mediator", ".NET");
-
+ var mediator = configuration.GetValue("SmtpMediator", ".NET");
if (mediator.Equals(".NET", StringComparison.InvariantCultureIgnoreCase))
{
- services.AddNetSmtpClient(configSection);
+ services.AddNetSmtpClient(configuration.GetSection("SmtpClient"));
}
else if (mediator.Equals("MailKit", StringComparison.InvariantCultureIgnoreCase))
{
- services.AddMailKitSmtpClient(configSection);
+ services.AddMailKitSmtpClient(configuration.GetSection("SmtpClient"));
+ }
+ else if (mediator.Equals("SendGrid", StringComparison.InvariantCultureIgnoreCase))
+ {
+ services.AddSendGridService(configuration.GetSection("SmtpService"));
}
else
{
- throw new NotImplementedException($"SmtpClient:Mediator={mediator} is not implemented.");
+ throw new NotImplementedException($"SmtpMediator '{mediator}' is not implemented.");
}
}
}
diff --git a/src/Correo/Extensions/StartupExtensions.cs b/src/Correo/Extensions/StartupExtensions.cs
index 8da630a..0d83145 100644
--- a/src/Correo/Extensions/StartupExtensions.cs
+++ b/src/Correo/Extensions/StartupExtensions.cs
@@ -30,7 +30,7 @@ namespace Correo.Extensions
services.AddMessaging(configuration);
// Add SMTP client
- services.AddSmtpClient(configuration);
+ services.AddSmtpService(configuration);
// Application services
services.AddApplicationServices(configuration);
diff --git a/src/Correo/appsettings.json b/src/Correo/appsettings.json
index 3539786..7341fcb 100644
--- a/src/Correo/appsettings.json
+++ b/src/Correo/appsettings.json
@@ -29,8 +29,8 @@
"Address": "noreply@homelab.com",
"Name": "HomeLab"
},
+ "SmtpMediator": ".NET", //.NET, MailKit, SendGrid
"SmtpClient": {
- "Mediator": ".NET", //.NET, MailKit
"Server": "smtp.gmail.com",
"Port": "587",
"UseAuthentication": true,
@@ -41,5 +41,8 @@
},
"EnableSsl": true,
"TrustServer": false
+ },
+ "SmtpService": {
+ "ApiKey": "xxxxxxxxxxx"
}
}
\ No newline at end of file