From d21b7a5a48f381f999fbffd2c3a09037d93764fa Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 10 Aug 2026 06:07:14 -0400 Subject: [PATCH] feat(notifications): EmailTransport enum + parser for the EWS transport discriminator --- .../EmailTransport.cs | 50 +++++++++++++++++++ .../EmailTransportParserTests.cs | 40 +++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 src/ZB.MOM.WW.ScadaBridge.NotificationService/EmailTransport.cs create mode 100644 tests/ZB.MOM.WW.ScadaBridge.NotificationService.Tests/EmailTransportParserTests.cs diff --git a/src/ZB.MOM.WW.ScadaBridge.NotificationService/EmailTransport.cs b/src/ZB.MOM.WW.ScadaBridge.NotificationService/EmailTransport.cs new file mode 100644 index 00000000..6b78a15f --- /dev/null +++ b/src/ZB.MOM.WW.ScadaBridge.NotificationService/EmailTransport.cs @@ -0,0 +1,50 @@ +namespace ZB.MOM.WW.ScadaBridge.NotificationService; + +/// +/// The email delivery transports the service supports. The configured +/// SmtpConfiguration.Transport string is parsed into this enum to choose +/// which client actually submits the message. +/// +public enum EmailTransport +{ + /// Direct SMTP submission via MailKit (the original path). + Smtp, + + /// On-prem Exchange Web Services — one CreateItem SOAP call over HTTPS. + Ews, +} + +/// +/// Parses the free-text SmtpConfiguration.Transport value into an +/// , rejecting unknown values rather than silently +/// falling back to SMTP. +/// +public static class EmailTransportParser +{ + /// + /// Parses a configured transport string. A null or empty value defaults to + /// — every pre-EWS configuration row stores no + /// transport and is an SMTP row. An unknown value is a configuration error that + /// retrying cannot fix, so it throws and the delivery adapter surfaces it as a + /// permanent failure (mirrors ). + /// + /// The transport string to parse (Smtp or Ews); null/empty defaults to Smtp. + /// The value is not one of Smtp/Ews. + /// The corresponding enum value. + public static EmailTransport Parse(string? transport) + { + if (string.IsNullOrWhiteSpace(transport)) + { + return EmailTransport.Smtp; + } + + return transport.Trim().ToLowerInvariant() switch + { + "smtp" => EmailTransport.Smtp, + "ews" => EmailTransport.Ews, + _ => throw new ArgumentException( + $"Unknown email transport '{transport}'. Expected one of: Smtp, Ews.", + nameof(transport)), + }; + } +} diff --git a/tests/ZB.MOM.WW.ScadaBridge.NotificationService.Tests/EmailTransportParserTests.cs b/tests/ZB.MOM.WW.ScadaBridge.NotificationService.Tests/EmailTransportParserTests.cs new file mode 100644 index 00000000..e19cc33f --- /dev/null +++ b/tests/ZB.MOM.WW.ScadaBridge.NotificationService.Tests/EmailTransportParserTests.cs @@ -0,0 +1,40 @@ +namespace ZB.MOM.WW.ScadaBridge.NotificationService.Tests; + +/// +/// Tests for parsing the configured email transport discriminator into the two-state enum. +/// +public class EmailTransportParserTests +{ + [Theory] + [InlineData("Smtp", EmailTransport.Smtp)] + [InlineData("smtp", EmailTransport.Smtp)] + [InlineData("SMTP", EmailTransport.Smtp)] + [InlineData("Ews", EmailTransport.Ews)] + [InlineData("ews", EmailTransport.Ews)] + [InlineData("EWS", EmailTransport.Ews)] + [InlineData(" ews ", EmailTransport.Ews)] + public void Parse_KnownTransports_ReturnsExpected(string input, EmailTransport expected) + { + Assert.Equal(expected, EmailTransportParser.Parse(input)); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + public void Parse_NullOrEmpty_DefaultsToSmtp(string? input) + { + // Every pre-EWS SmtpConfiguration row stores no transport; those rows are SMTP rows. + Assert.Equal(EmailTransport.Smtp, EmailTransportParser.Parse(input)); + } + + [Theory] + [InlineData("Graph")] + [InlineData("smtps")] + public void Parse_UnknownTransport_Throws(string input) + { + // An unknown transport must be rejected, not silently treated as SMTP. + var ex = Assert.Throws(() => EmailTransportParser.Parse(input)); + Assert.Contains(input, ex.Message); + } +}