using System.Reflection; namespace StellaOps.Infrastructure.Postgres.Migrations; /// /// Interface for running database migrations. /// public interface IMigrationRunner { /// /// Gets the schema name for this migration runner. /// string SchemaName { get; } /// /// Gets the module name for this migration runner. /// string ModuleName { get; } /// /// Runs pending migrations from the specified path. /// /// Path to directory containing SQL migration files. /// Migration execution options. /// Cancellation token. /// Result of migration execution. Task RunAsync( string migrationsPath, MigrationRunOptions? options = null, CancellationToken cancellationToken = default); /// /// Runs pending migrations from embedded resources in an assembly. /// /// Assembly containing embedded migration resources. /// Optional prefix to filter resources. /// Migration execution options. /// Cancellation token. /// Result of migration execution. Task RunFromAssemblyAsync( Assembly assembly, string? resourcePrefix = null, MigrationRunOptions? options = null, CancellationToken cancellationToken = default); /// /// Gets the current migration version (latest applied migration). /// Task GetCurrentVersionAsync(CancellationToken cancellationToken = default); /// /// Gets all applied migrations. /// Task> GetAppliedMigrationInfoAsync(CancellationToken cancellationToken = default); /// /// Validates checksums of applied migrations against source files. /// /// Assembly containing embedded migration resources. /// Optional prefix to filter resources. /// Cancellation token. /// List of checksum validation errors, empty if all valid. Task> ValidateChecksumsAsync( Assembly assembly, string? resourcePrefix = null, CancellationToken cancellationToken = default); } /// /// Options for migration execution. /// public sealed class MigrationRunOptions { /// /// Filter migrations by category. If null, all categories are included. /// public MigrationCategory? CategoryFilter { get; set; } /// /// If true, only show what would be executed without applying. /// public bool DryRun { get; set; } /// /// Timeout in seconds for individual migration execution. Default: 300. /// public int TimeoutSeconds { get; set; } = 300; /// /// If true, validate checksums before applying new migrations. /// public bool ValidateChecksums { get; set; } = true; /// /// If true, fail if checksum validation errors are found. /// public bool FailOnChecksumMismatch { get; set; } = true; } /// /// Result of a migration execution. /// public sealed class MigrationResult { /// /// Whether the migration run was successful. /// public bool Success { get; init; } /// /// Number of migrations applied. /// public int AppliedCount { get; init; } /// /// Number of migrations skipped (already applied). /// public int SkippedCount { get; init; } /// /// Number of migrations filtered out by category. /// public int FilteredCount { get; init; } /// /// Total duration in milliseconds. /// public long DurationMs { get; init; } /// /// Details of applied migrations. /// public IReadOnlyList AppliedMigrations { get; init; } = []; /// /// Checksum validation errors, if any. /// public IReadOnlyList ChecksumErrors { get; init; } = []; /// /// Error message if migration failed. /// public string? ErrorMessage { get; init; } /// /// Creates a successful result. /// public static MigrationResult Successful( int appliedCount, int skippedCount, int filteredCount, long durationMs, IReadOnlyList appliedMigrations) => new() { Success = true, AppliedCount = appliedCount, SkippedCount = skippedCount, FilteredCount = filteredCount, DurationMs = durationMs, AppliedMigrations = appliedMigrations }; /// /// Creates a failed result. /// public static MigrationResult Failed(string errorMessage, IReadOnlyList? checksumErrors = null) => new() { Success = false, ErrorMessage = errorMessage, ChecksumErrors = checksumErrors ?? [] }; } /// /// Details of an applied migration. /// public sealed record AppliedMigrationDetail( string Name, MigrationCategory Category, long DurationMs, bool WasDryRun);