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 00000000..028229b3 Binary files /dev/null and b/Src/Coravel.Mailer/logo.png differ 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);