refactor: address code review findings across all projects

Apply comprehensive fixes from code reviews including:
- Extract shared utilities (SqlFormatHelper, CellValueConverter, DbDestinationBase)
- Add interface abstractions (IAuthenticationService, IDatabaseMigrator, IMisQueryBuilder)
- Implement SecureStore for encrypted secrets storage
- Fix error handling with proper HTTP status codes and logging
- Optimize double enumeration in DevEtlRegistry
- Add DataSync.Dev README for developer onboarding
- Extract filter panel base classes to reduce duplication
- Update code review docs to mark all issues as fixed
This commit is contained in:
Joseph Doherty
2026-01-19 11:05:36 -05:00
parent 08f5aa1447
commit 604bfe919c
148 changed files with 8696 additions and 1538 deletions
+23 -3
View File
@@ -3,6 +3,8 @@ using JdeScoping.DataAccess.Options;
using JdeScoping.DataSync.Options;
using JdeScoping.ExcelIO.Options;
using JdeScoping.Database;
using JdeScoping.Infrastructure.Security;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
var builder = WebApplication.CreateBuilder(args);
@@ -11,14 +13,21 @@ var builder = WebApplication.CreateBuilder(args);
builder.Host.UseWindowsService();
// Run database migrations (skip in Testing environment)
// Note: IDatabaseMigrator interface enables mocking for integration tests
if (!builder.Environment.IsEnvironment("Testing"))
{
var migrator = new DatabaseMigrator(builder.Configuration);
// Create early logger for startup diagnostics
using var loggerFactory = LoggerFactory.Create(b => b
.AddConfiguration(builder.Configuration.GetSection("Logging"))
.AddConsole());
var startupLogger = loggerFactory.CreateLogger("JdeScoping.Host.Startup");
IDatabaseMigrator migrator = new DatabaseMigrator(builder.Configuration);
var migrationResult = migrator.Migrate();
if (!migrationResult.Successful)
{
Console.WriteLine($"Database migration failed: {migrationResult.Error?.Message}");
startupLogger.LogError(migrationResult.Error, "Database migration failed: {ErrorMessage}", migrationResult.Error?.Message);
return 1;
}
}
@@ -36,6 +45,13 @@ builder.Services
var app = builder.Build();
// Migrate existing secrets to SecureStore (skip in Testing environment)
if (!app.Environment.IsEnvironment("Testing"))
{
var migrator = app.Services.GetRequiredService<SecretsMigrator>();
migrator.MigrateIfNeeded();
}
// Startup validation - verify critical services are registered
ValidateServices(app.Services);
@@ -64,11 +80,15 @@ static void ValidateServices(IServiceProvider services)
{
using var scope = services.CreateScope();
var provider = scope.ServiceProvider;
var logger = provider.GetRequiredService<ILoggerFactory>().CreateLogger("JdeScoping.Host.Startup");
// Validate Options classes are bound
_ = provider.GetRequiredService<IOptions<DataAccessOptions>>();
_ = provider.GetRequiredService<IOptions<DataSyncOptions>>();
_ = provider.GetRequiredService<IOptions<ExcelExportOptions>>();
_ = provider.GetRequiredService<IOptions<SearchProcessingOptions>>();
Console.WriteLine("Service validation completed successfully.");
logger.LogInformation("Service validation completed successfully");
}
// Enable WebApplicationFactory<Program> for integration testing
public partial class Program { }
+7
View File
@@ -128,6 +128,13 @@
"UseFileDataSource": false,
"FileDirectory": "DevData"
},
"SecureStore": {
"StorePath": "data/secrets.json",
"KeyFilePath": "data/secrets.key",
"MasterKeyEnvVar": "SCOPINGTOOL_MASTER_KEY",
"AutoCreateStore": true,
"MigrateExistingSecrets": true
},
"WorkProcessor": {
"Enabled": true,
"WorkInterval": "00:00:05",