Skip to content

Commit

Permalink
Add MapConfiguration
Browse files Browse the repository at this point in the history
  • Loading branch information
thohng committed May 14, 2024
1 parent 2a36c0c commit 96c6df0
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 4 deletions.
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<!-- Embed source files that are not tracked by the source control manager in the PDB -->
<EmbedUntrackedSources>true</EmbedUntrackedSources>

<LangVersion>10.0</LangVersion>
<LangVersion>12.0</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,11 @@ public ConfigurationBuilderBuilder WithTransformConfiguration(string sectionKey
return WithAddPostConfiguration(builder => builder.AddTransformConfiguration(sectionKey));
}

public ConfigurationBuilderBuilder WithMapConfiguration(string sectionKey = "MapConfiguration")
{
return WithAddPostConfiguration(builder => builder.AddMapConfiguration(sectionKey));
}

public static ConfigurationBuilderBuilder Create<TStartup>(string[]? args = null)
=> new ConfigurationBuilderBuilder()
.WithCommandLines(args)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,23 @@ public static TConfigurationBuilder AddTransformConfiguration<TConfigurationBuil
return configBuilder;
}

public static TConfigurationBuilder AddMapConfiguration<TConfigurationBuilder>(this TConfigurationBuilder configBuilder, string sectionKey = "MapConfiguration")
where TConfigurationBuilder : IConfigurationBuilder
{
if (string.IsNullOrWhiteSpace(sectionKey))
{
throw new ArgumentException($"{nameof(sectionKey)} is required", nameof(sectionKey));
}

var configuration = configBuilder is IConfigurationRoot configurationRoot
? configurationRoot
: configBuilder.Build();

configBuilder.Add(new MapConfigurationSource(configuration, sectionKey));

return configBuilder;
}

public static TConfigurationBuilder AddAddFileConfiguration<TConfigurationBuilder>(this TConfigurationBuilder configBuilder, Action<AddFileConfigurationSourceOptions>? configureOptions = null, string sectionKey = "AddFile", bool? throwIfNotSupport = null)
where TConfigurationBuilder : IConfigurationBuilder
{
Expand Down
64 changes: 64 additions & 0 deletions src/NetLah.Extensions.Configuration/MapConfigurationProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Primitives;

namespace NetLah.Extensions.Configuration;

public class MapConfigurationProvider(MapConfigurationSource source) : ConfigurationProvider
{
private readonly MapConfigurationSource _source = source;
private object? _lock = null;
private IChangeToken? _token;
private IConfigurationSection? _configurationSection;

private void OnChange(object? obj)
{
Data.Clear();
InternalLoad();
OnReload();
}

public override void Load()
{
InternalLoad();

if (_configurationSection != null && Interlocked.CompareExchange(ref _lock, new object(), null) == null)
{
_token = _configurationSection.GetReloadToken();
_token.RegisterChangeCallback(OnChange, this);
}
}

private void InternalLoad()
{
var configuration = _source.Configuration;
_configurationSection ??= configuration.GetSection(_source.SectionKey);

foreach (var item in _configurationSection.GetChildren())
{
if (item.Value is { } keyValue)
{
TryParse(keyValue);
}
else
{
var key1 = item["From"] ?? item["Source"];
var key2 = item["To"] ?? item["Destination"] ?? item["Dest"];
if (!string.IsNullOrEmpty(key1) && !string.IsNullOrEmpty(key2) && configuration[key1] is { } value)
{
Data[key2] = value;
}
}
}

void TryParse(string keyValue)
{
var pos = keyValue.IndexOf('=');
var key1 = keyValue[..pos];
var key2 = keyValue[(pos + 1)..];
if (configuration[key1] is { } value)
{
Data[key2] = value;
}
}
}
}
15 changes: 15 additions & 0 deletions src/NetLah.Extensions.Configuration/MapConfigurationSource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Microsoft.Extensions.Configuration;

namespace NetLah.Extensions.Configuration;

public class MapConfigurationSource(IConfiguration configuration, string sectionKey) : IConfigurationSource
{
public IConfiguration Configuration { get; } = configuration;

public string SectionKey { get; } = sectionKey;

public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new MapConfigurationProvider(this);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,15 @@ private static void AssertProduction(IConfiguration configuration)
{
Assert.Equal("MainValue1", configuration["MainKey"]);
Assert.Equal("EnvironmentProductionValue1", configuration["EnvironmentKey"]);
Assert.Equal("ProductionSubValue1", configuration["ProductionSection:SubKey"]);
Assert.Null(configuration["SecondaryKey"]);
}

private static void AssertDevelopment(IConfiguration configuration)
{
Assert.Equal("MainValue1", configuration["MainKey"]);
Assert.Equal("EnvironmentDevelopmentValue1", configuration["EnvironmentKey"]);
Assert.Equal("DevelopmentSubValue1", configuration["DevelopmentSection:SubKey"]);
}

private static void AssertCommandLines(IConfiguration configuration)
Expand Down Expand Up @@ -564,7 +567,9 @@ public void Build_Production_changeTo_dev_Success()
null,
});

AssertDevelopment(configuration2);
Assert.Equal("MainValue1", configuration2["MainKey"]);
Assert.Equal("EnvironmentDevelopmentValue1", configuration2["EnvironmentKey"]);
Assert.Null(configuration2["DevelopmentSection:SubKey"]);
AssertCommandLines(configuration2);
}

Expand Down Expand Up @@ -1433,4 +1438,60 @@ public void AddFileConfigurationProduction_KeyPerFile_Handler()
Assert.Equal("Key-Per-File/mainValue line0", configuration["MainKey"]);
Assert.Equal("KeyPerFileSection__Add-File-Source.txt", configuration["KeyPerFileSection:Add-File-Source"]);
}

[Fact]
public void EnvironmentNameMapConfiguration()
{
var configuration = ConfigurationBuilderBuilder.Create(["/CommandLineSection:SubKey=CommandLineSection-SubKeyValue"])
.WithEnvironment("Map")
.WithMapConfiguration()
.BuildConfigurationRoot();

AssertProviders(configuration, new[] {
"JsonConfigurationProvider",
"JsonConfigurationProvider",
"EnvironmentVariablesConfigurationProvider",
"CommandLineConfigurationProvider",
"MapConfigurationProvider",
}, new[] {
"appsettings.json",
"appsettings.Map.json",
null,
null,
null,
});

Assert.Equal("MainValue1", configuration["MainKey"]);
Assert.Equal("MainValue1", configuration["SecondaryKey"]);
Assert.Equal("CommandLineSection-SubKeyValue", configuration["CommandLineSection:SubKey"]);
Assert.Equal("CommandLineSection-SubKeyValue", configuration["MapSection:SubKey"]);
}

[Fact]
public void ConfigurationBuilder_EnvironmentNameMapConfiguration()
{
var configuration = CreateDefaultBuilder(["/CommandLineSection:SubKey=CommandLineSection-SubKeyValue"], "Map")
.AddMapConfiguration()
.Build();

AssertProviders(configuration, [
"JsonConfigurationProvider",
"JsonConfigurationProvider",
"EnvironmentVariablesConfigurationProvider",
"CommandLineConfigurationProvider",
"MapConfigurationProvider",
], [
"appsettings.json",
"appsettings.Map.json",
null,
null,
null,
]);

Assert.Equal("MainValue1", configuration["MainKey"]);
Assert.Equal("MainValue1", configuration["SecondaryKey"]);
Assert.Equal("CommandLineSection-SubKeyValue", configuration["CommandLineSection:SubKey"]);
Assert.Equal("CommandLineSection-SubKeyValue", configuration["MapSection:SubKey"]);
}

}
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
{
"EnvironmentKey": "EnvironmentDevelopmentValue1"
"EnvironmentKey": "EnvironmentDevelopmentValue1",
"DevelopmentSection": {
"SubKey": "DevelopmentSubValue1"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"MapConfiguration": [
"MainKey=SecondaryKey",
{
"From": "CommandLineSection:SubKey",
"To": "MapSection:SubKey"
}
]
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
{
"EnvironmentKey": "EnvironmentProductionValue1"
"EnvironmentKey": "EnvironmentProductionValue1",
"ProductionSection": {
"SubKey": "ProductionSubValue1"
}
}

0 comments on commit 96c6df0

Please sign in to comment.