Skip to content

Commit

Permalink
fix broken file loading
Browse files Browse the repository at this point in the history
* Base path was not set causing an NPR
* improve env var and command line handling
* Add sections to docs
  • Loading branch information
rofr committed Jul 6, 2020
1 parent 65912ac commit 136487b
Show file tree
Hide file tree
Showing 10 changed files with 228 additions and 19 deletions.
22 changes: 12 additions & 10 deletions Fig.Core/EnvironmentVarsSettingsSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,24 @@ namespace Fig.Core
{
public class EnvironmentVarsSettingsSource : SettingsSource
{
private string _prefix;
private char _separator;
private readonly string _prefix;

private const char Separator = '_';

private IDictionary _vars;
private readonly bool _dropPrefix;
private readonly IDictionary _vars;


public EnvironmentVarsSettingsSource(string prefix = "", char separator = '_')
:this(Environment.GetEnvironmentVariables(), prefix, separator)
public EnvironmentVarsSettingsSource(string prefix, bool dropPrefix)
:this(Environment.GetEnvironmentVariables(), prefix, dropPrefix)
{
}

internal EnvironmentVarsSettingsSource(IDictionary vars, string prefix = "", char separator = '_')
internal EnvironmentVarsSettingsSource(IDictionary vars, string prefix, bool dropPrefix)
{
_vars = vars;
_prefix = prefix;
_separator = separator;
_dropPrefix = dropPrefix;
}

/// <summary>
Expand All @@ -38,9 +40,9 @@ internal EnvironmentVarsSettingsSource(IDictionary vars, string prefix = "", cha

foreach (var key in filteredKeys)
{
var transformedKey = key
.Remove(0, _prefix.Length)
.Replace('_', '.');
var transformedKey = key;
if (_dropPrefix) transformedKey = key.Remove(0, _prefix.Length);
transformedKey = transformedKey.Replace(Separator, '.');

yield return (transformedKey, (string) _vars[key]);
}
Expand Down
18 changes: 13 additions & 5 deletions Fig.Core/SettingsBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ private readonly CompositeSettingsDictionary _compositeDictionary
private string _environment = "";

/// <summary>
/// Where to look for files
/// Where to look for files, default is current working directory
/// </summary>
private string _basePath;
private string _basePath = "";

public Settings Build()
{
Expand Down Expand Up @@ -46,9 +46,17 @@ public SettingsBuilder UseCommandLine(string[] args, string prefix = "--fig:", c
return this;
}

public SettingsBuilder UseEnvironmentVariables(string prefix)
/// <summary>
/// Add all the current environment variables that start with a given prefix
/// <remarks>The path separator (default is _) will be translated to a dot</remarks>
/// </summary>
/// <param name="prefix">For example FIG_, default is no prefix. </param>
/// <param name="separator">Default is _</param>
/// <param name="dropPrefix">When true, the prefix will not be included in the key</param>
/// <returns></returns>
public SettingsBuilder UseEnvironmentVariables(string prefix = "", bool dropPrefix = true)
{
Add(new EnvironmentVarsSettingsSource(prefix).ToSettingsDictionary());
Add(new EnvironmentVarsSettingsSource(prefix, dropPrefix).ToSettingsDictionary());
return this;
}

Expand All @@ -66,7 +74,7 @@ protected internal void AddFileBasedSource(Func<string, SettingsSource> sourceFa
var source = sourceFactory.Invoke(fullPath);
Add(source.ToSettingsDictionary());
}
else if (required) throw new FileNotFoundException("No such file", fullPath);
else if (required) throw new FileNotFoundException("No such file: " + fullPath, fullPath);

}

Expand Down
2 changes: 0 additions & 2 deletions Fig.Core/StringArraySource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@ public class StringArraySource : SettingsSource
{
private readonly Regex _parser;
private readonly string[] _args;
private readonly string _prefix;

public StringArraySource(string[] args, string prefix, char delimiter)
{
_prefix = prefix;
_args = args;
var pattern =
"^" +
Expand Down
50 changes: 50 additions & 0 deletions Fig.Tests/BuilderTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;
using NUnit.Framework;

namespace Fig.Test
{
public class BuilderTests
{
[Test]
public void CanLoadRequiredFile()
{
var settings = new SettingsBuilder()
.UseAppSettingsJson("appsettings.json", required: true)
.Build();
Assert.DoesNotThrow(() => settings.Get("AllowedHosts"));
}

[Test]
public void CanLoadRequiredFileWithVariableExpansion()
{
Environment.SetEnvironmentVariable("ENV", "TEST");
var settings = new SettingsBuilder()
.UseEnvironmentVariables()
.UseAppSettingsJson("appsettings.${ENV}.json", required: true)
.Build();
Assert.DoesNotThrow(() => settings.Get("AllowedHosts"));
}

[Test]
public void CanReferenceOptionalNonExistentFile()
{
Assert.DoesNotThrow(() =>
{
var settings = new SettingsBuilder()
.UseAppSettingsJson("nonexistent.json", required: false)
.Build();
});
}

[Test]
public void CanReferenceOptionalNonExistentFileWithVariableExpansion()
{
Assert.DoesNotThrow(() =>
{
var settings = new SettingsBuilder()
.UseAppSettingsJson("nonexistent.${ENV}.json", required: false)
.Build();
});
}
}
}
35 changes: 35 additions & 0 deletions Fig.Tests/CommandLineArgsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using NUnit.Framework;

namespace Fig.Test
{
public class CommandLineArgsTests
{
[Test]
public void WithPrefix()
{
var args = new[] {"--fig:Color=red", "--fig:Size=4", "other=20"};
var settings = new SettingsBuilder().UseCommandLine(args).Build();
var colorExists = settings.SettingsDictionary.TryGetValue("Color", "", out _);
var sizeExists = settings.SettingsDictionary.TryGetValue("Size", "", out _);
var prefixlessNotPresent = settings.SettingsDictionary.TryGetValue("Other", "", out _);

Assert.True(colorExists);
Assert.True(sizeExists);
Assert.False(prefixlessNotPresent);
}

[Test]
public void WithoutPrefix()
{
var args = new[] {"myApp.Color=red", "Size=4", "--other=20"};
var settings = new SettingsBuilder().UseCommandLine(args, prefix:"").Build();
var colorExists = settings.SettingsDictionary.TryGetValue("myApp.Color", "", out _);
var sizeExists = settings.SettingsDictionary.TryGetValue("Size", "", out _);
var nonMatchingKey = settings.SettingsDictionary.TryGetValue("--Other", "", out _);

Assert.True(colorExists);
Assert.True(sizeExists);
Assert.False(nonMatchingKey);
}
}
}
35 changes: 35 additions & 0 deletions Fig.Tests/EnvironmentVarsSourceTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System.Collections.Generic;
using Fig.Core;
using NUnit.Framework;

namespace Fig.Test
{
public class EnvironmentVarsSourceTests
{
[Test]
public void WithDroppedPrefix()
{
var dictionary = new Dictionary<string, string>()
{
["FIG_A"] = "fig.a",

};
var source = new EnvironmentVarsSettingsSource(dictionary, "FIG_", dropPrefix: true);
var settings = source.ToSettingsDictionary();
Assert.True(settings.ContainsKey("a"));
}

[Test]
public void WithRetainedPrefix()
{
var dictionary = new Dictionary<string, string>()
{
["FIG_A"] = "fig.a",

};
var source = new EnvironmentVarsSettingsSource(dictionary, "FIG_", dropPrefix: false);
var settings = source.ToSettingsDictionary();
Assert.True(settings.ContainsKey("fig.a"));
}
}
}
3 changes: 3 additions & 0 deletions Fig.Tests/Fig.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
<None Include="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="appsettings.TEST.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<None Update="appsettings.json">
Expand Down
26 changes: 26 additions & 0 deletions Fig.Tests/appsettings.TEST.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"Timeout" : 42,
"ConnectionStrings": {
"DefaultConnection": "DataSource=app.db"
},
"Servers" : ["10.0.0.1", "10.0.0.2"],
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"Simpsons" : [
{ "Name" : "Bart", "Age" : 12},
{ "Name" : "Homer", "Age" : 35}
],

"EnvQualified:PROD" : {
"a" : 1,
"b" : 1
},
"EnvQualified:TEST" : {
"a" : 2,
"b" : 2
}
}
54 changes: 53 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,58 @@ Datasource.A.name
Datasource.A.connectionstring
```

## Environment variables
Given these environment variables:
```bash
export ENV=TEST
export FIG_TIMEOUT=30
export FIG_LOGGING_LOGLEVEL_DEFAULT=Warning
export MYAPP_COLOR=Red
export MYAPP_ENDPOINT=10.0.0.1:3001
```
`builder.UseEnvironmentVariables(prefix: "FIG_", dropPrefix:true)` yields:
```
TIMEOUT
LOGGING.LOGLEVEL.DEFAULT
```
`builder.UseEnvironmentVariables(prefix: "MYAPP_", dropPrefix:false)` yields:
```
MYAPP.COLOR
MYAPP.ENDPOINT
```

`builder.UseEnvironmentVariables()` yields:
```
ENV
FIG.TIMEOUT
FIG.LOGGING.LOGLEVEL.DEFAULT
MYAPP.COLOR
MYAPP.ENDPOINT
```


## Command line
Fig can take key/value paires passed on the command line. The default prefix is "--fig:" and default separator is "="
```c#
//Given
string[] args = new []{
"--fig:ENV=Test",
"--fig:Timeout=30",
"retries=3"};
```

`settingsBuilder.UseCommandLine(args)` yields:

```
ENV
Timeout
```

and `settingsBuilder.UseCommandLine(args, prefix: "")` yields:
```
retries
```

# Combining sources
Use the `SettingsBuilder` to add sources in order of precedence.
Settings in above layers override settings with the same key in
Expand All @@ -196,7 +248,7 @@ lower layers. Note that this is the opposite order of `Microsoft.Extensions.Conf
```c#
var settings = new SettingsBuilder()
.UseCommandLine(args)
.UseEnvironmentVariables()
.UseEnvironmentVariables(prefix: "FIG_", dropPrefix:false)
.UseAppSettingsJson("appSettings.${ENV}.json", optional:true)
.UseAppSettingsJson("appSettings.json")
.Build<Settings>();
Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: 1.7.{build}
version: 1.8.{build}
image: Ubuntu1804
assembly_info:
patch: true
Expand Down

0 comments on commit 136487b

Please sign in to comment.