From a15405431ba64177600a8ea0cfafd8b991046409 Mon Sep 17 00:00:00 2001 From: master <> Date: Sun, 19 Apr 2026 14:41:18 +0300 Subject: [PATCH] wip(scheduler): compose storage configuration compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sprint SPRINT_20260417_002_JobEngine_scheduler_storage_compose_compatibility (SCHEDULER-COMPAT-001 still DOING — sprint remains active). Adds scheduler storage configuration adapter layer so the web host accepts the compose-shaped storage configuration without manual remapping, plus SchedulerStorageConfigurationTests. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../SchedulerStorageConfiguration.cs | 32 ++++++++++++ .../SchedulerStorageConfigurationTests.cs | 49 +++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 src/JobEngine/StellaOps.Scheduler.WebService/Configuration/SchedulerStorageConfiguration.cs create mode 100644 src/JobEngine/StellaOps.Scheduler.__Tests/StellaOps.Scheduler.WebService.Tests/SchedulerStorageConfigurationTests.cs diff --git a/src/JobEngine/StellaOps.Scheduler.WebService/Configuration/SchedulerStorageConfiguration.cs b/src/JobEngine/StellaOps.Scheduler.WebService/Configuration/SchedulerStorageConfiguration.cs new file mode 100644 index 000000000..6baa06340 --- /dev/null +++ b/src/JobEngine/StellaOps.Scheduler.WebService/Configuration/SchedulerStorageConfiguration.cs @@ -0,0 +1,32 @@ +using Microsoft.Extensions.Configuration; + +namespace StellaOps.Scheduler.WebService.Configuration; + +public static class SchedulerStorageConfiguration +{ + public const string FlatSectionPath = "Scheduler:Storage"; + public const string ComposeNestedSectionPath = "Scheduler:Storage:Postgres:Scheduler"; + public const string LegacySectionPath = "Postgres:Scheduler"; + + public static string? ResolveSectionPath(IConfiguration configuration) + { + foreach (var sectionPath in GetCandidateSectionPaths()) + { + if (!string.IsNullOrWhiteSpace(configuration[$"{sectionPath}:ConnectionString"])) + { + return sectionPath; + } + } + + return null; + } + + public static string? ResolveConnectionString(IConfiguration configuration) + { + var sectionPath = ResolveSectionPath(configuration); + return sectionPath is null ? null : configuration[$"{sectionPath}:ConnectionString"]; + } + + private static string[] GetCandidateSectionPaths() + => [FlatSectionPath, ComposeNestedSectionPath, LegacySectionPath]; +} diff --git a/src/JobEngine/StellaOps.Scheduler.__Tests/StellaOps.Scheduler.WebService.Tests/SchedulerStorageConfigurationTests.cs b/src/JobEngine/StellaOps.Scheduler.__Tests/StellaOps.Scheduler.WebService.Tests/SchedulerStorageConfigurationTests.cs new file mode 100644 index 000000000..20fb1a9d8 --- /dev/null +++ b/src/JobEngine/StellaOps.Scheduler.__Tests/StellaOps.Scheduler.WebService.Tests/SchedulerStorageConfigurationTests.cs @@ -0,0 +1,49 @@ +using Microsoft.Extensions.Configuration; +using StellaOps.Scheduler.WebService.Configuration; + +namespace StellaOps.Scheduler.WebService.Tests; + +public sealed class SchedulerStorageConfigurationTests +{ + [Fact] + public void ResolveConnectionString_PrefersFlatSchedulerStorage() + { + var configuration = BuildConfiguration( + [ + new("Scheduler:Storage:ConnectionString", "Host=flat;Database=scheduler"), + new("Scheduler:Storage:Postgres:Scheduler:ConnectionString", "Host=nested;Database=scheduler"), + ]); + + Assert.Equal(SchedulerStorageConfiguration.FlatSectionPath, SchedulerStorageConfiguration.ResolveSectionPath(configuration)); + Assert.Equal("Host=flat;Database=scheduler", SchedulerStorageConfiguration.ResolveConnectionString(configuration)); + } + + [Fact] + public void ResolveConnectionString_AcceptsComposeNestedStorage() + { + var configuration = BuildConfiguration( + [ + new("Scheduler:Storage:Postgres:Scheduler:ConnectionString", "Host=nested;Database=scheduler"), + ]); + + Assert.Equal(SchedulerStorageConfiguration.ComposeNestedSectionPath, SchedulerStorageConfiguration.ResolveSectionPath(configuration)); + Assert.Equal("Host=nested;Database=scheduler", SchedulerStorageConfiguration.ResolveConnectionString(configuration)); + } + + [Fact] + public void ResolveConnectionString_FallsBackToLegacyPostgresScheduler() + { + var configuration = BuildConfiguration( + [ + new("Postgres:Scheduler:ConnectionString", "Host=legacy;Database=scheduler"), + ]); + + Assert.Equal(SchedulerStorageConfiguration.LegacySectionPath, SchedulerStorageConfiguration.ResolveSectionPath(configuration)); + Assert.Equal("Host=legacy;Database=scheduler", SchedulerStorageConfiguration.ResolveConnectionString(configuration)); + } + + private static IConfiguration BuildConfiguration(IEnumerable> values) + => new ConfigurationBuilder() + .AddInMemoryCollection(values) + .Build(); +}