Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Microsoft.Extensions.Configuration
{
/// <summary>
/// Extension methods for <see cref="IConfigurationRoot"/>.
/// </summary>
public static class ConfigurationRootExtensions
{
/// <summary>
/// Generates a human-readable view of the configuration showing where each value came from.
/// </summary>
/// <returns> The debug view. </returns>
public static string GetDebugView(this IConfigurationRoot root)
{
void RecurseChildren(
StringBuilder stringBuilder,
IEnumerable<IConfigurationSection> children,
string indent)
{
foreach (var child in children)
{
var valueAndProvider = GetValueAndProvider(root, child.Path);

if (valueAndProvider.Provider != null)
{
stringBuilder
.Append(indent)
.Append(child.Key)
.Append("=")
.Append(valueAndProvider.Value)
.Append(" (")
.Append(valueAndProvider.Provider)
.AppendLine(")");
}
else
{
stringBuilder
.Append(indent)
.Append(child.Key)
.AppendLine(":");
}

RecurseChildren(stringBuilder, child.GetChildren(), indent + " ");
}
}

var builder = new StringBuilder();

RecurseChildren(builder, root.GetChildren(), "");

return builder.ToString();
}

private static (string Value, IConfigurationProvider Provider) GetValueAndProvider(
IConfigurationRoot root,
string key)
{
foreach (var provider in root.Providers.Reverse())
{
if (provider.TryGet(key, out var value))
{
return (value, provider);
}
}

return (null, null);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,4 @@ public interface IConfigurationProvider
/// <returns>The child keys.</returns>
IEnumerable<string> GetChildKeys(IEnumerable<string> earlierKeys, string parentPath);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;

namespace Microsoft.Extensions.Configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ namespace Microsoft.Extensions.Configuration.AzureKeyVault.Test
{
public class ConfigurationProviderKeyVaultTest : ConfigurationProviderTestBase
{
public override void Null_values_are_included_in_the_config()
{
AssertConfig(BuildConfigRoot(LoadThroughProvider(TestSection.NullsTestConfig)), expectNulls: true);
}

protected override (IConfigurationProvider Provider, System.Action Initializer) LoadThroughProvider(
TestSection testConfig)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,10 @@ public override void Load_from_single_provider_with_differing_case_duplicates_th
{
AssertConfig(BuildConfigRoot(LoadThroughProvider(TestSection.DuplicatesDifferentCaseTestConfig)));
}

public override void Null_values_are_included_in_the_config()
{
AssertConfig(BuildConfigRoot(LoadThroughProvider(TestSection.NullsTestConfig)), expectNulls: true);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ public FileConfigurationProvider(FileConfigurationSource source)
/// The source settings for this provider.
/// </summary>
public FileConfigurationSource Source { get; }

/// <summary>
/// Generates a string representing this provider name and relevant details.
/// </summary>
/// <returns> The configuration name. </returns>
public override string ToString()
=> $"{GetType().Name} for '{Source.Path}' ({(Source.Optional ? "Optional" : "Required")})";

private void Load(bool reload)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,15 @@ protected override (IConfigurationProvider Provider, Action Initializer) LoadThr

private void SectionToJson(StringBuilder jsonBuilder, TestSection section)
{
string ValueToJson(object value) => value == null ? "null" : $"'{value}'";

jsonBuilder.AppendLine("{");

foreach (var tuple in section.Values)
{
jsonBuilder.AppendLine(tuple.Value.AsArray != null
? $"'{tuple.Key}': [{string.Join(", ", tuple.Value.AsArray.Select(v => $"'{v}'"))}],"
: $"'{tuple.Key}': '{tuple.Value.AsString}',");
? $"'{tuple.Key}': [{string.Join(", ", tuple.Value.AsArray.Select(ValueToJson))}],"
: $"'{tuple.Key}': {ValueToJson(tuple.Value.AsString)},");
}

foreach (var tuple in section.Sections)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,8 @@ public override void Load()
{
return;
}
else
{
throw new DirectoryNotFoundException("A non-null file provider for the directory is required when this source is not optional.");
}

throw new DirectoryNotFoundException("A non-null file provider for the directory is required when this source is not optional.");
}

var directory = Source.FileProvider.GetDirectoryContents("/");
Expand All @@ -68,5 +66,15 @@ public override void Load()
}
}
}

private string GetDirectoryName()
=> Source.FileProvider?.GetFileInfo("/")?.PhysicalPath ?? "<Unknown>";

/// <summary>
/// Generates a string representing this provider name and relevant details.
/// </summary>
/// <returns> The configuration name. </returns>
public override string ToString()
=> $"{GetType().Name} for files in '{GetDirectoryName()}' ({(Source.Optional ? "Optional" : "Required")})";
}
}
81 changes: 16 additions & 65 deletions src/Configuration/Config.KeyPerFile/test/KeyPerFileTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,20 +190,11 @@ public TestFileProvider(params IFileInfo[] files)
_contents = new TestDirectoryContents(files);
}

public IDirectoryContents GetDirectoryContents(string subpath)
{
return _contents;
}
public IDirectoryContents GetDirectoryContents(string subpath) => _contents;

public IFileInfo GetFileInfo(string subpath)
{
throw new NotImplementedException();
}
public IFileInfo GetFileInfo(string subpath) => new TestFile("TestDirectory");

public IChangeToken Watch(string filter)
{
throw new NotImplementedException();
}
public IChangeToken Watch(string filter) => throw new NotImplementedException();
}

class TestDirectoryContents : IDirectoryContents
Expand All @@ -215,75 +206,33 @@ public TestDirectoryContents(params IFileInfo[] files)
_list = new List<IFileInfo>(files);
}

public bool Exists
{
get
{
return true;
}
}
public bool Exists => true;

public IEnumerator<IFileInfo> GetEnumerator()
{
return _list.GetEnumerator();
}
public IEnumerator<IFileInfo> GetEnumerator() => _list.GetEnumerator();

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

//TODO: Probably need a directory and file type.
class TestFile : IFileInfo
{
private string _name;
private string _contents;
private readonly string _name;
private readonly string _contents;

public bool Exists
{
get
{
return true;
}
}
public bool Exists => true;

public bool IsDirectory
{
get;
}

public DateTimeOffset LastModified
{
get
{
throw new NotImplementedException();
}
}
public DateTimeOffset LastModified => throw new NotImplementedException();

public long Length
{
get
{
throw new NotImplementedException();
}
}
public long Length => throw new NotImplementedException();

public string Name
{
get
{
return _name;
}
}
public string Name => _name;

public string PhysicalPath
{
get
{
throw new NotImplementedException();
}
}
public string PhysicalPath => "Root/" + Name;

public TestFile(string name)
{
Expand All @@ -304,7 +253,9 @@ public Stream CreateReadStream()
throw new InvalidOperationException("Cannot create stream from directory");
}

return new MemoryStream(Encoding.UTF8.GetBytes(_contents));
return _contents == null
? new MemoryStream()
: new MemoryStream(Encoding.UTF8.GetBytes(_contents));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,42 @@ namespace Microsoft.Extensions.Configuration.Xml.Test
{
public class ConfigurationProviderXmlTest : ConfigurationProviderTestBase
{
public override void Combine_before_other_provider()
{
// Disabled test due to XML handling of empty section.
}

public override void Combine_after_other_provider()
{
// Disabled test due to XML handling of empty section.
}

public override void Has_debug_view()
{
var configRoot = BuildConfigRoot(LoadThroughProvider(TestSection.TestConfig));
var providerTag = configRoot.Providers.Single().ToString();

var expected =
$@"Key1=Value1 ({providerTag})
Section1:
Key2=Value12 ({providerTag})
Section2:
Key3=Value123 ({providerTag})
Key3a:
0=ArrayValue0 ({providerTag})
Name=0 ({providerTag})
1=ArrayValue1 ({providerTag})
Name=1 ({providerTag})
2=ArrayValue2 ({providerTag})
Name=2 ({providerTag})
Section3:
Section4:
Key4=Value344 ({providerTag})
";

AssertDebugView(configRoot, expected);
}

protected override (IConfigurationProvider Provider, Action Initializer) LoadThroughProvider(TestSection testConfig)
{
var xmlBuilder = new StringBuilder();
Expand All @@ -36,7 +67,7 @@ private void SectionToXml(StringBuilder xmlBuilder, string sectionName, TestSect

foreach (var tuple in section.Values)
{
if (tuple.Value.AsString != null)
if (tuple.Value.AsArray == null)
{
xmlBuilder.AppendLine($"<{tuple.Key}>{tuple.Value.AsString}</{tuple.Key}>");
}
Expand Down
6 changes: 6 additions & 0 deletions src/Configuration/Config/src/ConfigurationProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,11 @@ protected void OnReload()
var previousToken = Interlocked.Exchange(ref _reloadToken, new ConfigurationReloadToken());
previousToken.OnReload();
}

/// <summary>
/// Generates a string representing this provider name and relevant details.
/// </summary>
/// <returns> The configuration name. </returns>
public override string ToString() => $"{GetType().Name}";
}
}
7 changes: 2 additions & 5 deletions src/Configuration/Config/src/ConfigurationRoot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace Microsoft.Extensions.Configuration
/// </summary>
public class ConfigurationRoot : IConfigurationRoot
{
private IList<IConfigurationProvider> _providers;
private readonly IList<IConfigurationProvider> _providers;
private ConfigurationReloadToken _changeToken = new ConfigurationReloadToken();

/// <summary>
Expand Down Expand Up @@ -52,17 +52,14 @@ public string this[string key]
{
foreach (var provider in _providers.Reverse())
{
string value;

if (provider.TryGet(key, out value))
if (provider.TryGet(key, out var value))
{
return value;
}
}

return null;
}

set
{
if (!_providers.Any())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Configuration
/// <summary>
/// Extensions method for <see cref="IConfigurationRoot"/>
/// </summary>
internal static class ConfigurationRootExtension
internal static class InternalConfigurationRootExtensions
{
/// <summary>
/// Gets the immediate children sub-sections of configuration root based on key.
Expand Down
Loading