up
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Messaging;
|
||||
using StellaOps.Messaging.Abstractions;
|
||||
using StellaOps.Scheduler.WebService.Options;
|
||||
|
||||
namespace StellaOps.Scheduler.WebService.GraphJobs.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Transport-agnostic implementation of <see cref="IGraphJobCompletionPublisher"/> using StellaOps.Messaging abstractions.
|
||||
/// Works with any configured transport (Valkey, PostgreSQL, InMemory).
|
||||
/// </summary>
|
||||
internal sealed class MessagingGraphJobEventPublisher : IGraphJobCompletionPublisher
|
||||
{
|
||||
private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web)
|
||||
{
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
||||
};
|
||||
|
||||
private readonly IOptionsMonitor<SchedulerEventsOptions> _options;
|
||||
private readonly IEventStream<GraphJobCompletedEvent> _eventStream;
|
||||
private readonly ILogger<MessagingGraphJobEventPublisher> _logger;
|
||||
|
||||
public MessagingGraphJobEventPublisher(
|
||||
IOptionsMonitor<SchedulerEventsOptions> options,
|
||||
IEventStreamFactory eventStreamFactory,
|
||||
ILogger<MessagingGraphJobEventPublisher> logger)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
ArgumentNullException.ThrowIfNull(eventStreamFactory);
|
||||
|
||||
_options = options;
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
var eventsOptions = options.CurrentValue?.GraphJobs ?? new GraphJobEventsOptions();
|
||||
var streamKey = string.IsNullOrWhiteSpace(eventsOptions.Stream) ? "stella.events" : eventsOptions.Stream;
|
||||
var maxStreamLength = eventsOptions.MaxStreamLength > 0 ? eventsOptions.MaxStreamLength : (long?)null;
|
||||
|
||||
_eventStream = eventStreamFactory.Create<GraphJobCompletedEvent>(new EventStreamOptions
|
||||
{
|
||||
StreamName = streamKey,
|
||||
MaxLength = maxStreamLength,
|
||||
ApproximateTrimming = true,
|
||||
});
|
||||
|
||||
_logger.LogInformation("Initialized messaging graph job event publisher for stream {Stream}.", streamKey);
|
||||
}
|
||||
|
||||
public async Task PublishAsync(GraphJobCompletionNotification notification, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(notification);
|
||||
|
||||
var options = _options.CurrentValue?.GraphJobs ?? new GraphJobEventsOptions();
|
||||
if (!options.Enabled)
|
||||
{
|
||||
_logger.LogDebug("Graph job events disabled; skipping emission for {JobId}.", notification.Job.Id);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var envelope = GraphJobEventFactory.Create(notification);
|
||||
|
||||
var publishOptions = new EventPublishOptions
|
||||
{
|
||||
TenantId = envelope.Tenant,
|
||||
MaxStreamLength = options.MaxStreamLength > 0 ? options.MaxStreamLength : null,
|
||||
Headers = new Dictionary<string, string>
|
||||
{
|
||||
["kind"] = envelope.Kind,
|
||||
["occurredAt"] = envelope.Timestamp.ToString("O"),
|
||||
["jobId"] = notification.Job.Id,
|
||||
["status"] = notification.Status.ToString()
|
||||
}
|
||||
};
|
||||
|
||||
var publishTask = _eventStream.PublishAsync(envelope, publishOptions, cancellationToken);
|
||||
|
||||
if (options.PublishTimeoutSeconds > 0)
|
||||
{
|
||||
var timeout = TimeSpan.FromSeconds(options.PublishTimeoutSeconds);
|
||||
await publishTask.AsTask().WaitAsync(timeout, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await publishTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
_logger.LogDebug("Published graph job event {JobId} to stream.", notification.Job.Id);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to publish graph job completion for {JobId}; logging payload instead.", notification.Job.Id);
|
||||
LogEnvelope(notification);
|
||||
}
|
||||
}
|
||||
|
||||
private void LogEnvelope(GraphJobCompletionNotification notification)
|
||||
{
|
||||
var envelope = GraphJobEventFactory.Create(notification);
|
||||
var json = JsonSerializer.Serialize(envelope, SerializerOptions);
|
||||
_logger.LogInformation("{EventJson}", json);
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,9 @@
|
||||
<ProjectReference Include="../../__Libraries/StellaOps.Plugin/StellaOps.Plugin.csproj" />
|
||||
<ProjectReference Include="../../Authority/StellaOps.Authority/StellaOps.Auth.Abstractions/StellaOps.Auth.Abstractions.csproj" />
|
||||
<ProjectReference Include="../../Authority/StellaOps.Authority/StellaOps.Auth.ServerIntegration/StellaOps.Auth.ServerIntegration.csproj" />
|
||||
<ProjectReference Include="../../__Libraries/StellaOps.Messaging/StellaOps.Messaging.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.8.24" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.8.37" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace StellaOps.Scheduler.WebService.VulnerabilityResolverJobs;
|
||||
|
||||
/// <summary>
|
||||
/// Lightweight in-memory resolver job service to satisfy API contract and rate-limit callers.
|
||||
/// Suitable for stub/air-gap scenarios; replace with Mongo-backed implementation when ready.
|
||||
/// Suitable for stub/air-gap scenarios; replace with PostgreSQL-backed implementation when ready.
|
||||
/// </summary>
|
||||
public sealed class InMemoryResolverJobService : IResolverJobService
|
||||
{
|
||||
|
||||
@@ -102,7 +102,7 @@ internal sealed class BackfillRunner
|
||||
{
|
||||
Console.WriteLine($"Postgres graph job backfill starting (dry-run={_options.DryRun})");
|
||||
|
||||
// Placeholder: actual copy logic would map legacy Mongo export to new Postgres graph_jobs rows.
|
||||
// Placeholder: actual copy logic would map legacy export to new Postgres graph_jobs rows.
|
||||
if (_options.DryRun)
|
||||
{
|
||||
Console.WriteLine("Dry run: no changes applied.");
|
||||
|
||||
@@ -4,7 +4,7 @@ using System.Text.Json.Serialization;
|
||||
namespace StellaOps.Scheduler.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Scheduler configuration entity persisted in Mongo.
|
||||
/// Scheduler configuration entity persisted in storage.
|
||||
/// </summary>
|
||||
public sealed record Schedule
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.8.24" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.8.37" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.0" />
|
||||
|
||||
Reference in New Issue
Block a user