fix: clean up worktree refs + remaining schema extraction + route fixes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
master
2026-04-08 16:27:43 +03:00
parent 5d3e0d46b2
commit cd075ee08b
5 changed files with 224 additions and 8 deletions

View File

@@ -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<IResult> ListAuditEntries(
internal static async Task<IResult> ListAuditEntriesHandler(
HttpContext context,
[FromServices] TenantResolver tenantResolver,
[FromServices] IAuditRepository repository,

View File

@@ -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);
}
// -----------------------------------------------------------------------

View File

@@ -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;
/// <summary>
/// PostgreSQL data source for the ReleaseOrchestrator module.
/// </summary>
public sealed class ReleaseOrchestratorDataSource : DataSourceBase
{
/// <summary>
/// Default schema name for release-orchestrator tables.
/// </summary>
public const string DefaultSchemaName = "release_orchestrator";
/// <summary>
/// Creates a new ReleaseOrchestrator data source.
/// </summary>
public ReleaseOrchestratorDataSource(IOptions<PostgresOptions> options, ILogger<ReleaseOrchestratorDataSource> logger)
: base(CreateOptions(options.Value), logger)
{
}
/// <inheritdoc />
protected override string ModuleName => "ReleaseOrchestrator";
/// <inheritdoc />
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;
}
}

View File

@@ -0,0 +1,127 @@
using StellaOps.ReleaseOrchestrator.Persistence.Domain;
namespace StellaOps.ReleaseOrchestrator.Persistence.Repositories;
/// <summary>
/// Repository for audit log entries.
/// </summary>
public interface IAuditRepository
{
/// <summary>
/// Appends a new audit entry to the log.
/// </summary>
Task<AuditEntry> 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);
/// <summary>
/// Gets an audit entry by ID.
/// </summary>
Task<AuditEntry?> GetByIdAsync(
string tenantId,
Guid entryId,
CancellationToken cancellationToken = default);
/// <summary>
/// Lists audit entries with optional filters.
/// </summary>
Task<IReadOnlyList<AuditEntry>> 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);
/// <summary>
/// Gets audit entries by sequence range.
/// </summary>
Task<IReadOnlyList<AuditEntry>> GetBySequenceRangeAsync(
string tenantId,
long startSequence,
long endSequence,
CancellationToken cancellationToken = default);
/// <summary>
/// Gets the latest audit entry for a tenant.
/// </summary>
Task<AuditEntry?> GetLatestAsync(
string tenantId,
CancellationToken cancellationToken = default);
/// <summary>
/// Gets audit entries for a specific resource.
/// </summary>
Task<IReadOnlyList<AuditEntry>> GetByResourceAsync(
string tenantId,
string resourceType,
Guid resourceId,
int limit = 100,
CancellationToken cancellationToken = default);
/// <summary>
/// Gets the count of audit entries.
/// </summary>
Task<long> GetCountAsync(
string tenantId,
AuditEventType? eventType = null,
DateTimeOffset? startTime = null,
DateTimeOffset? endTime = null,
CancellationToken cancellationToken = default);
/// <summary>
/// Verifies the chain integrity for a range of entries.
/// </summary>
Task<ChainVerificationResult> VerifyChainAsync(
string tenantId,
long? startSequence = null,
long? endSequence = null,
CancellationToken cancellationToken = default);
/// <summary>
/// Gets audit summary statistics.
/// </summary>
Task<AuditSummary> GetSummaryAsync(
string tenantId,
DateTimeOffset? since = null,
CancellationToken cancellationToken = default);
}
/// <summary>
/// Result of chain verification.
/// </summary>
public sealed record ChainVerificationResult(
bool IsValid,
Guid? InvalidEntryId,
long? InvalidSequence,
string? ErrorMessage);
/// <summary>
/// Audit summary statistics.
/// </summary>
public sealed record AuditSummary(
long TotalEntries,
long EntriesSince,
long EventTypes,
long UniqueActors,
long UniqueResources,
DateTimeOffset? EarliestEntry,
DateTimeOffset? LatestEntry);

View File

@@ -0,0 +1,50 @@
using StellaOps.ReleaseOrchestrator.Persistence.Domain;
namespace StellaOps.ReleaseOrchestrator.Persistence.Services;
public interface IFirstSignalService
{
/// <summary>
/// Gets the first signal for a run, checking cache first.
/// </summary>
Task<FirstSignalResult> GetFirstSignalAsync(
Guid runId,
string tenantId,
string? ifNoneMatch = null,
CancellationToken cancellationToken = default);
/// <summary>
/// Updates the first signal snapshot for a run and invalidates any cached copies.
/// </summary>
Task UpdateSnapshotAsync(
Guid runId,
string tenantId,
FirstSignal signal,
CancellationToken cancellationToken = default);
/// <summary>
/// Invalidates cached first signal for a run.
/// </summary>
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
}