From 21d7128e7438b598ab749cb1183e87cb03bd1351 Mon Sep 17 00:00:00 2001 From: Janus Knudsen Date: Fri, 31 Jan 2025 22:08:54 +0100 Subject: [PATCH] More work in this Configuration Manager --- .../AzureConfigurationManager.cs | 4 +- Core/Configurations/ConfigurationBuilder.cs | 61 ++++++++ Core/Configurations/IConfigurationRoot.cs | 14 ++ .../PostgresqlConfigurationBuilder/Class1.cs | 4 +- .../SmartConfiguration/JsonConfiguration.cs | 11 +- .../JsonConfigurationSection.cs | 4 +- .../SmartConfiguration/SmartConfigManager.cs | 4 +- .../KeyValueJsonHandlingTests.cs | 139 ++++++++++++++++++ Tests/TestConfigurationManagement.cs | 3 +- Tests/TestFixture.cs | 130 ++++++++-------- Tests/UnitTest1.cs | 4 +- 11 files changed, 299 insertions(+), 79 deletions(-) create mode 100644 Core/Configurations/ConfigurationBuilder.cs create mode 100644 Core/Configurations/IConfigurationRoot.cs create mode 100644 Tests/ConfigurationTests/KeyValueJsonHandlingTests.cs diff --git a/Core/Configurations/AzureConfigurationManager.cs b/Core/Configurations/AzureConfigurationManager.cs index bf6c712..772ddaa 100644 --- a/Core/Configurations/AzureConfigurationManager.cs +++ b/Core/Configurations/AzureConfigurationManager.cs @@ -18,7 +18,7 @@ namespace Core.Configurations { if (_configurationBuilder == null) { - var envConfiguration = new ConfigurationBuilder() + var envConfiguration = new Microsoft.Extensions.Configuration.ConfigurationBuilder() .AddEnvironmentVariables() .Build(); @@ -27,7 +27,7 @@ namespace Core.Configurations var appConfigEndpoint = envConfiguration["AppConfigEndpoint"]; var appConfigLabel = envConfiguration["AppConfigLabelFilter"]; - _configurationBuilder = new ConfigurationBuilder(); + _configurationBuilder = new Microsoft.Extensions.Configuration.ConfigurationBuilder(); if (!string.IsNullOrEmpty(appConfigEndpoint)) { _configurationBuilder diff --git a/Core/Configurations/ConfigurationBuilder.cs b/Core/Configurations/ConfigurationBuilder.cs new file mode 100644 index 0000000..1c31410 --- /dev/null +++ b/Core/Configurations/ConfigurationBuilder.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Core.Configurations +{ + public class ConfigurationBuilder + { + private readonly List _providers = new(); + + public ConfigurationBuilder AddProvider(IConfigurationProvider provider) + { + _providers.Add(provider); + return this; + } + + public IConfigurationRoot Build() + { + // Her kan du implementere din egen sammenlægningslogik + return new ConfigurationRoot(_providers); + } + } + + public class ConfigurationRoot : IConfigurationRoot + { + public ConfigurationRoot(List configurationProviders) + { + + } + public T GetSection(string key) + { + throw new NotImplementedException(); + } + } + public static class ConfigurationPredicateExtensions + { + + public static IConfigurationSection GetSection(this IConfigurationRoot configurationSection, string key) + { + return null; + } + + public static T Get(this IConfigurationSection configuration, string key) + { + return default(T); + } + } + public interface IConfigurationProvider + { + Dictionary Configuration(); + } + + public interface IConfigurationSection + { + string Key { get; } + string Path { get; } + string Value { get; set; } + } +} diff --git a/Core/Configurations/IConfigurationRoot.cs b/Core/Configurations/IConfigurationRoot.cs new file mode 100644 index 0000000..ff2c5f1 --- /dev/null +++ b/Core/Configurations/IConfigurationRoot.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Core.Configurations +{ + public interface IConfigurationRoot + { + + } + +} diff --git a/Core/Configurations/PostgresqlConfigurationBuilder/Class1.cs b/Core/Configurations/PostgresqlConfigurationBuilder/Class1.cs index a4c8888..252bf2a 100644 --- a/Core/Configurations/PostgresqlConfigurationBuilder/Class1.cs +++ b/Core/Configurations/PostgresqlConfigurationBuilder/Class1.cs @@ -8,7 +8,7 @@ using System.Threading.Tasks; namespace Core.Configurations.PostgresqlConfigurationBuilder { - public class PostgresConfigurationSource : IConfigurationSource + public class PostgresConfigurationSource : Microsoft.Extensions.Configuration.IConfigurationSource { private readonly string _connectionString; private readonly string _channel; @@ -21,7 +21,7 @@ namespace Core.Configurations.PostgresqlConfigurationBuilder _configurationQuery = configurationQuery ?? throw new ArgumentNullException(nameof(configurationQuery)); } - public IConfigurationProvider Build(IConfigurationBuilder builder) + public Microsoft.Extensions.Configuration.IConfigurationProvider Build(IConfigurationBuilder builder) { return new PostgresConfigurationProvider(_connectionString, _channel, _configurationQuery); } diff --git a/Core/Configurations/SmartConfiguration/JsonConfiguration.cs b/Core/Configurations/SmartConfiguration/JsonConfiguration.cs index 670b0ab..1659475 100644 --- a/Core/Configurations/SmartConfiguration/JsonConfiguration.cs +++ b/Core/Configurations/SmartConfiguration/JsonConfiguration.cs @@ -4,7 +4,7 @@ using Microsoft.Extensions.Primitives; using System.Data; namespace Core.Configurations.SmartConfiguration; -public class JsonConfiguration : IConfiguration +public class JsonConfiguration : Microsoft.Extensions.Configuration.IConfiguration { private readonly JObject _data; @@ -19,11 +19,16 @@ public class JsonConfiguration : IConfiguration set => throw new NotImplementedException(); } - public IConfigurationSection GetSection(string key) => - new JsonConfigurationSection(_data, key); + public Microsoft.Extensions.Configuration.IConfigurationSection GetSection(string key) => null; + //new JsonConfigurationSection(_data, key); public IEnumerable GetChildren() => _data.Properties().Select(p => new JsonConfigurationSection(_data, p.Name)); public IChangeToken GetReloadToken() => throw new NotImplementedException(); + + IEnumerable IConfiguration.GetChildren() + { + throw new NotImplementedException(); + } } \ No newline at end of file diff --git a/Core/Configurations/SmartConfiguration/JsonConfigurationSection.cs b/Core/Configurations/SmartConfiguration/JsonConfigurationSection.cs index 8a652a2..f7f1583 100644 --- a/Core/Configurations/SmartConfiguration/JsonConfigurationSection.cs +++ b/Core/Configurations/SmartConfiguration/JsonConfigurationSection.cs @@ -43,12 +43,12 @@ namespace Core.Configurations.SmartConfiguration set => throw new NotImplementedException("Setting values is not supported."); } - public IConfigurationSection GetSection(string key) + public Microsoft.Extensions.Configuration.IConfigurationSection GetSection(string key) { if (string.IsNullOrEmpty(key)) throw new ArgumentNullException(nameof(key)); - return new JsonConfigurationSection(_data, string.IsNullOrEmpty(_path) ? key : $"{_path}:{key}"); + return null;// new JsonConfigurationSection(_data, string.IsNullOrEmpty(_path) ? key : $"{_path}:{key}"); } public JToken GetToken() => _data.SelectToken(_normalizedPath); diff --git a/Core/Configurations/SmartConfiguration/SmartConfigManager.cs b/Core/Configurations/SmartConfiguration/SmartConfigManager.cs index 72f2838..ac27240 100644 --- a/Core/Configurations/SmartConfiguration/SmartConfigManager.cs +++ b/Core/Configurations/SmartConfiguration/SmartConfigManager.cs @@ -18,14 +18,14 @@ namespace Core.Configurations { if (_configurationBuilder == null) { - var envConfiguration = new ConfigurationBuilder() + var envConfiguration = new Microsoft.Extensions.Configuration.ConfigurationBuilder() .AddEnvironmentVariables() .Build(); var appConfigEndpoint = envConfiguration["AppConfigEndpoint"]; var appConfigLabel = envConfiguration["AppConfigLabelFilter"]; - _configurationBuilder = new ConfigurationBuilder(); + _configurationBuilder = new Microsoft.Extensions.Configuration.ConfigurationBuilder(); if (!string.IsNullOrEmpty(appConfigEndpoint)) { _configurationBuilder diff --git a/Tests/ConfigurationTests/KeyValueJsonHandlingTests.cs b/Tests/ConfigurationTests/KeyValueJsonHandlingTests.cs new file mode 100644 index 0000000..033aa2e --- /dev/null +++ b/Tests/ConfigurationTests/KeyValueJsonHandlingTests.cs @@ -0,0 +1,139 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json.Linq; +using System.Collections.Generic; +using System.Linq; +namespace Tests.ConfigurationTests +{ + [TestClass] + public class KeyValueJsonHandlingTests : TestFixture + { + + [TestMethod] + public void FunMixedTypesTest() + { + var pairs = new List> + { + new("developer:coffeeLevel", 9001.5), + new("developer:bugsFixed", 42), + new("developer:awake", false), + new("compiler:errors:semicolon:0", "Missing ;"), + new("compiler:errors:semicolon:1", "Found ; but why?"), + new("computer:ram:status", "Out of memory"), + new("computer:cpu:temperature", 99.9), + new("friday:beer:count", 6), + new("friday:code:working", true), + new("friday:bugs:count", int.MaxValue), + new("real:object", JObject.Parse(@"{ + ""beer"": { + ""count"": 6 + }, + ""code"": { + ""working"": true + }, + ""bugs"": { + ""count"": 2147483647 + } + }")) + }; + + + + var result = KeyValueToJson.Convert(pairs); + + var expected = JObject.Parse(@"{ + 'developer': { + 'coffeeLevel': 9001.5, + 'bugsFixed': 42, + 'awake': false + }, + 'compiler': { + 'errors': { 'semicolon': ['Missing ;', 'Found ; but why?'] } + }, + 'computer': { + 'ram': { 'status': 'Out of memory' }, + 'cpu': { 'temperature': 99.9 } + }, + 'friday': { + 'beer': { 'count': 6 }, + 'code': { 'working': true }, + 'bugs': { 'count': 2147483647 } + } + }"); + + Assert.IsTrue(JToken.DeepEquals(expected, result)); + } + } + + +public static class KeyValueToJson + { + public static JObject Convert(List> pairs) + { + var root = new JObject(); + + foreach (var pair in pairs) + { + var keys = pair.Key.Split(':'); + var current = root; + + // Gennemgå hierarkiet og opret underobjekter, hvis de ikke eksisterer + for (int i = 0; i < keys.Length - 1; i++) + { + var key = keys[i]; + + if (current[key] == null) + { + current[key] = new JObject(); + } + + current = (JObject)current[key]; + } + + // Håndter den sidste nøgle og tilføj værdien + var lastKey = keys[keys.Length - 1]; + var value = ConvertValue(pair.Value); + + // Hvis den sidste nøgle allerede eksisterer, tilføj til en liste + if (current[lastKey] != null) + { + // Hvis den allerede er en liste, tilføj til listen + if (current[lastKey].Type == JTokenType.Array) + { + ((JArray)current[lastKey]).Add(value); + } + // Hvis den ikke er en liste, konverter til en liste + else + { + var existingValue = current[lastKey]; + current[lastKey] = new JArray { existingValue, value }; + } + } + // Ellers tilføj som en enkelt værdi + else + { + current[lastKey] = value; + } + } + + return root; + } + + private static JToken ConvertValue(object value) + { + // Konverter værdien til det korrekte JToken-format + return value switch + { + int i => new JValue(i), + double d => new JValue(d), + bool b => new JValue(b), + string s => new JValue(s), + _ => new JValue(value.ToString()) // Fallback for andre typer + }; + } + } +} diff --git a/Tests/TestConfigurationManagement.cs b/Tests/TestConfigurationManagement.cs index 9e73fdb..ecd44da 100644 --- a/Tests/TestConfigurationManagement.cs +++ b/Tests/TestConfigurationManagement.cs @@ -23,6 +23,7 @@ public class ConfigurationTests : TestFixture _builder = new KeyValueConfigurationBuilder(_mockRepo.Object); } + [TestMethod] public async Task LoadConfiguration_WithValidData_BuildsCorrectly() { @@ -142,7 +143,7 @@ public class ConfigurationTests : TestFixture } }; - IConfiguration configuration = new JsonConfiguration(configData, new Microsoft.Extensions.Configuration.ConfigurationReloadToken()); + Microsoft.Extensions.Configuration.IConfiguration configuration = null;// new JsonConfiguration(configData, new Microsoft.Extensions.Configuration.ConfigurationReloadToken()); // Act var welcomeConfig = configuration.GetSection("Email:Templates:Welcome").Get(); diff --git a/Tests/TestFixture.cs b/Tests/TestFixture.cs index 36a2cef..dde7f2b 100644 --- a/Tests/TestFixture.cs +++ b/Tests/TestFixture.cs @@ -9,89 +9,89 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Tests { - /// - /// Act as base class for tests. Avoids duplication of test setup code - /// - [TestClass] - public abstract partial class TestFixture - { - protected IContainer Container { get; private set; } - protected ContainerBuilder ContainerBuilder { get; private set; } + /// + /// Act as base class for tests. Avoids duplication of test setup code + /// + [TestClass] + public abstract partial class TestFixture + { + protected IContainer Container { get; private set; } + protected ContainerBuilder ContainerBuilder { get; private set; } - [AssemblyInitialize] - public static void AssemblySetup(TestContext tc) - { - Trace.Listeners.Add(new TextWriterTraceListener(Console.Out)); + [AssemblyInitialize] + public static void AssemblySetup(TestContext tc) + { + Trace.Listeners.Add(new TextWriterTraceListener(Console.Out)); - var envConfiguration = new ConfigurationBuilder() - .AddEnvironmentVariables() - .Build(); + var envConfiguration = new ConfigurationBuilder() + .AddEnvironmentVariables() + .Build(); - } + } - public virtual IConfigurationRoot Configuration() - { + public virtual IConfigurationRoot Configuration() + { - IConfigurationBuilder configBuilder = Core.Configurations.SmartConfigManager.AppConfigBuilder("appsettings.dev.json"); - IConfigurationRoot configuration = configBuilder.Build(); + IConfigurationBuilder configBuilder = Core.Configurations.AzureConfigurationManager.AppConfigBuilder("appsettings.dev.json"); + IConfigurationRoot configuration = configBuilder.Build(); - return configuration; - } + return configuration; + } - /// - /// Should not be overriden. Rather override PreArrangeAll to setup data needed for a test class. - /// Override PrebuildContainer with a method that does nothing to prevent early build of IOC container - /// - [TestInitialize] - public void Setup() - { - CreateContainerBuilder(); - Container = ContainerBuilder.Build(); - Insight.Database.Providers.PostgreSQL.PostgreSQLInsightDbProvider.RegisterProvider(); - } + /// + /// Should not be overriden. Rather override PreArrangeAll to setup data needed for a test class. + /// Override PrebuildContainer with a method that does nothing to prevent early build of IOC container + /// + [TestInitialize] + public void Setup() + { + CreateContainerBuilder(); + Container = ContainerBuilder.Build(); + Insight.Database.Providers.PostgreSQL.PostgreSQLInsightDbProvider.RegisterProvider(); + } - protected virtual void CreateContainerBuilder() - { - IConfigurationRoot configuration = Configuration(); + protected virtual void CreateContainerBuilder() + { + IConfigurationRoot configuration = Configuration(); - var builder = new ContainerBuilder(); - builder.RegisterInstance(new LoggerFactory()) - .As(); + var builder = new ContainerBuilder(); + builder.RegisterInstance(new LoggerFactory()) + .As(); - builder.RegisterGeneric(typeof(Logger<>)) - .As(typeof(ILogger<>)) - .SingleInstance(); + builder.RegisterGeneric(typeof(Logger<>)) + .As(typeof(ILogger<>)) + .SingleInstance(); - builder.RegisterModule(new Core.ModuleRegistry.DbPostgreSqlModule - { - ConnectionString = configuration.GetConnectionString("ptdb") - }); + builder.RegisterModule(new Core.ModuleRegistry.DbPostgreSqlModule + { + ConnectionString = configuration.GetConnectionString("ptdb") + }); - builder.RegisterModule(new Core.ModuleRegistry.TelemetryModule - { - TelemetryConfig = configuration.GetSection("ApplicationInsights").Get() - }); + builder.RegisterModule(new Core.ModuleRegistry.TelemetryModule + { + TelemetryConfig = configuration.GetSection("ApplicationInsights").Get() + }); - ContainerBuilder = builder; - } + ContainerBuilder = builder; + } - [TestCleanup] - public void CleanUp() - { - Trace.Flush(); - var telemetryClient = Container.Resolve(); - telemetryClient.Flush(); + [TestCleanup] + public void CleanUp() + { + Trace.Flush(); + var telemetryClient = Container.Resolve(); + telemetryClient.Flush(); - if (Container != null) - { - Container.Dispose(); - Container = null; - } - } + if (Container != null) + { + Container.Dispose(); + Container = null; + } + } - } + } } \ No newline at end of file diff --git a/Tests/UnitTest1.cs b/Tests/UnitTest1.cs index fb9829e..d86ffae 100644 --- a/Tests/UnitTest1.cs +++ b/Tests/UnitTest1.cs @@ -35,8 +35,8 @@ namespace Tests { var conn = Container.Resolve(); - var dbSetup = new Database.Identity.DbInfrastructureSetup(conn); - await dbSetup.CreateDatabaseWithSchema("swp"); + var dbSetup = new Database.AppConfigurationSystem.ConfigurationDatabaseSetup(conn); + //await dbSetup..CreateDatabaseWithSchema("swp"); }