Skip to content

Commit

Permalink
Added Morestachio Config Transformator
Browse files Browse the repository at this point in the history
  • Loading branch information
JPVenson committed Apr 25, 2021
1 parent 93d9791 commit e02a2d6
Show file tree
Hide file tree
Showing 14 changed files with 641 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0;net461;</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="NUnit" Version="3.13.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Morestachio.Configuration.Transform\Morestachio.Configuration.Transform.csproj" />
</ItemGroup>

</Project>
110 changes: 110 additions & 0 deletions Morestachio.Configuration.Transform.Tests/TestConfigTransformation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Microsoft.Extensions.Configuration;
using NUnit.Framework;

namespace Morestachio.Configuration.Transform.Tests
{
[TestFixture(true)]
[TestFixture(false)]
public class TestConfigTransformation
{
private readonly bool _useBuildTime;

public TestConfigTransformation(bool useBuildTime)
{
_useBuildTime = useBuildTime;
}

public IMorestachioConfigurationBuilder CreateConfig(Func<IConfigurationBuilder, IConfigurationBuilder> builderConfig = null)
{
IConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder = builderConfig != null ? builderConfig(configurationBuilder) : configurationBuilder;
if (_useBuildTime)
{
return configurationBuilder.UseBuildtimeMorestachio();
}
else
{
return configurationBuilder.UseRuntimeMorestachio();
}
}

[Test]
public void TestCanReplaceBuildTime()
{
var builder = CreateConfig(c => c
.AddInMemoryCollection(new[]
{
new KeyValuePair<string, string>("Str", "test"),
new KeyValuePair<string, string>("Number", "mex{{2 + 2}}")
}));

var config = builder.Build();
Assert.That(config["Str"], Is.EqualTo("test"));
Assert.That(config["Number"], Is.EqualTo("4"));
}

[Test]
public void TestCanReplaceBuildTimeWithDeepPaths()
{
var builder = CreateConfig(c => c.AddJsonStream(JsonfyText(
@"{
""strValue"": ""test"",
""objValue"": {
""constValue"": 123,
""valueA"": ""mex{{1 + 2 + 3}}""
},
""valueB"": ""mex{{5 * 3}}""
}")));

var config = builder.Build();
Assert.That(config["strValue"], Is.EqualTo("test"));
Assert.That(config["objValue:constValue"], Is.EqualTo("123"));
Assert.That(config["objValue:valueA"], Is.EqualTo("6"));
Assert.That(config["valueB"], Is.EqualTo("15"));
}

[Test]
public void TestCanReplaceBuildTimeWithArguments()
{
var builder = CreateConfig(c => c.AddJsonStream(JsonfyText(
@"{
""strValue"": ""test"",
""objValue"": {
""constValue"": 123,
""exp_mEx"": ""mex{{1 + 2 + 3 - Cores}}"",
""expRoot_mEx"": ""mex{{1 + 2 + 3 - Cores}}"",
},
""expA_mEx"": ""mex{{Cores * 3}}""
}")))

.UseValues(null, new Dictionary<string, object>()
{
{ "Cores", 5 }
})
.UseValues("objValue:exp_mEx", new Dictionary<string, object>()
{
{ "Cores", 1 }
})
.UseValues("objValue", new Dictionary<string, object>()
{
{ "Cores", 3 }
});

var config = builder.Build();
Assert.That(config["strValue"], Is.EqualTo("test"));
Assert.That(config["objValue:constValue"], Is.EqualTo("123"));
Assert.That(config["objValue:exp_mEx"], Is.EqualTo("5"));
Assert.That(config["objValue:expRoot_mEx"], Is.EqualTo("3"));
Assert.That(config["expA_mEx"], Is.EqualTo("15"));
}

private Stream JsonfyText(string text)
{
return new MemoryStream(Encoding.UTF8.GetBytes(text));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Microsoft.Extensions.Configuration;

namespace Morestachio.Configuration.Transform
{
/// <summary>
/// A morestachio config builder
/// </summary>
public interface IMorestachioConfigurationBuilder : IConfigurationBuilder
{
MorestachioConfigOptions Options { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0;net461;</TargetFrameworks>
<PackageTags>Formatter, C#, Engine, NetStandard, Configuration</PackageTags>
<Description>A adapter for using Morestachio with the IConfiguration system.</Description>
<PackageReleaseNotes>Several Bugfixes and Formatter refactoring</PackageReleaseNotes>
<BuildPackage>true</BuildPackage>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Morestachio\Morestachio.csproj" />
</ItemGroup>
<Import Project="../AfterDirectory.Build.props"></Import>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;

namespace Morestachio.Configuration.Transform
{
/// <summary>
/// Wraps the <see cref="IConfigurationBuilder"/> and preprocesses all keys that matches the <see cref="MorestachioConfigOptions.TransformCondition"/>
/// </summary>
public class MorestachioBuildtimeConfigBuilder : IMorestachioConfigurationBuilder
{
private readonly IConfigurationBuilder _builder;

/// <summary>
///
/// </summary>
/// <param name="builder"></param>
public MorestachioBuildtimeConfigBuilder(IConfigurationBuilder builder)
{
_builder = builder;
Options = new MorestachioConfigOptions();
}

/// <inheritdoc />
public IConfigurationBuilder Add(IConfigurationSource source)
{
return _builder.Add(source);
}

/// <inheritdoc />
public IConfigurationRoot Build()
{
var configurationRoot = _builder.Build();
foreach (var keyValuePair in configurationRoot.AsEnumerable())
{
if (Options.TransformCondition(keyValuePair))
{
var transformValue = MorestachioConfig
.TransformValue(new KeyValuePair<string, string>(keyValuePair.Key, keyValuePair.Value), Options);
configurationRoot[transformValue.Key] = transformValue.Value;
}
}
return configurationRoot;
}

/// <inheritdoc />
public IDictionary<string, object> Properties
{
get { return _builder.Properties; }
}

/// <inheritdoc />
public IList<IConfigurationSource> Sources
{
get { return _builder.Sources; }
}

/// <inheritdoc />
public MorestachioConfigOptions Options { get; set; }
}
}
114 changes: 114 additions & 0 deletions Morestachio.Configuration.Transform/MorestachioConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Primitives;
using Morestachio.Framework.Expression.Parser;

namespace Morestachio.Configuration.Transform
{
/// <summary>
/// Wrapper for Runtime Morestachio configs
/// </summary>
public class MorestachioConfig : IConfiguration
{
protected IConfiguration Config { get; }
protected MorestachioConfigOptions Options { get; }

/// <summary>
///
/// </summary>
/// <param name="config"></param>
/// <param name="options"></param>
public MorestachioConfig(IConfiguration config, MorestachioConfigOptions options)
{
Config = config;
Options = options;
}

/// <inheritdoc />
public IConfigurationSection GetSection(string key)
{
return new MorestachioConfigSection(Config.GetSection(key), Options);
}

/// <inheritdoc />
public IEnumerable<IConfigurationSection> GetChildren()
{
return Config.GetChildren().Select(e => new MorestachioConfigSection(e, Options));
}

/// <inheritdoc />
public IChangeToken GetReloadToken()
{
return Config.GetReloadToken();
}

/// <inheritdoc />
public string this[string key]
{
get
{
return CheckAndTransformValue(key, Config[key], Options).Value;
}
set { Config[key] = value; }
}

/// <summary>
/// Checks if a key matches the <see cref="MorestachioConfigOptions.TransformCondition"/> and converts it
/// </summary>
/// <returns></returns>
public static KeyValuePair<string, string> CheckAndTransformValue(string key, string value, MorestachioConfigOptions options)
{
if (options.TransformCondition(new KeyValuePair<string, string>(key, value)))
{
return TransformValue(new KeyValuePair<string, string>(key, value), options);
}

return new KeyValuePair<string, string>(key, value);
}

/// <summary>
/// Transforms the value by using <see cref="Options"/>
/// </summary>
/// <returns></returns>
public static KeyValuePair<string, string> TransformValue(KeyValuePair<string, string> keyValue, MorestachioConfigOptions options)
{
keyValue = options.PreTransform(keyValue);
var parserOptions = options.ParserOptions();
var values = new Dictionary<string, object>();
if (options.Values.TryGetValue(string.Empty, out var rootValues))
{
foreach (var rootValue in rootValues)
{
values[rootValue.Key] = rootValue.Value;
}
}

IList<string> keyPaths = new List<string>();
foreach (var keyPathPart in keyValue.Key.Split(':'))
{
keyPaths.Add(keyPathPart);
var keyPath = string.Join(":", keyPaths);
if (options.Values.TryGetValue(keyPath, out var specificValues))
{
foreach (var specificValue in specificValues)
{
values[specificValue.Key] = specificValue.Value;
}
}
}
var valueTask = ExpressionParser.EvaluateExpression(keyValue.Value, parserOptions, values);
string result;
if (valueTask.IsCompleted)
{
result = valueTask.Result?.ToString();
}
else
{
result = valueTask.GetAwaiter().GetResult()?.ToString();
}

return options.PostTransform(new KeyValuePair<string, string>(keyValue.Key, result));
}
}
}
Loading

0 comments on commit e02a2d6

Please sign in to comment.