up
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Findings Ledger CI / build-test (push) Has been cancelled
Findings Ledger CI / migration-validation (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
cryptopro-linux-csp / build-and-test (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
sm-remote-ci / build-and-test (push) Has been cancelled
Findings Ledger CI / generate-manifest (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Findings Ledger CI / build-test (push) Has been cancelled
Findings Ledger CI / migration-validation (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
cryptopro-linux-csp / build-and-test (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
sm-remote-ci / build-and-test (push) Has been cancelled
Findings Ledger CI / generate-manifest (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
This commit is contained in:
106
src/Signals/StellaOps.Signals/Services/RouterEventsPublisher.cs
Normal file
106
src/Signals/StellaOps.Signals/Services/RouterEventsPublisher.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Signals.Models;
|
||||
using StellaOps.Signals.Options;
|
||||
|
||||
namespace StellaOps.Signals.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Router-backed publisher placeholder. Emits envelopes to log until router event channel is provisioned.
|
||||
/// </summary>
|
||||
internal sealed class RouterEventsPublisher : IEventsPublisher
|
||||
{
|
||||
private readonly ReachabilityFactEventBuilder eventBuilder;
|
||||
private readonly SignalsOptions options;
|
||||
private readonly HttpClient httpClient;
|
||||
private readonly ILogger<RouterEventsPublisher> logger;
|
||||
private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web)
|
||||
{
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
WriteIndented = false
|
||||
};
|
||||
|
||||
public RouterEventsPublisher(
|
||||
ReachabilityFactEventBuilder eventBuilder,
|
||||
SignalsOptions options,
|
||||
HttpClient httpClient,
|
||||
ILogger<RouterEventsPublisher> logger)
|
||||
{
|
||||
this.eventBuilder = eventBuilder ?? throw new ArgumentNullException(nameof(eventBuilder));
|
||||
this.options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
this.httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public async Task PublishFactUpdatedAsync(ReachabilityFactDocument fact, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(fact);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var envelope = eventBuilder.Build(fact);
|
||||
|
||||
var json = JsonSerializer.Serialize(envelope, SerializerOptions);
|
||||
|
||||
try
|
||||
{
|
||||
using var request = new HttpRequestMessage(HttpMethod.Post, options.Events.Router.Path);
|
||||
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
request.Headers.TryAddWithoutValidation("X-Signals-Topic", envelope.Topic);
|
||||
request.Headers.TryAddWithoutValidation("X-Signals-Tenant", envelope.Tenant);
|
||||
request.Headers.TryAddWithoutValidation("X-Signals-Pipeline", options.Events.Pipeline);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(options.Events.Router.ApiKey))
|
||||
{
|
||||
request.Headers.TryAddWithoutValidation(
|
||||
string.IsNullOrWhiteSpace(options.Events.Router.ApiKeyHeader)
|
||||
? "X-API-Key"
|
||||
: options.Events.Router.ApiKeyHeader,
|
||||
options.Events.Router.ApiKey);
|
||||
}
|
||||
|
||||
foreach (var header in options.Events.Router.Headers)
|
||||
{
|
||||
request.Headers.TryAddWithoutValidation(header.Key, header.Value);
|
||||
}
|
||||
|
||||
using var response = await httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var body = response.Content is null
|
||||
? string.Empty
|
||||
: await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
|
||||
logger.LogError(
|
||||
"Router publish failed for {Topic} with status {StatusCode}: {Body}",
|
||||
envelope.Topic,
|
||||
(int)response.StatusCode,
|
||||
Truncate(body, 256));
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogInformation(
|
||||
"Router publish succeeded for {Topic} ({StatusCode})",
|
||||
envelope.Topic,
|
||||
(int)response.StatusCode);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (ex is not OperationCanceledException)
|
||||
{
|
||||
logger.LogError(ex, "Router publish failed for {Topic}", envelope.Topic);
|
||||
}
|
||||
}
|
||||
|
||||
private static string Truncate(string value, int maxLength)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value) || value.Length <= maxLength)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
return value[..maxLength];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user