Support setting WireMockServerSettings via Environment (#954)

* Support parsing environment variables (WireMockServerSettings__)

* case ignore

* fix

* SimpleSettingsParserTests

* .

* int

* more test
This commit is contained in:
Stef Heyenrath
2023-06-22 10:35:21 +02:00
committed by GitHub
parent 5d0bf6f4e1
commit 7ca70309cb
9 changed files with 306 additions and 122 deletions

View File

@@ -29,7 +29,7 @@ static class Program
XmlConfigurator.Configure(LogRepository, new FileInfo("log4net.config")); XmlConfigurator.Configure(LogRepository, new FileInfo("log4net.config"));
if (!WireMockServerSettingsParser.TryParseArguments(args, out var settings, new WireMockLog4NetLogger())) if (!WireMockServerSettingsParser.TryParseArguments(args, Environment.GetEnvironmentVariables(), out var settings, new WireMockLog4NetLogger()))
{ {
return; return;
} }

View File

@@ -1,3 +1,4 @@
using System;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@@ -24,7 +25,7 @@ public static class StandAloneApp
[PublicAPI] [PublicAPI]
public static WireMockServer Start(WireMockServerSettings settings) public static WireMockServer Start(WireMockServerSettings settings)
{ {
Guard.NotNull(settings, nameof(settings)); Guard.NotNull(settings);
var server = WireMockServer.Start(settings); var server = WireMockServer.Start(settings);
@@ -42,7 +43,7 @@ public static class StandAloneApp
[PublicAPI] [PublicAPI]
public static WireMockServer Start(string[] args, IWireMockLogger? logger = null) public static WireMockServer Start(string[] args, IWireMockLogger? logger = null)
{ {
Guard.NotNull(args, nameof(args)); Guard.NotNull(args);
if (TryStart(args, out var server, logger)) if (TryStart(args, out var server, logger))
{ {
@@ -61,9 +62,9 @@ public static class StandAloneApp
[PublicAPI] [PublicAPI]
public static bool TryStart(string[] args, [NotNullWhen(true)] out WireMockServer? server, IWireMockLogger? logger = null) public static bool TryStart(string[] args, [NotNullWhen(true)] out WireMockServer? server, IWireMockLogger? logger = null)
{ {
Guard.NotNull(args, nameof(args)); Guard.NotNull(args);
if (WireMockServerSettingsParser.TryParseArguments(args, out var settings, logger)) if (WireMockServerSettingsParser.TryParseArguments(args, Environment.GetEnvironmentVariables(), out var settings, logger))
{ {
settings.Logger?.Info("Version [{0}]", Version); settings.Logger?.Info("Version [{0}]", Version);
settings.Logger?.Debug("Server arguments [{0}]", string.Join(", ", args.Select(a => $"'{a}'"))); settings.Logger?.Debug("Server arguments [{0}]", string.Join(", ", args.Select(a => $"'{a}'")));

View File

@@ -0,0 +1,29 @@
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using Stef.Validation;
namespace WireMock.Extensions;
internal static class DictionaryExtensions
{
public static bool TryGetStringValue(this IDictionary dictionary, string key, [NotNullWhen(true)] out string? value)
{
Guard.NotNull(dictionary);
if (dictionary[key] is string valueIsString)
{
value = valueIsString;
return true;
}
var valueToString = dictionary[key]?.ToString();
if (valueToString != null)
{
value = valueToString;
return true;
}
value = default;
return false;
}
}

View File

@@ -1,17 +1,21 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using WireMock.Extensions;
namespace WireMock.Settings; namespace WireMock.Settings;
// Based on http://blog.gauffin.org/2014/12/simple-command-line-parser/ // Based on http://blog.gauffin.org/2014/12/simple-command-line-parser/
internal class SimpleCommandLineParser internal class SimpleSettingsParser
{ {
private const string Sigil = "--"; private const string Sigil = "--";
private const string Prefix = $"{nameof(WireMockServerSettings)}__";
private static readonly int PrefixLength = Prefix.Length;
private IDictionary<string, string[]> Arguments { get; } = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase); private IDictionary<string, string[]> Arguments { get; } = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
public void Parse(string[] arguments) public void Parse(string[] arguments, IDictionary? environment = null)
{ {
string currentName = string.Empty; string currentName = string.Empty;
@@ -44,6 +48,18 @@ internal class SimpleCommandLineParser
{ {
Arguments[currentName] = values.ToArray(); Arguments[currentName] = values.ToArray();
} }
// Now also parse environment
if (environment != null)
{
foreach (string key in environment.Keys)
{
if (key.StartsWith(Prefix, StringComparison.OrdinalIgnoreCase) && environment.TryGetStringValue(key, out var value))
{
Arguments[key.Substring(PrefixLength)] = value.Split(' ').ToArray();
}
}
}
} }
public bool Contains(string name) public bool Contains(string name)
@@ -85,7 +101,16 @@ internal class SimpleCommandLineParser
return Contains(name); return Contains(name);
} }
public int? GetIntValue(string name, int? defaultValue = null) public int? GetIntValue(string name)
{
return GetValue<int?>(name, values =>
{
var value = values.FirstOrDefault();
return !string.IsNullOrEmpty(value) ? int.Parse(value) : null;
}, null);
}
public int GetIntValue(string name, int defaultValue)
{ {
return GetValue(name, values => return GetValue(name, values =>
{ {

View File

@@ -22,6 +22,8 @@ namespace WireMock.Settings;
/// </summary> /// </summary>
public class WireMockServerSettings public class WireMockServerSettings
{ {
internal const int DefaultStartTimeout = 10000;
/// <summary> /// <summary>
/// Gets or sets the http port. /// Gets or sets the http port.
/// </summary> /// </summary>
@@ -81,7 +83,7 @@ public class WireMockServerSettings
/// StartTimeout /// StartTimeout
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public int StartTimeout { get; set; } = 10000; public int StartTimeout { get; set; } = DefaultStartTimeout;
/// <summary> /// <summary>
/// Allow Partial Mapping (default set to false). /// Allow Partial Mapping (default set to false).

View File

@@ -1,3 +1,4 @@
using System.Collections;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
@@ -18,16 +19,16 @@ public static class WireMockServerSettingsParser
/// Parse commandline arguments into WireMockServerSettings. /// Parse commandline arguments into WireMockServerSettings.
/// </summary> /// </summary>
/// <param name="args">The commandline arguments</param> /// <param name="args">The commandline arguments</param>
/// <param name="environment">The environment settings (optional)</param>
/// <param name="logger">The logger (optional, can be null)</param> /// <param name="logger">The logger (optional, can be null)</param>
/// <param name="settings">The parsed settings</param> /// <param name="settings">The parsed settings</param>
[PublicAPI] [PublicAPI]
public static bool TryParseArguments(string[] args, [NotNullWhen(true)] out WireMockServerSettings? settings, public static bool TryParseArguments(string[] args, IDictionary? environment, [NotNullWhen(true)] out WireMockServerSettings? settings, IWireMockLogger? logger = null)
IWireMockLogger? logger = null)
{ {
Guard.HasNoNulls(args); Guard.HasNoNulls(args);
var parser = new SimpleCommandLineParser(); var parser = new SimpleSettingsParser();
parser.Parse(args); parser.Parse(args, environment);
if (parser.GetBoolSwitchValue("help")) if (parser.GetBoolSwitchValue("help"))
{ {
@@ -55,6 +56,7 @@ public static class WireMockServerSettingsParser
RequestLogExpirationDuration = parser.GetIntValue("RequestLogExpirationDuration"), RequestLogExpirationDuration = parser.GetIntValue("RequestLogExpirationDuration"),
SaveUnmatchedRequests = parser.GetBoolValue(nameof(WireMockServerSettings.SaveUnmatchedRequests)), SaveUnmatchedRequests = parser.GetBoolValue(nameof(WireMockServerSettings.SaveUnmatchedRequests)),
StartAdminInterface = parser.GetBoolValue("StartAdminInterface", true), StartAdminInterface = parser.GetBoolValue("StartAdminInterface", true),
StartTimeout = parser.GetIntValue(nameof(WireMockServerSettings.StartTimeout), WireMockServerSettings.DefaultStartTimeout),
ThrowExceptionWhenMatcherFails = parser.GetBoolValue("ThrowExceptionWhenMatcherFails"), ThrowExceptionWhenMatcherFails = parser.GetBoolValue("ThrowExceptionWhenMatcherFails"),
UseRegexExtended = parser.GetBoolValue(nameof(WireMockServerSettings.UseRegexExtended), true), UseRegexExtended = parser.GetBoolValue(nameof(WireMockServerSettings.UseRegexExtended), true),
WatchStaticMappings = parser.GetBoolValue("WatchStaticMappings"), WatchStaticMappings = parser.GetBoolValue("WatchStaticMappings"),
@@ -79,8 +81,7 @@ public static class WireMockServerSettingsParser
return true; return true;
} }
private static void ParseLoggerSettings(WireMockServerSettings settings, IWireMockLogger? logger, private static void ParseLoggerSettings(WireMockServerSettings settings, IWireMockLogger? logger, SimpleSettingsParser parser)
SimpleCommandLineParser parser)
{ {
var loggerType = parser.GetStringValue("WireMockLogger"); var loggerType = parser.GetStringValue("WireMockLogger");
switch (loggerType) switch (loggerType)
@@ -103,7 +104,7 @@ public static class WireMockServerSettingsParser
} }
} }
private static void ParseProxyAndRecordSettings(WireMockServerSettings settings, SimpleCommandLineParser parser) private static void ParseProxyAndRecordSettings(WireMockServerSettings settings, SimpleSettingsParser parser)
{ {
var proxyUrl = parser.GetStringValue("ProxyURL") ?? parser.GetStringValue("ProxyUrl"); var proxyUrl = parser.GetStringValue("ProxyURL") ?? parser.GetStringValue("ProxyUrl");
if (!string.IsNullOrEmpty(proxyUrl)) if (!string.IsNullOrEmpty(proxyUrl))
@@ -135,7 +136,7 @@ public static class WireMockServerSettingsParser
} }
} }
private static void ParsePortSettings(WireMockServerSettings settings, SimpleCommandLineParser parser) private static void ParsePortSettings(WireMockServerSettings settings, SimpleSettingsParser parser)
{ {
if (parser.Contains(nameof(WireMockServerSettings.Port))) if (parser.Contains(nameof(WireMockServerSettings.Port)))
{ {
@@ -147,7 +148,7 @@ public static class WireMockServerSettingsParser
} }
} }
private static void ParseCertificateSettings(WireMockServerSettings settings, SimpleCommandLineParser parser) private static void ParseCertificateSettings(WireMockServerSettings settings, SimpleSettingsParser parser)
{ {
var certificateSettings = new WireMockCertificateSettings var certificateSettings = new WireMockCertificateSettings
{ {
@@ -163,7 +164,7 @@ public static class WireMockServerSettingsParser
} }
} }
private static void ParseWebProxyAddressSettings(ProxyAndRecordSettings settings, SimpleCommandLineParser parser) private static void ParseWebProxyAddressSettings(ProxyAndRecordSettings settings, SimpleSettingsParser parser)
{ {
string? proxyAddress = parser.GetStringValue("WebProxyAddress"); string? proxyAddress = parser.GetStringValue("WebProxyAddress");
if (!string.IsNullOrEmpty(proxyAddress)) if (!string.IsNullOrEmpty(proxyAddress))
@@ -177,7 +178,7 @@ public static class WireMockServerSettingsParser
} }
} }
private static void ParseProxyUrlReplaceSettings(ProxyAndRecordSettings settings, SimpleCommandLineParser parser) private static void ParseProxyUrlReplaceSettings(ProxyAndRecordSettings settings, SimpleSettingsParser parser)
{ {
var proxyUrlReplaceOldValue = parser.GetStringValue("ProxyUrlReplaceOldValue"); var proxyUrlReplaceOldValue = parser.GetStringValue("ProxyUrlReplaceOldValue");
var proxyUrlReplaceNewValue = parser.GetStringValue("ProxyUrlReplaceNewValue"); var proxyUrlReplaceNewValue = parser.GetStringValue("ProxyUrlReplaceNewValue");

View File

@@ -0,0 +1,51 @@
using System.Collections;
using FluentAssertions;
using WireMock.Extensions;
using Xunit;
namespace WireMock.Net.Tests.Extensions;
public class DictionaryExtensionsTests
{
[Fact]
public void TryGetStringValue_WhenKeyExistsAndValueIsString_ReturnsTrueAndStringValue()
{
// Arrange
var dictionary = new Hashtable { { "key", "value" } };
// Act
var result = dictionary.TryGetStringValue("key", out var value);
// Assert
result.Should().BeTrue();
value.Should().Be("value");
}
[Fact]
public void TryGetStringValue_WhenKeyExistsAndValueIsNotString_ReturnsTrueAndStringValue()
{
// Arrange
var dictionary = new Hashtable { { "key", 123 } };
// Act
var result = dictionary.TryGetStringValue("key", out var value);
// Assert
result.Should().BeTrue();
value.Should().Be("123");
}
[Fact]
public void TryGetStringValue_WhenKeyDoesNotExist_ReturnsFalseAndNull()
{
// Arrange
var dictionary = new Hashtable { { "otherKey", "value" } };
// Act
var result = dictionary.TryGetStringValue("key", out var value);
// Assert
result.Should().BeFalse();
value.Should().BeNull();
}
}

View File

@@ -1,102 +0,0 @@
using NFluent;
using WireMock.Settings;
using Xunit;
namespace WireMock.Net.Tests.Settings;
public class SimpleCommandLineParserTests
{
private readonly SimpleCommandLineParser _parser;
public SimpleCommandLineParserTests()
{
_parser = new SimpleCommandLineParser();
}
[Fact]
public void SimpleCommandLineParser_Parse_Arguments()
{
// Assign
_parser.Parse(new[] { "--test1", "one", "--test2", "two", "--test3", "three" });
// Act
string? value1 = _parser.GetStringValue("test1");
string? value2 = _parser.GetStringValue("test2");
string? value3 = _parser.GetStringValue("test3");
// Assert
Check.That(value1).IsEqualTo("one");
Check.That(value2).IsEqualTo("two");
Check.That(value3).IsEqualTo("three");
}
[Fact]
public void SimpleCommandLineParser_Parse_ArgumentsAsCombinedKeyAndValue()
{
// Assign
_parser.Parse(new[] { "--test1 one", "--test2 two", "--test3 three" });
// Act
string? value1 = _parser.GetStringValue("test1");
string? value2 = _parser.GetStringValue("test2");
string? value3 = _parser.GetStringValue("test3");
// Assert
Check.That(value1).IsEqualTo("one");
Check.That(value2).IsEqualTo("two");
Check.That(value3).IsEqualTo("three");
}
[Fact]
public void SimpleCommandLineParser_Parse_ArgumentsMixed()
{
// Assign
_parser.Parse(new[] { "--test1 one", "--test2", "two", "--test3 three" });
// Act
string? value1 = _parser.GetStringValue("test1");
string? value2 = _parser.GetStringValue("test2");
string? value3 = _parser.GetStringValue("test3");
// Assert
Check.That(value1).IsEqualTo("one");
Check.That(value2).IsEqualTo("two");
Check.That(value3).IsEqualTo("three");
}
[Fact]
public void SimpleCommandLineParser_Parse_GetBoolValue()
{
// Assign
_parser.Parse(new[] { "'--test1", "false'", "--test2 true" });
// Act
bool value1 = _parser.GetBoolValue("test1");
bool value2 = _parser.GetBoolValue("test2");
bool value3 = _parser.GetBoolValue("test3", true);
// Assert
Check.That(value1).IsEqualTo(false);
Check.That(value2).IsEqualTo(true);
Check.That(value3).IsEqualTo(true);
}
[Fact]
public void SimpleCommandLineParser_Parse_GetIntValue()
{
// Assign
_parser.Parse(new[] { "--test1", "42", "--test2 55" });
// Act
int? value1 = _parser.GetIntValue("test1");
int? value2 = _parser.GetIntValue("test2");
int? value3 = _parser.GetIntValue("test3", 100);
int? value4 = _parser.GetIntValue("test4");
// Assert
Check.That(value1).IsEqualTo(42);
Check.That(value2).IsEqualTo(55);
Check.That(value3).IsEqualTo(100);
Check.That(value4).IsNull();
}
}

View File

@@ -0,0 +1,177 @@
using System.Collections.Generic;
using FluentAssertions;
using NFluent;
using WireMock.Settings;
using WireMock.Types;
using Xunit;
namespace WireMock.Net.Tests.Settings;
public class SimpleSettingsParserTests
{
private readonly SimpleSettingsParser _parser;
public SimpleSettingsParserTests()
{
_parser = new SimpleSettingsParser();
}
[Fact]
public void SimpleCommandLineParser_Parse_Arguments()
{
// Assign
_parser.Parse(new[] { "--test1", "one", "--test2", "2", "--test3", "3", "--test4", "true", "--test5", "Https" });
// Act
string? stringValue = _parser.GetStringValue("test1");
int? intOptional = _parser.GetIntValue("test2");
int intWithDefault = _parser.GetIntValue("test3", 42);
bool? boolWithDefault = _parser.GetBoolValue("test4");
HostingScheme? enumOptional = _parser.GetEnumValue<HostingScheme>("test5");
HostingScheme enumWithDefault = _parser.GetEnumValue("test99", HostingScheme.HttpAndHttps);
// Assert
stringValue.Should().Be("one");
intOptional.Should().Be(2);
intWithDefault.Should().Be(3);
boolWithDefault.Should().Be(true);
enumOptional.Should().Be(HostingScheme.Https);
enumWithDefault.Should().Be(HostingScheme.HttpAndHttps);
}
[Fact]
public void SimpleCommandLineParser_Parse_Environment()
{
// Assign
var env = new Dictionary<string, string>
{
{ "WireMockServerSettings__test1", "one" },
{ "WireMockServerSettings__test2", "two" }
};
_parser.Parse(new string[0], env);
// Act
string? value1 = _parser.GetStringValue("test1");
string? value2 = _parser.GetStringValue("test2");
// Assert
Check.That(value1).IsEqualTo("one");
Check.That(value2).IsEqualTo("two");
}
[Fact]
public void SimpleCommandLineParser_Parse_ArgumentsAsCombinedKeyAndValue()
{
// Assign
_parser.Parse(new[] { "--test1 one", "--test2 two", "--test3 three" });
// Act
string? value1 = _parser.GetStringValue("test1");
string? value2 = _parser.GetStringValue("test2");
string? value3 = _parser.GetStringValue("test3");
// Assert
Check.That(value1).IsEqualTo("one");
Check.That(value2).IsEqualTo("two");
Check.That(value3).IsEqualTo("three");
}
[Fact]
public void SimpleCommandLineParser_Parse_ArgumentsMixed()
{
// Assign
_parser.Parse(new[] { "--test1 one", "--test2", "two", "--test3 three" });
// Act
string? value1 = _parser.GetStringValue("test1");
string? value2 = _parser.GetStringValue("test2");
string? value3 = _parser.GetStringValue("test3");
// Assert
Check.That(value1).IsEqualTo("one");
Check.That(value2).IsEqualTo("two");
Check.That(value3).IsEqualTo("three");
}
[Fact]
public void SimpleCommandLineParser_Parse_GetBoolValue()
{
// Assign
_parser.Parse(new[] { "'--test1", "false'", "--test2 true" });
// Act
bool value1 = _parser.GetBoolValue("test1");
bool value2 = _parser.GetBoolValue("test2");
bool value3 = _parser.GetBoolValue("test3", true);
// Assert
Check.That(value1).IsEqualTo(false);
Check.That(value2).IsEqualTo(true);
Check.That(value3).IsEqualTo(true);
}
[Fact]
public void SimpleCommandLineParser_Parse_Environment_GetBoolValue()
{
// Assign
var env = new Dictionary<string, string>
{
{ "WireMockServerSettings__test1", "false" },
{ "WireMockServerSettings__test2", "true" }
};
_parser.Parse(new string[0], env);
// Act
bool value1 = _parser.GetBoolValue("test1");
bool value2 = _parser.GetBoolValue("test2");
bool value3 = _parser.GetBoolValue("test3", true);
// Assert
Check.That(value1).IsEqualTo(false);
Check.That(value2).IsEqualTo(true);
Check.That(value3).IsEqualTo(true);
}
[Fact]
public void SimpleCommandLineParser_Parse_GetIntValue()
{
// Assign
_parser.Parse(new[] { "--test1", "42", "--test2 55" });
// Act
int? value1 = _parser.GetIntValue("test1");
int? value2 = _parser.GetIntValue("test2");
int? value3 = _parser.GetIntValue("test3", 100);
int? value4 = _parser.GetIntValue("test4");
// Assert
Check.That(value1).IsEqualTo(42);
Check.That(value2).IsEqualTo(55);
Check.That(value3).IsEqualTo(100);
Check.That(value4).IsNull();
}
[Fact]
public void SimpleCommandLineParser_Parse_Environment_GetIntValue()
{
// Assign
var env = new Dictionary<string, string>
{
{ "WireMockServerSettings__test1", "42" },
{ "WireMockServerSETTINGS__TEST2", "55" }
};
_parser.Parse(new string[0], env);
// Act
int? value1 = _parser.GetIntValue("test1");
int? value2 = _parser.GetIntValue("test2");
int? value3 = _parser.GetIntValue("test3", 100);
int? value4 = _parser.GetIntValue("test4");
// Assert
Check.That(value1).IsEqualTo(42);
Check.That(value2).IsEqualTo(55);
Check.That(value3).IsEqualTo(100);
Check.That(value4).IsNull();
}
}