MailKit conversion#78
Conversation
nblumhardt
left a comment
There was a problem hiding this comment.
Looks great; SmtpOptions is a nice improvement. Had some thoughts and queries inline. Cheers!
| var client = new SmtpClient(Host, Port ?? 25) {EnableSsl = EnableSsl ?? false}; | ||
| if (!string.IsNullOrWhiteSpace(Username)) | ||
| client.Credentials = new NetworkCredential(Username, Password); | ||
| var result = await _mailGateway.Send(_client, _options, new MimeMessage(new List<InternetAddress> {MailboxAddress.Parse(From)}, |
There was a problem hiding this comment.
Since result is unused, we'll no longer get errors logged to the app's diagnostic stream when sending fails. Perhaps until we've looked into the fallback server idea, we should just keep throwing exceptions on failure, here?
There was a problem hiding this comment.
Agree. I was planning to log errors as part of this but didn't implement a throw in the interim
There was a problem hiding this comment.
Updated - if !result.Success, we throw result.Errors.
| client.Credentials = new NetworkCredential(Username, Password); | ||
| var result = await _mailGateway.Send(_client, _options, new MimeMessage(new List<InternetAddress> {MailboxAddress.Parse(From)}, | ||
| new List<InternetAddress> {MailboxAddress.Parse(to)}, subject, | ||
| (new BodyBuilder() {HtmlBody = body}).ToMessageBody())); |
There was a problem hiding this comment.
Looks like we no longer set credentials for the outgoing mail server
There was a problem hiding this comment.
@nblumhardt We do, but it's in the SmtpOptions that are set during OnAttached.
There was a problem hiding this comment.
It's set on _options, but never passed to the client.
There was a problem hiding this comment.
Well, crap. Will fix.
There was a problem hiding this comment.
Missing code found down the back of the sofa
| { "$ServerUri", host.BaseUri } | ||
| { "$ServerUri", host.BaseUri }, | ||
| // Note, this will only be valid when events are streamed directly to the app, and not when the app is sending an alert notification. | ||
| { "$EventUri", string.Concat(host.BaseUri, "#/events?filter=@Id%20%3D%20'", evt.Id, "'&show=expanded") } |
| public bool UseSsl { get; set; } = false; | ||
| public bool RequiresAuthentication { get; set; } = false; | ||
| public string PreferredEncoding { get; set; } = string.Empty; | ||
| public SecureSocketOptions? SocketOptions { get; set; } |
There was a problem hiding this comment.
SocketOptions and UseSsl overlap, here. Perhaps we knock out UseSsl, make SocketOptions non-nullable, and set it to SslOnConnect when using SSL, or StartTlsWhenAvailable otherwise? (This is how MailKit interprets Boolean useSsl behind the scenes.
There was a problem hiding this comment.
@nblumhardt I think you're right. I'll take a look.
There was a problem hiding this comment.
SocketOptions is now the only way to fly
| public string Password { get; set; } = string.Empty; | ||
| public bool UseSsl { get; set; } = false; | ||
| public bool RequiresAuthentication { get; set; } = false; | ||
| public string PreferredEncoding { get; set; } = string.Empty; |
There was a problem hiding this comment.
To avoid confusion and an accumulation of unimplemented bits and pieces, it's probably best to remove the unused properties here until we've worked through the features they are used by
There was a problem hiding this comment.
Removed the unused bitses
|
|
||
| [Fact] | ||
| public void ToAddressesAreTemplated() | ||
| public async void ToAddressesAreTemplated() |
There was a problem hiding this comment.
Should return async Task here, or xUnit can't track completion
There was a problem hiding this comment.
Now returning async Task.
| client.Send(message); | ||
| try | ||
| { | ||
| await client.ConnectAsync(options.Server, options.Port, options.UseSsl); |
There was a problem hiding this comment.
Since we connect and disconnect here, any reason not to just create and release a new SmtpClient each time?
There was a problem hiding this comment.
Yeah agree. MailKit initialises quite quickly with minimal overhead and is Async to boot, so that shouldn't be a problem. There are some cases where it's more efficient to hold a single instance of a client (not MailKit) to reduce initialisation overhead, but this was a change before I really got deep into the conversion and proved unnecessary.
There was a problem hiding this comment.
Now implementing SmtpClient each time. This is now redundant in EmailApp and have removed it from IMailGateway/DirectMailGateway/etc
| readonly ConcurrentDictionary<uint, DateTime> _lastSeen = new ConcurrentDictionary<uint, DateTime>(); | ||
| readonly Lazy<Template> _bodyTemplate, _subjectTemplate, _toAddressesTemplate; | ||
| private SmtpClient _client = new SmtpClient(); | ||
| private SmtpOptions _options = new SmtpOptions(); |
There was a problem hiding this comment.
Missed these - should be readonly, no explicit private
There was a problem hiding this comment.
Done - no need for OnAttached now.
|
@nblumhardt Might need a little bit more work to ensure that the to address parse doesn't fail for comma-separated strings. I was working through mailhost fallbacks and possibly DNS delivery so will fix as part of that. |
|
@nblumhardt This will need a bit of battle testing to make sure fallbacks and DNS delivery work as expected, but I'm quietly optimistic. |
|
@nblumhardt Okay, I've now successfully run through unit tests for delivering via mailhost and delivering via DNS, and made corrections based on the result. I left the test cases that I used commented out for your benefit, although obviously they'll be removed. In any case - the code is working for both scenarios 😀 |
|
@nblumhardt Reviewed the rest of the ideas from #77 and pulled in the following enhancements;
Rejected or parked:
This should make Email Plus more fully featured without overburdening it with unnecessary drudge. |
|
Thanks Matt! Just a heads up that we're deep in some feature work this week and may not emerge for a few more days yet - will get some 👀 back on this ASAP 😅 RE how we shape up PRs - it's often much faster to discuss/review/merge small PRs one at a time; adding features to one large PR can stall the process sometimes. If you're able to force push the older commit with just the mailkit conversion, but send the remaining commits in a follow-up PR, that would be optimal. Can help to figure out a plan next week, either way. Have a great weekend! |
|
@nblumhardt Fair enough on all points 😊 Will see what I can do. Have a great weekend yourselves! |
|
Closing PR in favour of #79 ... will send the remaining commits in a follow-up PR as discussed. |
Hey @nblumhardt,
This is the initial MailKit conversion that we discussed in #77 (I removed the SSL validation suppression option that I'd implemented).
I noted that we can make use of async methods for MailKit so I've updated EmailPlus to use onasync, and provided opportunity to handle unsuccessful emails in future (eg. the fallback mailhost idea I mentioned) by implementing a MailResult class.
I used a couple of idea from FluentEmail wrt an 'Options' class (SmtpOptions) and left a couple of unused properties that would likely be used for some of the ideas from #77.
I also ported over the $EventUri template that we added to Seq.App.Opsgenie as a nicety, and updated Handlebars.NET to the latest build (from v2.0.8 to v2.0.9).
As I was doing this I could envisage how we would go about implementing a number of the ideas from my list, but as agreed, I started with MailKit 😁
Cheers,
Matt