Skip to content

Commit 48b090c

Browse files
committed
Allow ValidateKey() to provider intermediate key mapping for source.
1 parent f1f8e1a commit 48b090c

4 files changed

Lines changed: 132 additions & 7 deletions

File tree

src/Azure/AzureKeyVaultConfigBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ public override ICollection<KeyValuePair<string, string>> GetAllValues(string pr
148148
/// </summary>
149149
/// <param name="key">The string to be validated. May be partial.</param>
150150
/// <returns>True if the string is valid. False if the string is not a valid key.</returns>
151-
public override bool ValidateKey(string key)
151+
public override bool ValidateKey(ref string key)
152152
{
153153
// Key Vault only allows alphanumerics and '-'. This builder also allows for one '/'.
154154
return Regex.IsMatch(key, "^[a-zA-Z0-9-]+(/?[a-zA-Z0-9-]+)?$");

src/AzureAppConfig/AzureAppConfigurationBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ protected override void LazyInitialize(string name, NameValueCollection config)
100100
/// </summary>
101101
/// <param name="key">The string to be validated. May be partial.</param>
102102
/// <returns>True if the string is valid. False if the string is not a valid key.</returns>
103-
public override bool ValidateKey(string key)
103+
public override bool ValidateKey(ref string key)
104104
{
105105
// Azure App Config does not restrict key names, although a couple characters have special meaning if not escaped.
106106
// We may want to restrict using those characters unescaped in a key name in the future.

src/Base/KeyValueConfigBuilder.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public abstract class KeyValueConfigBuilder : ConfigurationBuilder
7373
/// </summary>
7474
/// <param name="key">The string to be validated. May be partial.</param>
7575
/// <returns>True if the string is valid. False if the string is not a valid key.</returns>
76-
public virtual bool ValidateKey(string key) { return true; }
76+
public virtual bool ValidateKey(ref string key) { return true; }
7777
/// <summary>
7878
/// Transforms the raw key to a new string just before updating items in Strict and Greedy modes.
7979
/// </summary>
@@ -244,13 +244,14 @@ private void EnsureGreedyInitialized()
244244
{
245245
// In Greedy mode, we need to know all the key/value pairs from this config source. So we
246246
// can't 'cache' them as we go along. Slurp them all up now. But only once. ;)
247-
if (!_greedyInitialized && (String.IsNullOrEmpty(KeyPrefix) || ValidateKey(KeyPrefix)))
247+
if (!_greedyInitialized)
248248
{
249+
string prefix = KeyPrefix; // Do this outside the lock. It ensures _cachedValues is initialized.
249250
lock (_cachedValues)
250251
{
251-
if (!_greedyInitialized)
252+
if (!_greedyInitialized && (String.IsNullOrEmpty(prefix) || ValidateKey(ref prefix)))
252253
{
253-
foreach (KeyValuePair<string, string> kvp in GetAllValues(KeyPrefix))
254+
foreach (KeyValuePair<string, string> kvp in GetAllValues(prefix))
254255
{
255256
_cachedValues.Add(kvp);
256257
}
@@ -302,7 +303,7 @@ private string GetValueInternal(string key)
302303
// ie <add key="MySetting" /> should only match the key/value (KeyPrefix + "MySetting") from the source.
303304
string sourceKey = (StripPrefix) ? KeyPrefix + key : key;
304305

305-
if (!ValidateKey(sourceKey))
306+
if (!ValidateKey(ref sourceKey))
306307
return null;
307308

308309
return (_cachedValues.ContainsKey(sourceKey)) ? _cachedValues[sourceKey] : _cachedValues[sourceKey] = GetValue(sourceKey);

test/Microsoft.Configuration.ConfigurationBuilders.Test/BaseTests.cs

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,113 @@ public void BaseBehavior_Greedy()
494494
Assert.Equal("Prefix_TestKey1Value", newSettings.Settings["Prefix_TestKey1"]?.Value);
495495
}
496496

497+
// ======================================================================
498+
// Extension Points
499+
// ======================================================================
500+
[Fact]
501+
public void Ext_KeyMapping()
502+
{
503+
// Strict
504+
var builder = new FakeKeyMappingConfigBuilder();
505+
builder.Initialize("test", new System.Collections.Specialized.NameValueCollection());
506+
AppSettingsSection newSettings = (AppSettingsSection)builder.ProcessConfigurationSection(GetAppSettings());
507+
Assert.Equal("TestKey1Value", newSettings.Settings["TestKey1"]?.Value);
508+
Assert.Equal("${TestKey1}", newSettings.Settings["test1"]?.Value);
509+
Assert.Equal("expandTestValue", newSettings.Settings["${TestKey1}"]?.Value);
510+
Assert.Equal("PrefixTest1", newSettings.Settings["TestKey"]?.Value);
511+
Assert.Null(newSettings.Settings["Prefix_TestKey"]?.Value);
512+
Assert.Equal("Prefix_TestKeyValue", newSettings.Settings["Prefix#TestKey"]?.Value);
513+
Assert.Equal("${Prefix_TestKey1}", newSettings.Settings["PreTest2"]?.Value);
514+
Assert.Equal("MappingTest1", newSettings.Settings["Prefix_Alt_Token"]?.Value);
515+
Assert.Null(newSettings.Settings["Alt#Token"]?.Value);
516+
Assert.Null(newSettings.Settings["Alt_Token"]?.Value);
517+
Assert.Equal("ThisWasADifferentAlternateTokenPattern", newSettings.Settings["Alt:Token"]?.Value);
518+
519+
// Strict with prefix
520+
builder = new FakeKeyMappingConfigBuilder();
521+
builder.Initialize("test", new System.Collections.Specialized.NameValueCollection() { { "prefix", "Prefix_" } });
522+
newSettings = (AppSettingsSection)builder.ProcessConfigurationSection(GetAppSettings());
523+
Assert.Equal("val1", newSettings.Settings["TestKey1"]?.Value);
524+
Assert.Equal("${TestKey1}", newSettings.Settings["test1"]?.Value);
525+
Assert.Equal("expandTestValue", newSettings.Settings["${TestKey1}"]?.Value);
526+
Assert.Equal("PrefixTest1", newSettings.Settings["TestKey"]?.Value);
527+
Assert.Null(newSettings.Settings["Prefix_TestKey"]?.Value);
528+
Assert.Equal("Prefix_TestKeyValue", newSettings.Settings["Prefix#TestKey"]?.Value);
529+
Assert.Equal("${Prefix_TestKey1}", newSettings.Settings["PreTest2"]?.Value);
530+
Assert.Equal("MappingTest1", newSettings.Settings["Prefix_Alt_Token"]?.Value);
531+
Assert.Null(newSettings.Settings["Alt#Token"]?.Value);
532+
Assert.Null(newSettings.Settings["Alt_Token"]?.Value);
533+
Assert.Equal("MappingTest2", newSettings.Settings["Alt:Token"]?.Value);
534+
535+
// Greedy
536+
builder = new FakeKeyMappingConfigBuilder();
537+
builder.Initialize("test", new System.Collections.Specialized.NameValueCollection() { { "mode", "Greedy" } });
538+
newSettings = (AppSettingsSection)builder.ProcessConfigurationSection(GetAppSettings());
539+
Assert.Equal("TestKey1Value", newSettings.Settings["TestKey1"]?.Value);
540+
Assert.Equal("TestKey2Value", newSettings.Settings["TestKey2"]?.Value);
541+
Assert.Equal("${TestKey1}", newSettings.Settings["test1"]?.Value);
542+
Assert.Equal("expandTestValue", newSettings.Settings["${TestKey1}"]?.Value);
543+
Assert.Equal("PrefixTest1", newSettings.Settings["TestKey"]?.Value);
544+
Assert.Null(newSettings.Settings["Prefix_TestKey"]?.Value);
545+
Assert.Equal("Prefix_TestKeyValue", newSettings.Settings["Prefix#TestKey"]?.Value);
546+
Assert.Null(newSettings.Settings["Prefix_TestKey1"]?.Value);
547+
Assert.Equal("Prefix_TestKey1Value", newSettings.Settings["Prefix#TestKey1"]?.Value);
548+
Assert.Equal("${Prefix_TestKey1}", newSettings.Settings["PreTest2"]?.Value);
549+
Assert.Equal("MappingTest1", newSettings.Settings["Prefix_Alt_Token"]?.Value);
550+
Assert.Equal("ThisWasAnAltTokenPatternWithPrefix", newSettings.Settings["Prefix#Alt:Token"]?.Value);
551+
Assert.Null(newSettings.Settings["Prefix_Alt:Token"]?.Value);
552+
Assert.Equal("ThisWasADifferentAlternateTokenPattern", newSettings.Settings["Alt#Token"]?.Value);
553+
Assert.Null(newSettings.Settings["Alt_Token"]?.Value);
554+
Assert.Equal("ThisWasAnAlternateTokenPattern", newSettings.Settings["Alt:Token"]?.Value);
555+
556+
// Greedy with prefix and stripping
557+
builder = new FakeKeyMappingConfigBuilder();
558+
builder.Initialize("test", new System.Collections.Specialized.NameValueCollection() { { "mode", "Greedy" }, { "prefix", "Prefix_" }, { "stripPrefix", "TRUE" } });
559+
newSettings = (AppSettingsSection)builder.ProcessConfigurationSection(GetAppSettings());
560+
Assert.Equal("Prefix_TestKey1Value", newSettings.Settings["TestKey1"]?.Value);
561+
Assert.Null(newSettings.Settings["TestKey2"]?.Value);
562+
Assert.Equal("${TestKey1}", newSettings.Settings["test1"]?.Value);
563+
Assert.Equal("expandTestValue", newSettings.Settings["${TestKey1}"]?.Value);
564+
Assert.Equal("Prefix_TestKeyValue", newSettings.Settings["TestKey"]?.Value);
565+
Assert.Equal("PrefixTest2", newSettings.Settings["Prefix_TestKey"]?.Value);
566+
Assert.Null(newSettings.Settings["Prefix#TestKey"]?.Value);
567+
Assert.Null(newSettings.Settings["Prefix_TestKey1"]?.Value);
568+
Assert.Null(newSettings.Settings["Prefix#TestKey1"]?.Value);
569+
Assert.Equal("${Prefix_TestKey1}", newSettings.Settings["PreTest2"]?.Value);
570+
Assert.Equal("MappingTest1", newSettings.Settings["Prefix_Alt_Token"]?.Value);
571+
Assert.Equal("ThisWasAnAltTokenPatternWithPrefix", newSettings.Settings["Alt:Token"]?.Value);
572+
Assert.Null(newSettings.Settings["Alt#Token"]?.Value);
573+
Assert.Null(newSettings.Settings["Alt_Token"]?.Value);
574+
575+
// Greedy with interesting prefix
576+
builder = new FakeKeyMappingConfigBuilder();
577+
builder.Initialize("test", new System.Collections.Specialized.NameValueCollection() { { "mode", "Greedy" }, { "prefix", "Prefix:" } });
578+
newSettings = (AppSettingsSection)builder.ProcessConfigurationSection(GetAppSettings());
579+
Assert.Equal("val1", newSettings.Settings["TestKey1"]?.Value);
580+
Assert.Null(newSettings.Settings["TestKey2"]?.Value);
581+
Assert.Equal("${TestKey1}", newSettings.Settings["test1"]?.Value);
582+
Assert.Equal("expandTestValue", newSettings.Settings["${TestKey1}"]?.Value);
583+
Assert.Equal("PrefixTest1", newSettings.Settings["TestKey"]?.Value);
584+
Assert.Null(newSettings.Settings["Prefix_TestKey"]?.Value);
585+
Assert.Null(newSettings.Settings["Prefix:TestKey"]?.Value);
586+
Assert.Equal("Prefix_TestKeyValue", newSettings.Settings["Prefix#TestKey"]?.Value);
587+
Assert.Null(newSettings.Settings["Prefix_TestKey1"]?.Value);
588+
Assert.Null(newSettings.Settings["Prefix:TestKey1"]?.Value);
589+
Assert.Equal("Prefix_TestKey1Value", newSettings.Settings["Prefix#TestKey1"]?.Value);
590+
Assert.Equal("${Prefix_TestKey1}", newSettings.Settings["PreTest2"]?.Value);
591+
Assert.Equal("MappingTest1", newSettings.Settings["Prefix_Alt_Token"]?.Value);
592+
Assert.Null(newSettings.Settings["Prefix_Alt:Token"]?.Value);
593+
Assert.Null(newSettings.Settings["Prefix_Alt#Token"]?.Value);
594+
Assert.Null(newSettings.Settings["Prefix#Alt_Token"]?.Value);
595+
Assert.Equal("ThisWasAnAltTokenPatternWithPrefix", newSettings.Settings["Prefix#Alt:Token"]?.Value);
596+
Assert.Null(newSettings.Settings["Prefix#Alt#Token"]?.Value);
597+
Assert.Null(newSettings.Settings["Prefix:Alt_Token"]?.Value);
598+
Assert.Null(newSettings.Settings["Prefix:Alt#Token"]?.Value);
599+
Assert.Null(newSettings.Settings["Prefix:Alt:Token"]?.Value);
600+
Assert.Equal("MappingTest2", newSettings.Settings["Alt:Token"]?.Value);
601+
Assert.Null(newSettings.Settings["Alt#Token"]?.Value);
602+
Assert.Null(newSettings.Settings["Alt_Token"]?.Value);
603+
}
497604

498605
// ======================================================================
499606
// Errors
@@ -605,6 +712,8 @@ AppSettingsSection GetAppSettings()
605712
appSettings.Settings.Add("TestKey", "PrefixTest1");
606713
appSettings.Settings.Add("Prefix_TestKey", "PrefixTest2");
607714
appSettings.Settings.Add("PreTest2", "${Prefix_TestKey1}");
715+
appSettings.Settings.Add("Prefix_Alt_Token", "MappingTest1");
716+
appSettings.Settings.Add("Alt:Token", "MappingTest2");
608717
return appSettings;
609718
}
610719

@@ -628,6 +737,7 @@ class FakeConfigBuilder : KeyValueConfigBuilder
628737
{ "Prefix_TestKey", "Prefix_TestKeyValue" },
629738
{ "Prefix_TestKey1", "Prefix_TestKey1Value" },
630739
{ "Alt:Token", "ThisWasAnAlternateTokenPattern" },
740+
{ "Alt_Token", "ThisWasADifferentAlternateTokenPattern" },
631741
{ "Prefix_Alt:Token", "ThisWasAnAltTokenPatternWithPrefix" }
632742
};
633743

@@ -673,4 +783,18 @@ public void SetTokenPattern(string newPattern)
673783
this.TokenPattern = newPattern;
674784
}
675785
}
786+
787+
class FakeKeyMappingConfigBuilder : FakeConfigBuilder
788+
{
789+
public override bool ValidateKey(ref string key)
790+
{
791+
key = key.Replace(":", "_");
792+
return true;
793+
}
794+
795+
public override string UpdateKey(string rawKey)
796+
{
797+
return rawKey.Replace("_", "#");
798+
}
799+
}
676800
}

0 commit comments

Comments
 (0)