Skip to content

Commit 8d17a5b

Browse files
author
Nate McMaster
committed
Merge source code from aspnet/Configuration into this repo
\n\nCommit migrated from dotnet/extensions@8a9b64a
2 parents 2fa46e6 + 09e84b2 commit 8d17a5b

8 files changed

+505
-0
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<Project>
2+
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory)..\, Directory.Build.props))\Directory.Build.props" />
3+
4+
<PropertyGroup>
5+
<GenerateDocumentationFile Condition=" '$(IsTestProject)' != 'true' AND '$(IsSampleProject)' != 'true' ">true</GenerateDocumentationFile>
6+
<PackageTags>configuration</PackageTags>
7+
</PropertyGroup>
8+
</Project>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System;
2+
using System.IO;
3+
using Microsoft.Extensions.Configuration.KeyPerFile;
4+
using Microsoft.Extensions.FileProviders;
5+
6+
namespace Microsoft.Extensions.Configuration
7+
{
8+
/// <summary>
9+
/// Extension methods for registering <see cref="KeyPerFileConfigurationProvider"/> with <see cref="IConfigurationBuilder"/>.
10+
/// </summary>
11+
public static class KeyPerFileConfigurationBuilderExtensions
12+
{
13+
/// <summary>
14+
/// Adds configuration using files from a directory. File names are used as the key,
15+
/// file contents are used as the value.
16+
/// </summary>
17+
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
18+
/// <param name="directoryPath">The path to the directory.</param>
19+
/// <param name="optional">Whether the directory is optional.</param>
20+
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
21+
public static IConfigurationBuilder AddKeyPerFile(this IConfigurationBuilder builder, string directoryPath, bool optional)
22+
=> builder.AddKeyPerFile(source =>
23+
{
24+
// Only try to set the file provider if its not optional or the directory exists
25+
if (!optional || Directory.Exists(directoryPath))
26+
{
27+
source.FileProvider = new PhysicalFileProvider(directoryPath);
28+
}
29+
source.Optional = optional;
30+
});
31+
32+
/// <summary>
33+
/// Adds configuration using files from a directory. File names are used as the key,
34+
/// file contents are used as the value.
35+
/// </summary>
36+
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
37+
/// <param name="configureSource">Configures the source.</param>
38+
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
39+
public static IConfigurationBuilder AddKeyPerFile(this IConfigurationBuilder builder, Action<KeyPerFileConfigurationSource> configureSource)
40+
=> builder.Add(configureSource);
41+
}
42+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
5+
namespace Microsoft.Extensions.Configuration.KeyPerFile
6+
{
7+
/// <summary>
8+
/// A <see cref="ConfigurationProvider"/> that uses a directory's files as configuration key/values.
9+
/// </summary>
10+
public class KeyPerFileConfigurationProvider : ConfigurationProvider
11+
{
12+
KeyPerFileConfigurationSource Source { get; set; }
13+
14+
/// <summary>
15+
/// Initializes a new instance.
16+
/// </summary>
17+
/// <param name="source">The settings.</param>
18+
public KeyPerFileConfigurationProvider(KeyPerFileConfigurationSource source)
19+
=> Source = source ?? throw new ArgumentNullException(nameof(source));
20+
21+
private static string NormalizeKey(string key)
22+
=> key.Replace("__", ConfigurationPath.KeyDelimiter);
23+
24+
private static string TrimNewLine(string value)
25+
=> value.EndsWith(Environment.NewLine)
26+
? value.Substring(0, value.Length - Environment.NewLine.Length)
27+
: value;
28+
29+
/// <summary>
30+
/// Loads the docker secrets.
31+
/// </summary>
32+
public override void Load()
33+
{
34+
Data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
35+
36+
if (Source.FileProvider == null)
37+
{
38+
if (Source.Optional)
39+
{
40+
return;
41+
}
42+
else
43+
{
44+
throw new DirectoryNotFoundException("A non-null file provider for the directory is required when this source is not optional.");
45+
}
46+
}
47+
48+
var directory = Source.FileProvider.GetDirectoryContents("/");
49+
if (!directory.Exists && !Source.Optional)
50+
{
51+
throw new DirectoryNotFoundException("The root directory for the FileProvider doesn't exist and is not optional.");
52+
}
53+
54+
foreach (var file in directory)
55+
{
56+
if (file.IsDirectory)
57+
{
58+
continue;
59+
}
60+
61+
using (var stream = file.CreateReadStream())
62+
using (var streamReader = new StreamReader(stream))
63+
{
64+
if (Source.IgnoreCondition == null || !Source.IgnoreCondition(file.Name))
65+
{
66+
Data.Add(NormalizeKey(file.Name), TrimNewLine(streamReader.ReadToEnd()));
67+
}
68+
}
69+
}
70+
}
71+
}
72+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using System;
2+
using System.IO;
3+
using Microsoft.Extensions.FileProviders;
4+
5+
namespace Microsoft.Extensions.Configuration.KeyPerFile
6+
{
7+
/// <summary>
8+
/// An <see cref="IConfigurationSource"/> used to configure <see cref="KeyPerFileConfigurationProvider"/>.
9+
/// </summary>
10+
public class KeyPerFileConfigurationSource : IConfigurationSource
11+
{
12+
/// <summary>
13+
/// Constructor;
14+
/// </summary>
15+
public KeyPerFileConfigurationSource()
16+
=> IgnoreCondition = s => IgnorePrefix != null && s.StartsWith(IgnorePrefix);
17+
18+
/// <summary>
19+
/// The FileProvider whos root "/" directory files will be used as configuration data.
20+
/// </summary>
21+
public IFileProvider FileProvider { get; set; }
22+
23+
/// <summary>
24+
/// Files that start with this prefix will be excluded.
25+
/// Defaults to "ignore.".
26+
/// </summary>
27+
public string IgnorePrefix { get; set; } = "ignore.";
28+
29+
/// <summary>
30+
/// Used to determine if a file should be ignored using its name.
31+
/// Defaults to using the IgnorePrefix.
32+
/// </summary>
33+
public Func<string, bool> IgnoreCondition { get; set; }
34+
35+
/// <summary>
36+
/// If false, will throw if the directory doesn't exist.
37+
/// </summary>
38+
public bool Optional { get; set; }
39+
40+
/// <summary>
41+
/// Builds the <see cref="KeyPerFileConfigurationProvider"/> for this source.
42+
/// </summary>
43+
/// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
44+
/// <returns>A <see cref="KeyPerFileConfigurationProvider"/></returns>
45+
public IConfigurationProvider Build(IConfigurationBuilder builder)
46+
=> new KeyPerFileConfigurationProvider(this);
47+
}
48+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<Description>Configuration provider that uses files in a directory for Microsoft.Extensions.Configuration.</Description>
5+
<TargetFramework>netstandard2.0</TargetFramework>
6+
<EnableApiCheck>false</EnableApiCheck>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<Reference Include="Microsoft.Extensions.Configuration" />
11+
<Reference Include="Microsoft.Extensions.FileProviders.Physical" />
12+
</ItemGroup>
13+
14+
</Project>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
This is a configuration provider that uses a directory's files as data. A file's name is the key and the contents are the value.

0 commit comments

Comments
 (0)