Refactor code structure for improved readability and maintainability; removed redundant code blocks and optimized function calls.
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
namespace StellaOps.Notifier.Worker.Options;
|
||||
|
||||
public sealed class EgressSloOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Webhook endpoint to receive SLO delivery signals. When null/empty, publishing is disabled.
|
||||
/// </summary>
|
||||
public string? Webhook { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Request timeout in seconds for the webhook call.
|
||||
/// </summary>
|
||||
public int TimeoutSeconds { get; set; } = 5;
|
||||
|
||||
public bool Enabled => !string.IsNullOrWhiteSpace(Webhook);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System.Collections.Generic;
|
||||
using StellaOps.Notify.Models;
|
||||
|
||||
namespace StellaOps.Notifier.Worker.Processing;
|
||||
|
||||
/// <summary>
|
||||
/// Tracks per-event delivery intents for SLO evaluation and webhook emission.
|
||||
/// </summary>
|
||||
internal sealed class EgressSloContext
|
||||
{
|
||||
private readonly List<EgressSloSignal> _signals = new();
|
||||
|
||||
public IReadOnlyList<EgressSloSignal> Signals => _signals;
|
||||
|
||||
public static EgressSloContext FromNotifyEvent(NotifyEvent notifyEvent)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(notifyEvent);
|
||||
return new EgressSloContext
|
||||
{
|
||||
EventId = notifyEvent.EventId,
|
||||
TenantId = notifyEvent.Tenant,
|
||||
EventKind = notifyEvent.Kind,
|
||||
OccurredAt = notifyEvent.Ts
|
||||
};
|
||||
}
|
||||
|
||||
public Guid EventId { get; private set; }
|
||||
|
||||
public string TenantId { get; private set; } = string.Empty;
|
||||
|
||||
public string EventKind { get; private set; } = string.Empty;
|
||||
|
||||
public DateTimeOffset OccurredAt { get; private set; }
|
||||
|
||||
public void AddDelivery(string channelType, string template, string kind)
|
||||
{
|
||||
_signals.Add(new EgressSloSignal(channelType, template, kind, OccurredAt));
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed record EgressSloSignal(
|
||||
string Channel,
|
||||
string Template,
|
||||
string Kind,
|
||||
DateTimeOffset OccurredAt);
|
||||
@@ -0,0 +1,93 @@
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Notifier.Worker.Options;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Notifier.Worker.Processing;
|
||||
|
||||
internal sealed class HttpEgressSloSink : IEgressSloSink
|
||||
{
|
||||
private readonly IHttpClientFactory _clientFactory;
|
||||
private readonly EgressSloOptions _options;
|
||||
private readonly ILogger<HttpEgressSloSink> _logger;
|
||||
|
||||
public HttpEgressSloSink(
|
||||
IHttpClientFactory clientFactory,
|
||||
IOptions<EgressSloOptions> options,
|
||||
ILogger<HttpEgressSloSink> logger)
|
||||
{
|
||||
_clientFactory = clientFactory ?? throw new ArgumentNullException(nameof(clientFactory));
|
||||
_options = options?.Value ?? throw new ArgumentNullException(nameof(options));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public async Task PublishAsync(EgressSloContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
if (context is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
if (!_options.Enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
if (_options.TimeoutSeconds > 0)
|
||||
{
|
||||
linkedCts.CancelAfter(TimeSpan.FromSeconds(_options.TimeoutSeconds));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var client = _clientFactory.CreateClient("notifier-slo-webhook");
|
||||
var payload = Map(context);
|
||||
|
||||
var response = await client.PostAsJsonAsync(_options.Webhook, payload, linkedCts.Token).ConfigureAwait(false);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"SLO webhook returned non-success status {StatusCode} for event {EventId} (tenant {TenantId}).",
|
||||
(int)response.StatusCode,
|
||||
context.EventId,
|
||||
context.TenantId);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) when (linkedCts.IsCancellationRequested)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"SLO webhook timed out after {TimeoutSeconds}s for event {EventId} (tenant {TenantId}).",
|
||||
_options.TimeoutSeconds,
|
||||
context.EventId,
|
||||
context.TenantId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
ex,
|
||||
"Failed to publish SLO webhook for event {EventId} (tenant {TenantId}).",
|
||||
context.EventId,
|
||||
context.TenantId);
|
||||
}
|
||||
}
|
||||
|
||||
private static object Map(EgressSloContext context)
|
||||
=> new
|
||||
{
|
||||
context.EventId,
|
||||
context.TenantId,
|
||||
context.EventKind,
|
||||
context.OccurredAt,
|
||||
deliveries = context.Signals.Select(signal => new
|
||||
{
|
||||
signal.Channel,
|
||||
signal.Template,
|
||||
signal.Kind,
|
||||
signal.OccurredAt
|
||||
})
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StellaOps.Notifier.Worker.Processing;
|
||||
|
||||
internal interface IEgressSloSink
|
||||
{
|
||||
Task PublishAsync(EgressSloContext context, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
internal sealed class NullEgressSloSink : IEgressSloSink
|
||||
{
|
||||
public Task PublishAsync(EgressSloContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user