up
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
api-governance / spectral-lint (push) Has been cancelled
oas-ci / oas-validate (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Policy Simulation / policy-simulate (push) Has been cancelled
SDK Publish & Sign / sdk-publish (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
api-governance / spectral-lint (push) Has been cancelled
oas-ci / oas-validate (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Policy Simulation / policy-simulate (push) Has been cancelled
SDK Publish & Sign / sdk-publish (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
using StellaOps.Notify.Models;
|
||||
|
||||
namespace StellaOps.Notifier.Worker.Channels;
|
||||
|
||||
/// <summary>
|
||||
/// Contract implemented by channel adapters to dispatch notifications.
|
||||
/// </summary>
|
||||
public interface IChannelAdapter
|
||||
{
|
||||
/// <summary>
|
||||
/// Channel type handled by this adapter.
|
||||
/// </summary>
|
||||
NotifyChannelType ChannelType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches a notification delivery through the channel.
|
||||
/// </summary>
|
||||
Task<ChannelDispatchResult> DispatchAsync(
|
||||
ChannelDispatchContext context,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Checks channel health/connectivity.
|
||||
/// </summary>
|
||||
Task<ChannelHealthCheckResult> CheckHealthAsync(
|
||||
NotifyChannel channel,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Context for dispatching a notification through a channel.
|
||||
/// </summary>
|
||||
public sealed record ChannelDispatchContext(
|
||||
string DeliveryId,
|
||||
string TenantId,
|
||||
NotifyChannel Channel,
|
||||
NotifyDelivery Delivery,
|
||||
string RenderedBody,
|
||||
string? Subject,
|
||||
IReadOnlyDictionary<string, string> Metadata,
|
||||
DateTimeOffset Timestamp,
|
||||
string TraceId);
|
||||
|
||||
/// <summary>
|
||||
/// Result of a channel dispatch attempt.
|
||||
/// </summary>
|
||||
public sealed record ChannelDispatchResult
|
||||
{
|
||||
public required bool Success { get; init; }
|
||||
public required ChannelDispatchStatus Status { get; init; }
|
||||
public string? Message { get; init; }
|
||||
public string? ExternalId { get; init; }
|
||||
public int? HttpStatusCode { get; init; }
|
||||
public TimeSpan? Duration { get; init; }
|
||||
public IReadOnlyDictionary<string, string> Metadata { get; init; } = new Dictionary<string, string>();
|
||||
public Exception? Exception { get; init; }
|
||||
|
||||
public static ChannelDispatchResult Succeeded(
|
||||
string? externalId = null,
|
||||
string? message = null,
|
||||
TimeSpan? duration = null,
|
||||
IReadOnlyDictionary<string, string>? metadata = null) => new()
|
||||
{
|
||||
Success = true,
|
||||
Status = ChannelDispatchStatus.Sent,
|
||||
ExternalId = externalId,
|
||||
Message = message ?? "Delivery dispatched successfully.",
|
||||
Duration = duration,
|
||||
Metadata = metadata ?? new Dictionary<string, string>()
|
||||
};
|
||||
|
||||
public static ChannelDispatchResult Failed(
|
||||
string message,
|
||||
ChannelDispatchStatus status = ChannelDispatchStatus.Failed,
|
||||
int? httpStatusCode = null,
|
||||
Exception? exception = null,
|
||||
TimeSpan? duration = null,
|
||||
IReadOnlyDictionary<string, string>? metadata = null) => new()
|
||||
{
|
||||
Success = false,
|
||||
Status = status,
|
||||
Message = message,
|
||||
HttpStatusCode = httpStatusCode,
|
||||
Exception = exception,
|
||||
Duration = duration,
|
||||
Metadata = metadata ?? new Dictionary<string, string>()
|
||||
};
|
||||
|
||||
public static ChannelDispatchResult Throttled(
|
||||
string message,
|
||||
TimeSpan? retryAfter = null,
|
||||
IReadOnlyDictionary<string, string>? metadata = null)
|
||||
{
|
||||
var meta = metadata is not null
|
||||
? new Dictionary<string, string>(metadata)
|
||||
: new Dictionary<string, string>();
|
||||
|
||||
if (retryAfter.HasValue)
|
||||
{
|
||||
meta["retryAfterSeconds"] = retryAfter.Value.TotalSeconds.ToString("F0");
|
||||
}
|
||||
|
||||
return new()
|
||||
{
|
||||
Success = false,
|
||||
Status = ChannelDispatchStatus.Throttled,
|
||||
Message = message,
|
||||
HttpStatusCode = 429,
|
||||
Metadata = meta
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispatch attempt status.
|
||||
/// </summary>
|
||||
public enum ChannelDispatchStatus
|
||||
{
|
||||
Sent,
|
||||
Failed,
|
||||
Throttled,
|
||||
InvalidConfiguration,
|
||||
Timeout,
|
||||
NetworkError
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Result of a channel health check.
|
||||
/// </summary>
|
||||
public sealed record ChannelHealthCheckResult
|
||||
{
|
||||
public required bool Healthy { get; init; }
|
||||
public required string Status { get; init; }
|
||||
public string? Message { get; init; }
|
||||
public TimeSpan? Latency { get; init; }
|
||||
public IReadOnlyDictionary<string, string> Metadata { get; init; } = new Dictionary<string, string>();
|
||||
|
||||
public static ChannelHealthCheckResult Ok(string? message = null, TimeSpan? latency = null) => new()
|
||||
{
|
||||
Healthy = true,
|
||||
Status = "healthy",
|
||||
Message = message ?? "Channel is operational.",
|
||||
Latency = latency
|
||||
};
|
||||
|
||||
public static ChannelHealthCheckResult Degraded(string message, TimeSpan? latency = null) => new()
|
||||
{
|
||||
Healthy = true,
|
||||
Status = "degraded",
|
||||
Message = message,
|
||||
Latency = latency
|
||||
};
|
||||
|
||||
public static ChannelHealthCheckResult Unhealthy(string message) => new()
|
||||
{
|
||||
Healthy = false,
|
||||
Status = "unhealthy",
|
||||
Message = message
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user