From cd075ee08bcbe17cecd06f709c0b22f00da9091c Mon Sep 17 00:00:00 2001 From: master <> Date: Wed, 8 Apr 2026 16:27:43 +0300 Subject: [PATCH] fix: clean up worktree refs + remaining schema extraction + route fixes Co-Authored-By: Claude Opus 4.6 (1M context) --- .../Endpoints/AuditEndpoints.cs | 4 +- .../Endpoints/JobEngineLegacyEndpoints.cs | 7 +- .../Postgres/ReleaseOrchestratorDataSource.cs | 44 ++++++ .../Repositories/IAuditRepository.cs | 127 ++++++++++++++++++ .../Services/IFirstSignalService.cs | 50 +++++++ 5 files changed, 224 insertions(+), 8 deletions(-) create mode 100644 src/ReleaseOrchestrator/__Libraries/StellaOps.ReleaseOrchestrator.Persistence/Postgres/ReleaseOrchestratorDataSource.cs create mode 100644 src/ReleaseOrchestrator/__Libraries/StellaOps.ReleaseOrchestrator.Persistence/Repositories/IAuditRepository.cs create mode 100644 src/ReleaseOrchestrator/__Libraries/StellaOps.ReleaseOrchestrator.Persistence/Services/IFirstSignalService.cs diff --git a/src/ReleaseOrchestrator/__Apps/StellaOps.ReleaseOrchestrator.WebApi/Endpoints/AuditEndpoints.cs b/src/ReleaseOrchestrator/__Apps/StellaOps.ReleaseOrchestrator.WebApi/Endpoints/AuditEndpoints.cs index 93d10a0cb..faf7cc826 100644 --- a/src/ReleaseOrchestrator/__Apps/StellaOps.ReleaseOrchestrator.WebApi/Endpoints/AuditEndpoints.cs +++ b/src/ReleaseOrchestrator/__Apps/StellaOps.ReleaseOrchestrator.WebApi/Endpoints/AuditEndpoints.cs @@ -24,7 +24,7 @@ public static class AuditEndpoints .RequireTenant(); // List and get operations - group.MapGet(string.Empty, ListAuditEntries) + group.MapGet(string.Empty, ListAuditEntriesHandler) .WithName("ReleaseOrchestrator_ListAuditEntries") .WithDescription(_t("orchestrator.audit.list_description")); @@ -56,7 +56,7 @@ public static class AuditEndpoints return group; } - private static async Task ListAuditEntries( + internal static async Task ListAuditEntriesHandler( HttpContext context, [FromServices] TenantResolver tenantResolver, [FromServices] IAuditRepository repository, diff --git a/src/ReleaseOrchestrator/__Apps/StellaOps.ReleaseOrchestrator.WebApi/Endpoints/JobEngineLegacyEndpoints.cs b/src/ReleaseOrchestrator/__Apps/StellaOps.ReleaseOrchestrator.WebApi/Endpoints/JobEngineLegacyEndpoints.cs index 6af166eaa..babf6466d 100644 --- a/src/ReleaseOrchestrator/__Apps/StellaOps.ReleaseOrchestrator.WebApi/Endpoints/JobEngineLegacyEndpoints.cs +++ b/src/ReleaseOrchestrator/__Apps/StellaOps.ReleaseOrchestrator.WebApi/Endpoints/JobEngineLegacyEndpoints.cs @@ -1,9 +1,4 @@ -using Microsoft.AspNetCore.Mvc; using StellaOps.Auth.ServerIntegration.Tenancy; -using StellaOps.JobEngine.Core.Domain; -using StellaOps.JobEngine.Infrastructure.Repositories; -using StellaOps.ReleaseOrchestrator.WebApi.Contracts; -using StellaOps.ReleaseOrchestrator.WebApi.Services; namespace StellaOps.ReleaseOrchestrator.WebApi.Endpoints; @@ -42,7 +37,7 @@ public static class JobEngineLegacyEndpoints .RequireAuthorization(ReleaseOrchestratorPolicies.Read) .RequireTenant(); - group.MapGet("events", AuditEndpoints.ListAuditEventsLegacy); + group.MapGet("events", AuditEndpoints.ListAuditEntriesHandler); } // ----------------------------------------------------------------------- diff --git a/src/ReleaseOrchestrator/__Libraries/StellaOps.ReleaseOrchestrator.Persistence/Postgres/ReleaseOrchestratorDataSource.cs b/src/ReleaseOrchestrator/__Libraries/StellaOps.ReleaseOrchestrator.Persistence/Postgres/ReleaseOrchestratorDataSource.cs new file mode 100644 index 000000000..cdc6c8359 --- /dev/null +++ b/src/ReleaseOrchestrator/__Libraries/StellaOps.ReleaseOrchestrator.Persistence/Postgres/ReleaseOrchestratorDataSource.cs @@ -0,0 +1,44 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Npgsql; +using StellaOps.Infrastructure.Postgres.Connections; +using StellaOps.Infrastructure.Postgres.Options; + +namespace StellaOps.ReleaseOrchestrator.Persistence.Postgres; + +/// +/// PostgreSQL data source for the ReleaseOrchestrator module. +/// +public sealed class ReleaseOrchestratorDataSource : DataSourceBase +{ + /// + /// Default schema name for release-orchestrator tables. + /// + public const string DefaultSchemaName = "release_orchestrator"; + + /// + /// Creates a new ReleaseOrchestrator data source. + /// + public ReleaseOrchestratorDataSource(IOptions options, ILogger logger) + : base(CreateOptions(options.Value), logger) + { + } + + /// + protected override string ModuleName => "ReleaseOrchestrator"; + + /// + protected override void ConfigureDataSourceBuilder(NpgsqlDataSourceBuilder builder) + { + base.ConfigureDataSourceBuilder(builder); + } + + private static PostgresOptions CreateOptions(PostgresOptions baseOptions) + { + if (string.IsNullOrWhiteSpace(baseOptions.SchemaName)) + { + baseOptions.SchemaName = DefaultSchemaName; + } + return baseOptions; + } +} diff --git a/src/ReleaseOrchestrator/__Libraries/StellaOps.ReleaseOrchestrator.Persistence/Repositories/IAuditRepository.cs b/src/ReleaseOrchestrator/__Libraries/StellaOps.ReleaseOrchestrator.Persistence/Repositories/IAuditRepository.cs new file mode 100644 index 000000000..cce08b5ff --- /dev/null +++ b/src/ReleaseOrchestrator/__Libraries/StellaOps.ReleaseOrchestrator.Persistence/Repositories/IAuditRepository.cs @@ -0,0 +1,127 @@ +using StellaOps.ReleaseOrchestrator.Persistence.Domain; + +namespace StellaOps.ReleaseOrchestrator.Persistence.Repositories; + +/// +/// Repository for audit log entries. +/// +public interface IAuditRepository +{ + /// + /// Appends a new audit entry to the log. + /// + Task AppendAsync( + string tenantId, + AuditEventType eventType, + string resourceType, + Guid resourceId, + string actorId, + ActorType actorType, + string description, + string? oldState = null, + string? newState = null, + string? actorIp = null, + string? userAgent = null, + string? httpMethod = null, + string? requestPath = null, + string? correlationId = null, + string? metadata = null, + CancellationToken cancellationToken = default); + + /// + /// Gets an audit entry by ID. + /// + Task GetByIdAsync( + string tenantId, + Guid entryId, + CancellationToken cancellationToken = default); + + /// + /// Lists audit entries with optional filters. + /// + Task> ListAsync( + string tenantId, + AuditEventType? eventType = null, + string? resourceType = null, + Guid? resourceId = null, + string? actorId = null, + DateTimeOffset? startTime = null, + DateTimeOffset? endTime = null, + int limit = 100, + int offset = 0, + CancellationToken cancellationToken = default); + + /// + /// Gets audit entries by sequence range. + /// + Task> GetBySequenceRangeAsync( + string tenantId, + long startSequence, + long endSequence, + CancellationToken cancellationToken = default); + + /// + /// Gets the latest audit entry for a tenant. + /// + Task GetLatestAsync( + string tenantId, + CancellationToken cancellationToken = default); + + /// + /// Gets audit entries for a specific resource. + /// + Task> GetByResourceAsync( + string tenantId, + string resourceType, + Guid resourceId, + int limit = 100, + CancellationToken cancellationToken = default); + + /// + /// Gets the count of audit entries. + /// + Task GetCountAsync( + string tenantId, + AuditEventType? eventType = null, + DateTimeOffset? startTime = null, + DateTimeOffset? endTime = null, + CancellationToken cancellationToken = default); + + /// + /// Verifies the chain integrity for a range of entries. + /// + Task VerifyChainAsync( + string tenantId, + long? startSequence = null, + long? endSequence = null, + CancellationToken cancellationToken = default); + + /// + /// Gets audit summary statistics. + /// + Task GetSummaryAsync( + string tenantId, + DateTimeOffset? since = null, + CancellationToken cancellationToken = default); +} + +/// +/// Result of chain verification. +/// +public sealed record ChainVerificationResult( + bool IsValid, + Guid? InvalidEntryId, + long? InvalidSequence, + string? ErrorMessage); + +/// +/// Audit summary statistics. +/// +public sealed record AuditSummary( + long TotalEntries, + long EntriesSince, + long EventTypes, + long UniqueActors, + long UniqueResources, + DateTimeOffset? EarliestEntry, + DateTimeOffset? LatestEntry); diff --git a/src/ReleaseOrchestrator/__Libraries/StellaOps.ReleaseOrchestrator.Persistence/Services/IFirstSignalService.cs b/src/ReleaseOrchestrator/__Libraries/StellaOps.ReleaseOrchestrator.Persistence/Services/IFirstSignalService.cs new file mode 100644 index 000000000..8ee8266f2 --- /dev/null +++ b/src/ReleaseOrchestrator/__Libraries/StellaOps.ReleaseOrchestrator.Persistence/Services/IFirstSignalService.cs @@ -0,0 +1,50 @@ +using StellaOps.ReleaseOrchestrator.Persistence.Domain; + +namespace StellaOps.ReleaseOrchestrator.Persistence.Services; + +public interface IFirstSignalService +{ + /// + /// Gets the first signal for a run, checking cache first. + /// + Task GetFirstSignalAsync( + Guid runId, + string tenantId, + string? ifNoneMatch = null, + CancellationToken cancellationToken = default); + + /// + /// Updates the first signal snapshot for a run and invalidates any cached copies. + /// + Task UpdateSnapshotAsync( + Guid runId, + string tenantId, + FirstSignal signal, + CancellationToken cancellationToken = default); + + /// + /// Invalidates cached first signal for a run. + /// + Task InvalidateCacheAsync( + Guid runId, + string tenantId, + CancellationToken cancellationToken = default); +} + +public sealed record FirstSignalResult +{ + public required FirstSignalResultStatus Status { get; init; } + public FirstSignal? Signal { get; init; } + public string? ETag { get; init; } + public bool CacheHit { get; init; } + public string? Source { get; init; } +} + +public enum FirstSignalResultStatus +{ + Found, + NotModified, + NotFound, + NotAvailable, + Error +}