From 746d8326124424d9aec7bd596ebbfea6128584ea Mon Sep 17 00:00:00 2001 From: James Hickey Date: Fri, 20 Sep 2024 11:03:33 -0300 Subject: [PATCH] Mailer-fixes (#402) Mostly mailer fixes + improvements. - Issue where globalFrom was taking precedence over explicitly defined from email address in code - Adds ability to assign "sender" field (breaking change to custom mailer) - Added globalFrom documentation - Sender documentation --- .github/workflows/dotnet.yml | 36 ++++++++++ Demo/Demo.csproj | 6 +- Demo/Startup.cs | 19 ++--- DocsV2/docs/Mailing/README.md | 65 ++++++++++++------ Samples/EFCoreSample/test.csproj | 1 - .../QueueWithCancellationTokens.csproj | 1 - Src/Coravel.Mailer/Coravel.Mailer.csproj | 7 ++ Src/Coravel.Mailer/Mail/Interfaces/IMailer.cs | 2 +- Src/Coravel.Mailer/Mail/Mailable.cs | 17 ++++- .../Mail/Mailers/AssertMailer.cs | 6 +- .../Mail/Mailers/CustomMailer.cs | 6 +- .../Mail/Mailers/FileLogMailer.cs | 5 +- Src/Coravel.Mailer/Mail/Mailers/SmtpMailer.cs | 15 +++- Src/Coravel.Mailer/logo.png | Bin 0 -> 9982 bytes Src/Coravel.Mailer/readme.md | 29 ++++++++ .../TestMvcApp/appsettings.json | 9 ++- .../MailerUnitTests/Mail/CustomMailerTests.cs | 35 ++++++++-- 17 files changed, 206 insertions(+), 53 deletions(-) create mode 100644 Src/Coravel.Mailer/logo.png create mode 100644 Src/Coravel.Mailer/readme.md diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index b7876f8d..e955bda6 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -25,4 +25,40 @@ jobs: run: dotnet test ./Src/UnitTests/CoravelUnitTests/CoravelUnitTests.csproj - name: Integration Tests run: dotnet test ./Src/IntegrationTests/Tests/Tests.csproj + build_and_tests_on_dotnet_7: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 7.0.x + - name: Restore dependencies + run: dotnet restore ./Src/All.sln + - name: Build + run: dotnet build ./Src/All.sln + - name: Mailer Tests + run: dotnet test ./Src/UnitTests/MailerUnitTests/MailerUnitTests.csproj + - name: Unit Tests + run: dotnet test ./Src/UnitTests/CoravelUnitTests/CoravelUnitTests.csproj + - name: Integration Tests + run: dotnet test ./Src/IntegrationTests/Tests/Tests.csproj + build_and_tests_on_dotnet_8: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 8.0.x + - name: Restore dependencies + run: dotnet restore ./Src/All.sln + - name: Build + run: dotnet build ./Src/All.sln + - name: Mailer Tests + run: dotnet test ./Src/UnitTests/MailerUnitTests/MailerUnitTests.csproj + - name: Unit Tests + run: dotnet test ./Src/UnitTests/CoravelUnitTests/CoravelUnitTests.csproj + - name: Integration Tests + run: dotnet test ./Src/IntegrationTests/Tests/Tests.csproj diff --git a/Demo/Demo.csproj b/Demo/Demo.csproj index 646d419e..b7b5041f 100644 --- a/Demo/Demo.csproj +++ b/Demo/Demo.csproj @@ -3,11 +3,12 @@ net6.0 Linux + True - + @@ -15,8 +16,7 @@ - - + diff --git a/Demo/Startup.cs b/Demo/Startup.cs index ccd89b95..75b821d4 100644 --- a/Demo/Startup.cs +++ b/Demo/Startup.cs @@ -34,7 +34,8 @@ public void ConfigureServices(IServiceCollection services) options.MinimumSameSitePolicy = SameSiteMode.None; }); - services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); + services.AddMvc(options => { options.EnableEndpointRouting = false; }).AddRazorRuntimeCompilation(); + services.AddControllersWithViews(); // Coravel Scheduling services.AddScheduler(); @@ -68,19 +69,19 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env) else { app.UseExceptionHandler("/Home/Error"); - app.UseHsts(); + // app.UseHsts(); } - app.UseHttpsRedirection(); + // app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseCookiePolicy(); - // app.UseMvc(routes => - // { - // routes.MapRoute( - // name: "default", - // template: "{controller=Home}/{action=Index}/{id?}"); - // }); + app.UseMvc(routes => + { + routes.MapRoute( + name: "default", + template: "{controller=Home}/{action=Index}/{id?}"); + }); IEventRegistration registration = app.ApplicationServices.ConfigureEvents(); diff --git a/DocsV2/docs/Mailing/README.md b/DocsV2/docs/Mailing/README.md index 49bffebc..c062139c 100644 --- a/DocsV2/docs/Mailing/README.md +++ b/DocsV2/docs/Mailing/README.md @@ -102,7 +102,8 @@ async Task SendMailCustomAsync( MailRecipient replyTo, IEnumerable cc, IEnumerable bcc, - IEnumerable attachments = null + IEnumerable attachments = null, + MailRecipient sender = null ) { // Custom logic for sending an email. @@ -111,6 +112,10 @@ async Task SendMailCustomAsync( services.AddCustomMailer(this.Configuration, SendMailCustomAsync); ``` +:::warn +Breaking changes to this method signature are more likely than other as this is the signature that the Mailer's internals use. If a new version of the Mailer causes your code to stop compiling sucessfully, it's probably this signature that needs to be updated. Luckliy, it's usually a quick change in 1 spot. +::: + ### Built-In View Templates Coravel's mailer comes with some pre-built e-mail friendly razor templates! This means you don't have to worry about @@ -134,7 +139,7 @@ What about static content like the mail footer and logo? Coravel's got you cover In your `appsettings.json`, you may add the following global values that will populate when using Coravel's built-in templates: -``` +```json "Coravel": { "Mail": { /* Your app's logo that will be shown at the top of your e-mails. */ @@ -195,35 +200,53 @@ You can then call various methods like `To` and `From` to configure the recipien ### From -To specify who the sender of the email is, use the `From()` method: +To specify who the email is from, use the `From()` method: -`From("test@test.com")` +```csharp +From("test@test.com") +``` You may also supply an instance of `Coravel.Mailer.Mail.MailRecipient` to include the address and sender name: -`From(new MailRecipient(email, name))` +```csharp +From(new MailRecipient(email, name)) +``` -### Send To Recipient +You can set a global from address by setting it in `appsettings.json`: -Using the `To()` method, you can supply the recipient's e-mail address and name. - -#### Address +```json + "Coravel": { + "Mail": { + "From":{ + "Address": "global@from.com", + "Name": "My Company" + } + } + } +``` -Using an e-mail address in a `string`: +### To -`To("test@test.com")` +Using the `To()` method, you can supply the recipient's e-mail address and name. -#### Multiple Addresses +Or, using an e-mail address in a `string`: -You can pass`IEnumerable` to the `To()` method. +```csharp +To("test@test.com") +``` -#### MailRecipient +You can also pass: +- `To(IEnumerable)` +- `To(MailRecipient)` +- `To(IEnumerable)` -Pass an instance of `MailRecipient` to the `To()` method. +### Sender -#### Multiple MailRecipients +To specify the sender of the email (different from the `From` address), use the `Sender()` method: -Pass an `IEnumerable` to the `To()` method. +```csharp +Sender("test@test.com") +``` #### Attachments @@ -261,18 +284,16 @@ Further methods, which all accept either `IEnumerable` or `IEnumerable 👈 Make sure it's this SDK. - Exe - netcoreapp3.1 True 👈 Add this too. ``` diff --git a/Samples/EFCoreSample/test.csproj b/Samples/EFCoreSample/test.csproj index 877225ad..5da47bb0 100644 --- a/Samples/EFCoreSample/test.csproj +++ b/Samples/EFCoreSample/test.csproj @@ -13,7 +13,6 @@ - diff --git a/Samples/QueueWithCancellationTokens/QueueWithCancellationTokens.csproj b/Samples/QueueWithCancellationTokens/QueueWithCancellationTokens.csproj index d26720ab..a48099d1 100644 --- a/Samples/QueueWithCancellationTokens/QueueWithCancellationTokens.csproj +++ b/Samples/QueueWithCancellationTokens/QueueWithCancellationTokens.csproj @@ -6,7 +6,6 @@ - diff --git a/Src/Coravel.Mailer/Coravel.Mailer.csproj b/Src/Coravel.Mailer/Coravel.Mailer.csproj index b0ea54b4..5a6b8305 100644 --- a/Src/Coravel.Mailer/Coravel.Mailer.csproj +++ b/Src/Coravel.Mailer/Coravel.Mailer.csproj @@ -17,10 +17,17 @@ true true $(NoWarn);1591 + ./readme.md + ./logo.png + + + + + diff --git a/Src/Coravel.Mailer/Mail/Interfaces/IMailer.cs b/Src/Coravel.Mailer/Mail/Interfaces/IMailer.cs index 1cdc37b5..87c52b11 100644 --- a/Src/Coravel.Mailer/Mail/Interfaces/IMailer.cs +++ b/Src/Coravel.Mailer/Mail/Interfaces/IMailer.cs @@ -7,6 +7,6 @@ public interface IMailer { Task RenderAsync(Mailable mailable); Task SendAsync(Mailable mailable); - Task SendAsync(string message, string subject, IEnumerable to, MailRecipient from, MailRecipient replyTo, IEnumerable cc, IEnumerable bcc, IEnumerable attachments = null); + Task SendAsync(string message, string subject, IEnumerable to, MailRecipient from, MailRecipient replyTo, IEnumerable cc, IEnumerable bcc, IEnumerable attachments = null, MailRecipient sender = null); } } \ No newline at end of file diff --git a/Src/Coravel.Mailer/Mail/Mailable.cs b/Src/Coravel.Mailer/Mail/Mailable.cs index 3c7a3caa..3b8722ed 100644 --- a/Src/Coravel.Mailer/Mail/Mailable.cs +++ b/Src/Coravel.Mailer/Mail/Mailable.cs @@ -17,6 +17,11 @@ public class Mailable /// private MailRecipient _from; + /// + /// The email sender. + /// + private MailRecipient _sender; + /// /// Recipients of the message. /// @@ -73,6 +78,15 @@ public Mailable From(MailRecipient recipient) public Mailable From(string email) => this.From(new MailRecipient(email)); + public Mailable Sender(MailRecipient recipient) + { + this._sender = recipient; + return this; + } + + public Mailable Sender(string email) => + this.Sender(new MailRecipient(email)); + public Mailable To(IEnumerable recipients) { this._to = recipients; @@ -175,7 +189,8 @@ await mailer.SendAsync( this._replyTo, this._cc, this._bcc, - this._attachments + this._attachments, + sender: this._sender ).ConfigureAwait(false); } diff --git a/Src/Coravel.Mailer/Mail/Mailers/AssertMailer.cs b/Src/Coravel.Mailer/Mail/Mailers/AssertMailer.cs index 86b0b039..0eeb6be9 100644 --- a/Src/Coravel.Mailer/Mail/Mailers/AssertMailer.cs +++ b/Src/Coravel.Mailer/Mail/Mailers/AssertMailer.cs @@ -13,6 +13,7 @@ public class Data public string subject { get; set; } public IEnumerable to { get; set; } public MailRecipient from { get; set; } + public MailRecipient sender { get; set; } public MailRecipient replyTo { get; set; } public IEnumerable cc { get; set; } public IEnumerable bcc { get; set; } @@ -26,7 +27,7 @@ public AssertMailer(Action assertAction) this._assertAction = assertAction; } - public Task SendAsync(string message, string subject, IEnumerable to, MailRecipient from, MailRecipient replyTo, IEnumerable cc, IEnumerable bcc, IEnumerable attachments) + public Task SendAsync(string message, string subject, IEnumerable to, MailRecipient from, MailRecipient replyTo, IEnumerable cc, IEnumerable bcc, IEnumerable attachments, MailRecipient sender = null) { this._assertAction(new Data { @@ -37,7 +38,8 @@ public Task SendAsync(string message, string subject, IEnumerable replyTo = replyTo, cc = cc, bcc = bcc, - attachments = attachments + attachments = attachments, + sender = sender }); return Task.CompletedTask; } diff --git a/Src/Coravel.Mailer/Mail/Mailers/CustomMailer.cs b/Src/Coravel.Mailer/Mail/Mailers/CustomMailer.cs index 7a003075..3cec413a 100644 --- a/Src/Coravel.Mailer/Mail/Mailers/CustomMailer.cs +++ b/Src/Coravel.Mailer/Mail/Mailers/CustomMailer.cs @@ -7,7 +7,7 @@ namespace Coravel.Mailer.Mail.Mailers { public class CustomMailer : IMailer { - public delegate Task SendAsyncFunc(string message, string subject, IEnumerable to, MailRecipient from, MailRecipient replyTo, IEnumerable cc, IEnumerable bcc, IEnumerable attachments = null); + public delegate Task SendAsyncFunc(string message, string subject, IEnumerable to, MailRecipient from, MailRecipient replyTo, IEnumerable cc, IEnumerable bcc, IEnumerable attachments = null, MailRecipient sender = null); private RazorRenderer _renderer; private SendAsyncFunc _sendAsyncFunc; private MailRecipient _globalFrom; @@ -25,10 +25,10 @@ public Task RenderAsync(Mailable mailable) => public async Task SendAsync(Mailable mailable) => await mailable.SendAsync(this._renderer, this); - public async Task SendAsync(string message, string subject, IEnumerable to, MailRecipient from, MailRecipient replyTo, IEnumerable cc, IEnumerable bcc, IEnumerable attachments) + public async Task SendAsync(string message, string subject, IEnumerable to, MailRecipient from, MailRecipient replyTo, IEnumerable cc, IEnumerable bcc, IEnumerable attachments, MailRecipient sender = null) { await this._sendAsyncFunc( - message, subject, to, this._globalFrom ?? from, replyTo, cc, bcc, attachments + message, subject, to, from ?? this._globalFrom, replyTo, cc, bcc, attachments, sender: sender ); } } diff --git a/Src/Coravel.Mailer/Mail/Mailers/FileLogMailer.cs b/Src/Coravel.Mailer/Mail/Mailers/FileLogMailer.cs index 3d1a1b89..5cfd6a65 100644 --- a/Src/Coravel.Mailer/Mail/Mailers/FileLogMailer.cs +++ b/Src/Coravel.Mailer/Mail/Mailers/FileLogMailer.cs @@ -27,9 +27,9 @@ public async Task RenderAsync(Mailable mailable) return await mailable.RenderAsync(this._renderer, this); } - public async Task SendAsync(string message, string subject, IEnumerable to, MailRecipient from, MailRecipient replyTo, IEnumerable cc, IEnumerable bcc, IEnumerable attachments = null) + public async Task SendAsync(string message, string subject, IEnumerable to, MailRecipient from, MailRecipient replyTo, IEnumerable cc, IEnumerable bcc, IEnumerable attachments = null, MailRecipient sender = null) { - from = this._globalFrom ?? from; + from = from ?? this._globalFrom; using (var writer = File.CreateText(FilePath)) { @@ -38,6 +38,7 @@ await writer.WriteAsync($@" Subject: {subject} To: {CommaSeparated(to)} From: {DisplayAddress(from)} +Sender: { (sender is null ? "N/A" : DisplayAddress(sender)) } ReplyTo: {DisplayAddress(replyTo)} Cc: {CommaSeparated(cc)} Bcc: {CommaSeparated(bcc)} diff --git a/Src/Coravel.Mailer/Mail/Mailers/SmtpMailer.cs b/Src/Coravel.Mailer/Mail/Mailers/SmtpMailer.cs index 19fe6d4f..bb10c956 100644 --- a/Src/Coravel.Mailer/Mail/Mailers/SmtpMailer.cs +++ b/Src/Coravel.Mailer/Mail/Mailers/SmtpMailer.cs @@ -50,11 +50,12 @@ public async Task SendAsync(Mailable mailable) await mailable.SendAsync(this._renderer, this); } - public async Task SendAsync(string message, string subject, IEnumerable to, MailRecipient from, MailRecipient replyTo, IEnumerable cc, IEnumerable bcc, IEnumerable attachments = null) + public async Task SendAsync(string message, string subject, IEnumerable to, MailRecipient from, MailRecipient replyTo, IEnumerable cc, IEnumerable bcc, IEnumerable attachments = null, MailRecipient sender = null) { var mail = new MimeMessage(); this.SetFrom(@from, mail); + SetSender(sender, mail); SetRecipients(to, mail); SetCc(cc, mail); SetBcc(bcc, mail); @@ -121,7 +122,17 @@ private static void SetRecipients(IEnumerable to, MimeMessage mai private void SetFrom(MailRecipient @from, MimeMessage mail) { - mail.From.Add(AsMailboxAddress(this._globalFrom ?? @from)); + mail.From.Add(AsMailboxAddress(@from ?? this._globalFrom)); + } + + private static void SetSender(MailRecipient sender, MimeMessage mail) + { + if(sender is null) + { + return; + } + + mail.Sender = AsMailboxAddress(sender); } private static void SetReplyTo(MailRecipient replyTo, MimeMessage mail) diff --git a/Src/Coravel.Mailer/logo.png b/Src/Coravel.Mailer/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..028229b3687cefd06abb90e1bd9d36cb180b787d GIT binary patch literal 9982 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 W_|~q{cz>&?0C{N@sagpW$o~U>B+0}8 literal 0 HcmV?d00001 diff --git a/Src/Coravel.Mailer/readme.md b/Src/Coravel.Mailer/readme.md new file mode 100644 index 00000000..9b30e87b --- /dev/null +++ b/Src/Coravel.Mailer/readme.md @@ -0,0 +1,29 @@ +Coravel makes advanced application features accessible and easy-to-use by giving you a simple, +expressive and straightforward syntax - helping developers get their .NET Core applications up-and-running fast without compromising code quality. + +This is the mailer specific package for Coravel. + +## Features: + +E-mails are not as easy as they should be. Luckily for you, Coravel solves this by offering: + +- Built-in e-mail friendly razor templates +- Simple and flexible mailing API +- Render your e-mails for visual testing +- Drivers supporting SMTP, local log file or BYOM ("bring your own mailer") driver +- Quick and simple configuration via `appsettings.json` +- And more! + +## Official Documentation + +[You can view the official docs here.](https://docs.coravel.net/Installation/) + +## Coravel Pro + +If you are building a .NET Core application with EF Core, then you might want to look into [Coravel Pro](https://www.pro.coravel.net/). It is an admin panel & tools to make maintaining and managing your .NET Core app a breeze! + +- Visual job scheduling & management +- Scaffold a CRUD UI for managing your EF Core entities +- Easily configure a dashboard to show health metrics (or whatever you want) +- Build custom tablular reports of your data +- And more! \ No newline at end of file diff --git a/Src/IntegrationTests/TestMvcApp/appsettings.json b/Src/IntegrationTests/TestMvcApp/appsettings.json index def9159a..13ba4530 100644 --- a/Src/IntegrationTests/TestMvcApp/appsettings.json +++ b/Src/IntegrationTests/TestMvcApp/appsettings.json @@ -4,5 +4,12 @@ "Default": "Warning" } }, - "AllowedHosts": "*" + "AllowedHosts": "*", + "Coravel": { + "Mail": { + "From":{ + "Address": "global@from.com" + } + } + } } diff --git a/Src/UnitTests/MailerUnitTests/Mail/CustomMailerTests.cs b/Src/UnitTests/MailerUnitTests/Mail/CustomMailerTests.cs index 3576197c..ec1e6798 100644 --- a/Src/UnitTests/MailerUnitTests/Mail/CustomMailerTests.cs +++ b/Src/UnitTests/MailerUnitTests/Mail/CustomMailerTests.cs @@ -15,7 +15,7 @@ public class CustomMailerTests [Fact] public async Task CustomMailerSucessful() { - async Task SendMailCustom(string message, string subject, IEnumerable to, MailRecipient from, MailRecipient replyTo, IEnumerable cc, IEnumerable bcc, IEnumerable attachments) + async Task SendMailCustom(string message, string subject, IEnumerable to, MailRecipient from, MailRecipient replyTo, IEnumerable cc, IEnumerable bcc, IEnumerable attachments = null, MailRecipient sender = null) { Assert.Equal("test", subject); Assert.Equal("from@test.com", from.Email); @@ -41,7 +41,7 @@ await mailer.SendAsync( [Fact] public async Task CustomMailer_GlobalFrom() { - async Task SendMailCustom(string message, string subject, IEnumerable to, MailRecipient from, MailRecipient replyTo, IEnumerable cc, IEnumerable bcc, IEnumerable attachments) + async Task SendMailCustom(string message, string subject, IEnumerable to, MailRecipient from, MailRecipient replyTo, IEnumerable cc, IEnumerable bcc, IEnumerable attachments = null, MailRecipient sender = null) { Assert.Equal("global@test.com", from.Email); Assert.Equal("Global", from.Name); @@ -57,7 +57,32 @@ async Task SendMailCustom(string message, string subject, IEnumerable to, MailRecipient from, MailRecipient replyTo, IEnumerable cc, IEnumerable bcc, IEnumerable attachments = null, MailRecipient sender = null) + { + Assert.Equal("from@test.com", from.Email); + Assert.Null(from.Name); + await Task.CompletedTask; + }; + + var mailer = new CustomMailer( + null, // We aren't rendering anything, so it's null. + SendMailCustom, + new MailRecipient("global@test.com", "Global") + ); + + await mailer.SendAsync( + new GenericHtmlMailable() + .Subject("test") + .From("from@test.com") // Should override the global from. .To("to@test.com") .Html("test") ); @@ -66,7 +91,7 @@ await mailer.SendAsync( [Fact] public async Task CustomMailer_Render() { - async Task SendMailCustom(string message, string subject, IEnumerable to, MailRecipient from, MailRecipient replyTo, IEnumerable cc, IEnumerable bcc, IEnumerable attachments) + async Task SendMailCustom(string message, string subject, IEnumerable to, MailRecipient from, MailRecipient replyTo, IEnumerable cc, IEnumerable bcc, IEnumerable attachments = null, MailRecipient sender = null) { await Task.CompletedTask; }; @@ -92,7 +117,7 @@ async Task SendMailCustom(string message, string subject, IEnumerable to, MailRecipient from, MailRecipient replyTo, IEnumerable cc, IEnumerable bcc, IEnumerable attachments) + async Task SendMailCustom(string message, string subject, IEnumerable to, MailRecipient from, MailRecipient replyTo, IEnumerable cc, IEnumerable bcc, IEnumerable attachments = null, MailRecipient sender = null) { Assert.Equal(2, attachments.Count()); Assert.Equal("Attachment 2", attachments.Skip(1).Single().Name);