-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4b30076
commit 8b1d1e2
Showing
13 changed files
with
487 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
|
||
Microsoft Visual Studio Solution File, Format Version 12.00 | ||
# Visual Studio Version 16 | ||
VisualStudioVersion = 16.0.30503.244 | ||
MinimumVisualStudioVersion = 15.0.26124.0 | ||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{DE5D710E-2B98-42F1-8E4B-E16A75C6D566}" | ||
EndProject | ||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests", "test\Extensionsions.Configuration.Object.UnitTests\UnitTests.csproj", "{7653901A-5E68-46EE-853E-CFA1AA9950F9}" | ||
EndProject | ||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Extensions.Configuration.Object", "src\Extensions.Configuration.Object\Extensions.Configuration.Object.csproj", "{DADDEA83-7AE9-4EF8-B826-1500FE514467}" | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|Any CPU = Debug|Any CPU | ||
Debug|x64 = Debug|x64 | ||
Debug|x86 = Debug|x86 | ||
Release|Any CPU = Release|Any CPU | ||
Release|x64 = Release|x64 | ||
Release|x86 = Release|x86 | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{7653901A-5E68-46EE-853E-CFA1AA9950F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{7653901A-5E68-46EE-853E-CFA1AA9950F9}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{7653901A-5E68-46EE-853E-CFA1AA9950F9}.Debug|x64.ActiveCfg = Debug|Any CPU | ||
{7653901A-5E68-46EE-853E-CFA1AA9950F9}.Debug|x64.Build.0 = Debug|Any CPU | ||
{7653901A-5E68-46EE-853E-CFA1AA9950F9}.Debug|x86.ActiveCfg = Debug|Any CPU | ||
{7653901A-5E68-46EE-853E-CFA1AA9950F9}.Debug|x86.Build.0 = Debug|Any CPU | ||
{7653901A-5E68-46EE-853E-CFA1AA9950F9}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{7653901A-5E68-46EE-853E-CFA1AA9950F9}.Release|Any CPU.Build.0 = Release|Any CPU | ||
{7653901A-5E68-46EE-853E-CFA1AA9950F9}.Release|x64.ActiveCfg = Release|Any CPU | ||
{7653901A-5E68-46EE-853E-CFA1AA9950F9}.Release|x64.Build.0 = Release|Any CPU | ||
{7653901A-5E68-46EE-853E-CFA1AA9950F9}.Release|x86.ActiveCfg = Release|Any CPU | ||
{7653901A-5E68-46EE-853E-CFA1AA9950F9}.Release|x86.Build.0 = Release|Any CPU | ||
{DADDEA83-7AE9-4EF8-B826-1500FE514467}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{DADDEA83-7AE9-4EF8-B826-1500FE514467}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{DADDEA83-7AE9-4EF8-B826-1500FE514467}.Debug|x64.ActiveCfg = Debug|Any CPU | ||
{DADDEA83-7AE9-4EF8-B826-1500FE514467}.Debug|x64.Build.0 = Debug|Any CPU | ||
{DADDEA83-7AE9-4EF8-B826-1500FE514467}.Debug|x86.ActiveCfg = Debug|Any CPU | ||
{DADDEA83-7AE9-4EF8-B826-1500FE514467}.Debug|x86.Build.0 = Debug|Any CPU | ||
{DADDEA83-7AE9-4EF8-B826-1500FE514467}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{DADDEA83-7AE9-4EF8-B826-1500FE514467}.Release|Any CPU.Build.0 = Release|Any CPU | ||
{DADDEA83-7AE9-4EF8-B826-1500FE514467}.Release|x64.ActiveCfg = Release|Any CPU | ||
{DADDEA83-7AE9-4EF8-B826-1500FE514467}.Release|x64.Build.0 = Release|Any CPU | ||
{DADDEA83-7AE9-4EF8-B826-1500FE514467}.Release|x86.ActiveCfg = Release|Any CPU | ||
{DADDEA83-7AE9-4EF8-B826-1500FE514467}.Release|x86.Build.0 = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(SolutionProperties) = preSolution | ||
HideSolutionNode = FALSE | ||
EndGlobalSection | ||
GlobalSection(NestedProjects) = preSolution | ||
{7653901A-5E68-46EE-853E-CFA1AA9950F9} = {DE5D710E-2B98-42F1-8E4B-E16A75C6D566} | ||
EndGlobalSection | ||
GlobalSection(ExtensibilityGlobals) = postSolution | ||
SolutionGuid = {4EC01313-2DCE-42CE-B022-1EA967A62372} | ||
EndGlobalSection | ||
EndGlobal |
26 changes: 26 additions & 0 deletions
26
src/Extensions.Configuration.Object/Extensions.Configuration.Object.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netstandard2.0</TargetFramework> | ||
<Company>Laurynas Lazauskas</Company> | ||
<Product>Extensions.Configuration.Object</Product> | ||
<Description>A simple way to add configuration using plain objects.</Description> | ||
<PackageLicenseExpression>MIT</PackageLicenseExpression> | ||
<PackageProjectUrl>https://github.com/lawrence-laz/Extensions.Configuration.Object</PackageProjectUrl> | ||
<RepositoryUrl>https://github.com/lawrence-laz/Extensions.Configuration.Object</RepositoryUrl> | ||
<PackageTags>configuration;options;object;extensions</PackageTags> | ||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> | ||
<GenerateDocumentationFile>true</GenerateDocumentationFile> | ||
<Copyright>Copyright 2020</Copyright> | ||
<Version>1.0.0</Version> | ||
<RepositoryType>git</RepositoryType> | ||
<PackageIcon>icon.png</PackageIcon> | ||
<PackageIconUrl /> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.8" /> | ||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.8" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace Extensions.Configuration.Object.Internal | ||
{ | ||
internal static class Constants | ||
{ | ||
public const string IssuesUrl = "https://github.com/lawrence-laz/Extensions.Configuration.Object/issues"; | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
src/Extensions.Configuration.Object/Internal/ReflectionExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
using System; | ||
using System.Reflection; | ||
using System.Runtime.CompilerServices; | ||
using System.Text.RegularExpressions; | ||
|
||
namespace Extensions.Configuration.Object.Internal | ||
{ | ||
internal static class ReflectionExtensions | ||
{ | ||
// Gets a sanitized name of a field. In case of anonymous objects name is unmangled, ex. "<MyField>i__Field" -> "MyField". | ||
public static string GetName(this FieldInfo field) | ||
{ | ||
if (field.DeclaringType.IsAnonymous()) | ||
{ | ||
var match = Regex.Match(field.Name, "<(.*)>", RegexOptions.Compiled); | ||
if (!match.Success || match.Groups.Count < 2 || !match.Groups[1].Success || string.IsNullOrWhiteSpace(match.Groups[1].Value)) | ||
{ | ||
throw new Exception($"Unsupported field name '{field.Name}'. Please report this to {Constants.IssuesUrl}."); | ||
} | ||
|
||
return match.Groups[1].Value; | ||
} | ||
|
||
return field.Name; | ||
} | ||
|
||
public static bool TryGetFields(this Type type, out FieldInfo[] fields) | ||
{ | ||
var bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static; | ||
fields = type.GetFields(bindingFlags); | ||
|
||
return fields.Length > 0; | ||
} | ||
|
||
public static bool IsAnonymous(this Type type) | ||
{ | ||
return Attribute.IsDefined(type, typeof(CompilerGeneratedAttribute), false) | ||
&& type.IsGenericType | ||
&& (type.Name.Contains("AnonymousType") || type.Name.Contains("AnonType")) | ||
&& (type.Name.StartsWith("<>") || type.Name.StartsWith("VB$")) | ||
&& type.Attributes.HasFlag(TypeAttributes.NotPublic); | ||
} | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
src/Extensions.Configuration.Object/ObjectConfigurationExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
using Microsoft.Extensions.Configuration; | ||
using System; | ||
|
||
namespace Extensions.Configuration.Object | ||
{ | ||
/// <summary> | ||
/// Extension methods for adding <see cref="ObjectConfigurationProvider"/>. | ||
/// </summary> | ||
public static class ObjectConfigurationExtensions | ||
{ | ||
/// <summary> | ||
/// Adds a <see cref="ObjectConfigurationProvider"/> to with a given configuration object. | ||
/// </summary> | ||
/// <param name="builder"><see cref="IConfigurationBuilder"/> to add provider to.</param> | ||
/// <param name="configurationObject">Configuration object with configuration values.</param> | ||
/// <returns>Same <see cref="IConfigurationBuilder"/> instance to continue configuration.</returns> | ||
public static IConfigurationBuilder AddObject(this IConfigurationBuilder builder, object configurationObject) | ||
{ | ||
if (builder is null) | ||
{ | ||
throw new ArgumentNullException(nameof(builder)); | ||
} | ||
|
||
if (configurationObject is null) | ||
{ | ||
throw new ArgumentNullException(nameof(configurationObject)); | ||
} | ||
|
||
return builder.Add((ObjectConfigurationSource source) => source.ConfigurationObject = configurationObject); | ||
} | ||
} | ||
} |
61 changes: 61 additions & 0 deletions
61
src/Extensions.Configuration.Object/ObjectConfigurationProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
using Extensions.Configuration.Object.Internal; | ||
using Microsoft.Extensions.Configuration; | ||
using System; | ||
|
||
namespace Extensions.Configuration.Object | ||
{ | ||
/// <summary> | ||
/// Loads configuration key/values from an object into a provider. | ||
/// </summary> | ||
public class ObjectConfigurationProvider : ConfigurationProvider | ||
{ | ||
/// <summary> | ||
/// Object used as a source for configuration. | ||
/// </summary> | ||
public object ConfigurationObject { get; set; } | ||
|
||
/// <summary> | ||
/// Creates an instance of <see cref="ObjectConfigurationProvider"/> using the provided configuration object. | ||
/// </summary> | ||
/// <param name="configurationObject">Object used as a source for configuration.</param> | ||
public ObjectConfigurationProvider(object configurationObject) | ||
{ | ||
ConfigurationObject = configurationObject ?? throw new ArgumentNullException(nameof(configurationObject)); | ||
} | ||
|
||
/// <summary> | ||
/// Recursively loads values from <see cref="ConfigurationObject"/> to this provider. | ||
/// </summary> | ||
public override void Load() | ||
{ | ||
LoadRecursively(null, ConfigurationObject); | ||
} | ||
|
||
private void LoadRecursively(string currentKey, object section) | ||
{ | ||
if (section is null) | ||
{ | ||
return; | ||
} | ||
|
||
if (section is string || section.GetType().IsPrimitive) | ||
{ | ||
base.Set(currentKey, section.ToString()); | ||
} | ||
else if (section.GetType().TryGetFields(out var fields)) | ||
{ | ||
foreach (var field in fields) | ||
{ | ||
var name = field.GetName(); | ||
var value = field.GetValue(section); | ||
|
||
var newKey = string.IsNullOrWhiteSpace(currentKey) | ||
? name | ||
: $"{currentKey}:{name}"; | ||
|
||
LoadRecursively(newKey, value); | ||
} | ||
} | ||
} | ||
} | ||
} |
31 changes: 31 additions & 0 deletions
31
src/Extensions.Configuration.Object/ObjectConfigurationSource.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
using Microsoft.Extensions.Configuration; | ||
using System; | ||
|
||
namespace Extensions.Configuration.Object | ||
{ | ||
/// <summary> | ||
/// Represents an anonymous, class or struct object as an <see cref="IConfigurationSource"/>. | ||
/// </summary> | ||
public class ObjectConfigurationSource : IConfigurationSource | ||
{ | ||
/// <summary> | ||
/// Object used as a source for configuration. | ||
/// </summary> | ||
public object ConfigurationObject { get; set; } | ||
|
||
/// <summary> | ||
/// Builds the <see cref="ObjectConfigurationProvider"/> for this source. | ||
/// </summary> | ||
/// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param> | ||
/// <returns>An <see cref="ObjectConfigurationProvider"/></returns> | ||
public IConfigurationProvider Build(IConfigurationBuilder builder) | ||
{ | ||
if (ConfigurationObject is null) | ||
{ | ||
throw new ArgumentNullException($"Property {ConfigurationObject} needs to be set before calling method {nameof(Build)}."); | ||
} | ||
|
||
return new ObjectConfigurationProvider(ConfigurationObject); | ||
} | ||
} | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
43 changes: 43 additions & 0 deletions
43
test/Extensionsions.Configuration.Object.UnitTests/AnonymousObjectTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
using FluentAssertions; | ||
using Microsoft.Extensions.Configuration; | ||
using Xunit; | ||
|
||
namespace Extensions.Configuration.Object.UnitTests | ||
{ | ||
public class AnonymousObjectTest | ||
{ | ||
[Fact] | ||
public void AddObject_WithAnonymousObject_ShouldLoadFieldsIntoConfiguration() | ||
{ | ||
var configuration = new ConfigurationBuilder() | ||
.AddObject(new | ||
{ | ||
Position = new | ||
{ | ||
Title = "Editor", | ||
Name = "Joe Smith", | ||
Age = 33 | ||
}, | ||
MyKey = "My appsettings.json Value", | ||
Logging = new | ||
{ | ||
LogLevel = new | ||
{ | ||
Default = "Information", | ||
Microsoft = "Warning" | ||
} | ||
}, | ||
AllowedHosts = "*" | ||
}) | ||
.Build(); | ||
|
||
configuration["Position:Title"].Should().Be("Editor"); | ||
configuration["Position:Name"].Should().Be("Joe Smith"); | ||
configuration["Position:Age"].Should().Be("33"); | ||
configuration["MyKey"].Should().Be("My appsettings.json Value"); | ||
configuration["Logging:LogLevel:Default"].Should().Be("Information"); | ||
configuration["Logging:LogLevel:Microsoft"].Should().Be("Warning"); | ||
configuration["AllowedHosts"].Should().Be("*"); | ||
} | ||
} | ||
} |
69 changes: 69 additions & 0 deletions
69
test/Extensionsions.Configuration.Object.UnitTests/ClassObjectTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
using FluentAssertions; | ||
using Microsoft.Extensions.Configuration; | ||
using Xunit; | ||
|
||
namespace Extensions.Configuration.Object.UnitTests | ||
{ | ||
public class ClassObjectTest | ||
{ | ||
internal class MyConfiguration | ||
{ | ||
public PositionConfiguration Position; | ||
public string MyKey; | ||
public LoggingConfiguration Logging; | ||
public string AllowedHosts; | ||
} | ||
|
||
internal class PositionConfiguration | ||
{ | ||
public string Title; | ||
public string Name; | ||
public int Age; | ||
} | ||
|
||
internal class LoggingConfiguration | ||
{ | ||
public LogLevelConfiguration LogLevel; | ||
} | ||
|
||
internal class LogLevelConfiguration | ||
{ | ||
public string Default; | ||
public string Microsoft; | ||
} | ||
|
||
[Fact] | ||
public void AddObject_WithAnonymousObject_ShouldLoadFieldsIntoConfiguration() | ||
{ | ||
var configuration = new ConfigurationBuilder() | ||
.AddObject(new MyConfiguration | ||
{ | ||
Position = new PositionConfiguration | ||
{ | ||
Title = "Editor", | ||
Name = "Joe Smith", | ||
Age = 33 | ||
}, | ||
MyKey = "My appsettings.json Value", | ||
Logging = new LoggingConfiguration | ||
{ | ||
LogLevel = new LogLevelConfiguration | ||
{ | ||
Default = "Information", | ||
Microsoft = "Warning" | ||
} | ||
}, | ||
AllowedHosts = "*" | ||
}) | ||
.Build(); | ||
|
||
configuration["Position:Title"].Should().Be("Editor"); | ||
configuration["Position:Name"].Should().Be("Joe Smith"); | ||
configuration["Position:Age"].Should().Be("33"); | ||
configuration["MyKey"].Should().Be("My appsettings.json Value"); | ||
configuration["Logging:LogLevel:Default"].Should().Be("Information"); | ||
configuration["Logging:LogLevel:Microsoft"].Should().Be("Warning"); | ||
configuration["AllowedHosts"].Should().Be("*"); | ||
} | ||
} | ||
} |
Oops, something went wrong.