7b0b9c7365
Solution + 23 src projects + 26 test projects renamed; folders, csproj, namespaces, and ScadaLinkDbContext/ScadaBridgeDbContext class updated. ActorSystem "scadalink" → "scadabridge", Akka seed-node URLs migrated. SQL roles/logins, LDAP domains, CLI command name, and CLI config dir (~/.scadalink → ~/.scadabridge) also renamed. Build green; 5 Host.Tests fail awaiting SQL login rename in next commit. Pre-existing StaleTagMonitor timing flakes unchanged. Rename script committed at tools/rename-to-scadabridge.sh.
96 lines
3.5 KiB
C#
96 lines
3.5 KiB
C#
using System.Net.Sockets;
|
|
using MailKit;
|
|
using MailKit.Net.Smtp;
|
|
|
|
namespace ZB.MOM.WW.ScadaBridge.NotificationService;
|
|
|
|
/// <summary>
|
|
/// NS-002/NS-003: The classification of an SMTP delivery failure. This decides
|
|
/// whether a failure is retried or surfaced to the caller, so it is part of the
|
|
/// system's correctness-relevant behaviour.
|
|
/// </summary>
|
|
public enum SmtpErrorClass
|
|
{
|
|
/// <summary>Cancellation or an unrecognised exception — caller decides.</summary>
|
|
Unknown,
|
|
|
|
/// <summary>Retryable failure (4xx, connection/socket/protocol error, timeout).</summary>
|
|
Transient,
|
|
|
|
/// <summary>Non-retryable failure (5xx) — must not be retried.</summary>
|
|
Permanent,
|
|
}
|
|
|
|
/// <summary>
|
|
/// NS-002/NS-003: Classifies an SMTP failure using MailKit's typed exceptions and
|
|
/// the numeric <see cref="SmtpStatusCode"/> rather than locale-dependent substring
|
|
/// matching on the exception message.
|
|
/// <para>
|
|
/// Public and shared: the central Notification Outbox's <c>EmailNotificationDeliveryAdapter</c>
|
|
/// routes every SMTP failure through this single policy. (NS-019: the orphaned site-side
|
|
/// <c>NotificationDeliveryService</c> that previously co-used this classifier was removed
|
|
/// when sites stopped delivering notifications.)
|
|
/// </para>
|
|
/// </summary>
|
|
public static class SmtpErrorClassifier
|
|
{
|
|
/// <summary>
|
|
/// Classifies an SMTP failure. A cancellation requested by the caller is never
|
|
/// treated as a transient SMTP error.
|
|
/// </summary>
|
|
/// <param name="ex">The exception thrown by the SMTP send sequence.</param>
|
|
/// <param name="cancellationToken">
|
|
/// The token governing the send; a requested cancellation classifies as
|
|
/// <see cref="SmtpErrorClass.Unknown"/> so the caller can re-throw it.
|
|
/// </param>
|
|
public static SmtpErrorClass Classify(Exception ex, CancellationToken cancellationToken)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(ex);
|
|
|
|
// A deliberate cancellation is not an SMTP error at all.
|
|
if (ex is OperationCanceledException && cancellationToken.IsCancellationRequested)
|
|
{
|
|
return SmtpErrorClass.Unknown;
|
|
}
|
|
|
|
// MailKit reports SMTP command failures with the real status code; the
|
|
// SmtpStatusCode enum's underlying value is the numeric SMTP reply code.
|
|
if (ex is SmtpCommandException command)
|
|
{
|
|
var code = (int)command.StatusCode;
|
|
if (code >= 400 && code < 500)
|
|
{
|
|
return SmtpErrorClass.Transient;
|
|
}
|
|
|
|
if (code >= 500 && code < 600)
|
|
{
|
|
return SmtpErrorClass.Permanent;
|
|
}
|
|
|
|
return SmtpErrorClass.Unknown;
|
|
}
|
|
|
|
// Protocol errors, a dropped/unavailable service, socket failures and
|
|
// timeouts are all retryable — the message has not been rejected.
|
|
if (ex is SmtpProtocolException
|
|
or ServiceNotConnectedException
|
|
or SocketException
|
|
or TimeoutException)
|
|
{
|
|
return SmtpErrorClass.Transient;
|
|
}
|
|
|
|
return SmtpErrorClass.Unknown;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convenience predicate: true when <see cref="Classify"/> returns
|
|
/// <see cref="SmtpErrorClass.Transient"/>.
|
|
/// </summary>
|
|
/// <param name="ex">The exception to classify.</param>
|
|
/// <param name="cancellationToken">Cancellation token passed to <see cref="Classify"/>.</param>
|
|
public static bool IsTransient(Exception ex, CancellationToken cancellationToken)
|
|
=> Classify(ex, cancellationToken) == SmtpErrorClass.Transient;
|
|
}
|