-
Notifications
You must be signed in to change notification settings - Fork 38
Add RegisterAll
API to enable monitoring collections of key-values for refresh
#574
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 65 commits
a6e3300
15c53fe
75a01eb
f1b07b4
25c262c
302140b
cc1bc58
28ecc05
38ba768
67ec320
9f4c1de
937e012
7e17870
f4c17ae
65de1cd
25c477b
913587e
5f84295
e18a835
d41499e
85d9909
d6ac25b
5410b5d
fc752d3
8b02c76
3ebf1e4
26d3391
aa037b6
415c577
57f5b80
7ac2cd5
bbdac93
ec9a9da
e6aadf4
a122a10
36f0b80
ba8ad5f
13db869
d39a767
113143a
ebae7c9
05ede06
e584d6c
76f369b
4e0f949
8391d74
e20546d
655ec2d
6e39cac
d9bf761
b92ae2c
81f844c
6212daa
e27dea1
78b83d3
ee36891
569656f
d049c2d
1375b28
47cd0ba
fff31b3
b9a39c4
d8ae90f
3ab8672
c2bca82
6d1cc1a
aa4829b
9afe17a
69fb9cc
1155b78
9ed77af
cd62a68
beab237
1928357
9135d42
c55aa60
0b6aecb
8b3f49a
00ac014
d318127
396fe54
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,12 +23,14 @@ public class AzureAppConfigurationOptions | |
private const int MaxRetries = 2; | ||
private static readonly TimeSpan MaxRetryDelay = TimeSpan.FromMinutes(1); | ||
|
||
private List<KeyValueWatcher> _changeWatchers = new List<KeyValueWatcher>(); | ||
private List<KeyValueWatcher> _multiKeyWatchers = new List<KeyValueWatcher>(); | ||
private List<KeyValueWatcher> _individualKvWatchers = new List<KeyValueWatcher>(); | ||
private List<KeyValueWatcher> _ffWatchers = new List<KeyValueWatcher>(); | ||
private List<IKeyValueAdapter> _adapters; | ||
private List<Func<ConfigurationSetting, ValueTask<ConfigurationSetting>>> _mappers = new List<Func<ConfigurationSetting, ValueTask<ConfigurationSetting>>>(); | ||
private List<KeyValueSelector> _kvSelectors = new List<KeyValueSelector>(); | ||
private List<KeyValueSelector> _kvSelectors; | ||
private List<KeyValueSelector> _featureFlagSelectors = new List<KeyValueSelector>(); | ||
private IConfigurationRefresher _refresher = new AzureAppConfigurationRefresher(); | ||
private bool _selectCalled = false; | ||
|
||
// The following set is sorted in descending order. | ||
// Since multiple prefixes could start with the same characters, we need to trim the longest prefix first. | ||
|
@@ -62,19 +64,34 @@ public class AzureAppConfigurationOptions | |
internal TokenCredential Credential { get; private set; } | ||
|
||
/// <summary> | ||
/// A collection of <see cref="KeyValueSelector"/>. | ||
/// Key Value selectors specified by user. | ||
/// </summary> | ||
internal IEnumerable<KeyValueSelector> KeyValueSelectors => _kvSelectors; | ||
|
||
/// <summary> | ||
/// Feature Flag selectors specified by user. | ||
/// </summary> | ||
internal IEnumerable<KeyValueSelector> FeatureFlagSelectors => _featureFlagSelectors; | ||
|
||
/// <summary> | ||
/// Indicates if <see cref="AzureAppConfigurationRefreshOptions.RegisterAll"/> was called. | ||
/// </summary> | ||
internal bool RegisterAllEnabled { get; private set; } | ||
|
||
/// <summary> | ||
/// Refresh interval for selected key-value collections when <see cref="AzureAppConfigurationRefreshOptions.RegisterAll"/> is called. | ||
/// </summary> | ||
internal TimeSpan KvCollectionRefreshInterval { get; private set; } | ||
|
||
/// <summary> | ||
/// A collection of <see cref="KeyValueWatcher"/>. | ||
/// </summary> | ||
internal IEnumerable<KeyValueWatcher> ChangeWatchers => _changeWatchers; | ||
internal IEnumerable<KeyValueWatcher> IndividualKvWatchers => _individualKvWatchers; | ||
|
||
/// <summary> | ||
/// A collection of <see cref="KeyValueWatcher"/>. | ||
/// </summary> | ||
internal IEnumerable<KeyValueWatcher> MultiKeyWatchers => _multiKeyWatchers; | ||
internal IEnumerable<KeyValueWatcher> FeatureFlagWatchers => _ffWatchers; | ||
|
||
/// <summary> | ||
/// A collection of <see cref="IKeyValueAdapter"/>. | ||
|
@@ -101,6 +118,12 @@ internal IEnumerable<IKeyValueAdapter> Adapters | |
/// <remarks>This property is used only for unit testing.</remarks> | ||
internal IConfigurationClientManager ClientManager { get; set; } | ||
|
||
/// <summary> | ||
/// An optional class used to process pageable results from Azure App Configuration. | ||
/// </summary> | ||
/// <remarks>This property is only set outside of this class if it's used for unit testing.</remarks> | ||
|
||
internal ConfigurationSettingPageableManager PageableManager { get; set; } = new ConfigurationSettingPageableManager(); | ||
|
||
|
||
/// <summary> | ||
/// An optional timespan value to set the minimum backoff duration to a value other than the default. | ||
/// </summary> | ||
|
@@ -142,6 +165,9 @@ public AzureAppConfigurationOptions() | |
new JsonKeyValueAdapter(), | ||
new FeatureManagementKeyValueAdapter(FeatureFlagTracing) | ||
}; | ||
|
||
// Adds the default query to App Configuration if <see cref="Select"/> and <see cref="SelectSnapshot"/> are never called. | ||
_kvSelectors = new List<KeyValueSelector> { new KeyValueSelector { KeyFilter = KeyFilter.Any, LabelFilter = LabelFilter.Null } }; | ||
} | ||
|
||
/// <summary> | ||
|
@@ -170,22 +196,30 @@ public AzureAppConfigurationOptions Select(string keyFilter, string labelFilter | |
throw new ArgumentNullException(nameof(keyFilter)); | ||
} | ||
|
||
// Do not support * and , for label filter for now. | ||
if (labelFilter != null && (labelFilter.Contains('*') || labelFilter.Contains(','))) | ||
{ | ||
throw new ArgumentException("The characters '*' and ',' are not supported in label filters.", nameof(labelFilter)); | ||
} | ||
|
||
if (string.IsNullOrWhiteSpace(labelFilter)) | ||
{ | ||
labelFilter = LabelFilter.Null; | ||
} | ||
|
||
// Do not support * and , for label filter for now. | ||
if (labelFilter.Contains('*') || labelFilter.Contains(',')) | ||
if (!_selectCalled) | ||
{ | ||
throw new ArgumentException("The characters '*' and ',' are not supported in label filters.", nameof(labelFilter)); | ||
_kvSelectors.Clear(); | ||
|
||
_selectCalled = true; | ||
} | ||
|
||
_kvSelectors.AppendUnique(new KeyValueSelector | ||
{ | ||
KeyFilter = keyFilter, | ||
LabelFilter = labelFilter | ||
}); | ||
|
||
return this; | ||
} | ||
|
||
|
@@ -201,6 +235,13 @@ public AzureAppConfigurationOptions SelectSnapshot(string name) | |
throw new ArgumentNullException(nameof(name)); | ||
} | ||
|
||
if (!_selectCalled) | ||
{ | ||
_kvSelectors.Clear(); | ||
|
||
_selectCalled = true; | ||
} | ||
|
||
_kvSelectors.AppendUnique(new KeyValueSelector | ||
{ | ||
SnapshotName = name | ||
|
@@ -212,7 +253,7 @@ public AzureAppConfigurationOptions SelectSnapshot(string name) | |
/// <summary> | ||
/// Configures options for Azure App Configuration feature flags that will be parsed and transformed into feature management configuration. | ||
/// If no filtering is specified via the <see cref="FeatureFlagOptions"/> then all feature flags with no label are loaded. | ||
/// All loaded feature flags will be automatically registered for refresh on an individual flag level. | ||
/// All loaded feature flags will be automatically registered for refresh as a collection. | ||
/// </summary> | ||
/// <param name="configure">A callback used to configure feature flag options.</param> | ||
public AzureAppConfigurationOptions UseFeatureFlags(Action<FeatureFlagOptions> configure = null) | ||
|
@@ -237,25 +278,21 @@ public AzureAppConfigurationOptions UseFeatureFlags(Action<FeatureFlagOptions> c | |
options.FeatureFlagSelectors.Add(new KeyValueSelector | ||
{ | ||
KeyFilter = FeatureManagementConstants.FeatureFlagMarker + "*", | ||
LabelFilter = options.Label == null ? LabelFilter.Null : options.Label | ||
LabelFilter = string.IsNullOrWhiteSpace(options.Label) ? LabelFilter.Null : options.Label | ||
}); | ||
} | ||
|
||
avanigupta marked this conversation as resolved.
Show resolved
Hide resolved
|
||
foreach (var featureFlagSelector in options.FeatureFlagSelectors) | ||
foreach (KeyValueSelector featureFlagSelector in options.FeatureFlagSelectors) | ||
{ | ||
var featureFlagFilter = featureFlagSelector.KeyFilter; | ||
var labelFilter = featureFlagSelector.LabelFilter; | ||
|
||
Select(featureFlagFilter, labelFilter); | ||
_featureFlagSelectors.AppendUnique(featureFlagSelector); | ||
|
||
_multiKeyWatchers.AppendUnique(new KeyValueWatcher | ||
_ffWatchers.AppendUnique(new KeyValueWatcher | ||
{ | ||
Key = featureFlagFilter, | ||
Label = labelFilter, | ||
Key = featureFlagSelector.KeyFilter, | ||
Label = featureFlagSelector.LabelFilter, | ||
// If UseFeatureFlags is called multiple times for the same key and label filters, last refresh interval wins | ||
RefreshInterval = options.RefreshInterval | ||
}); | ||
|
||
} | ||
|
||
return this; | ||
|
@@ -376,18 +413,40 @@ public AzureAppConfigurationOptions ConfigureClientOptions(Action<ConfigurationC | |
/// <param name="configure">A callback used to configure Azure App Configuration refresh options.</param> | ||
public AzureAppConfigurationOptions ConfigureRefresh(Action<AzureAppConfigurationRefreshOptions> configure) | ||
{ | ||
if (RegisterAllEnabled) | ||
{ | ||
throw new ArgumentException($"{nameof(ConfigureRefresh)}() cannot be invoked multiple times when {nameof(AzureAppConfigurationRefreshOptions.RegisterAll)} has been invoked."); | ||
|
||
} | ||
|
||
var refreshOptions = new AzureAppConfigurationRefreshOptions(); | ||
configure?.Invoke(refreshOptions); | ||
|
||
if (!refreshOptions.RefreshRegistrations.Any()) | ||
bool isRegisterCalled = refreshOptions.RefreshRegistrations.Any(); | ||
RegisterAllEnabled = refreshOptions.RegisterAllEnabled; | ||
|
||
if (!isRegisterCalled && !RegisterAllEnabled) | ||
{ | ||
throw new ArgumentException($"{nameof(ConfigureRefresh)}() must have at least one key-value registered for refresh."); | ||
throw new ArgumentException($"{nameof(ConfigureRefresh)}() must register at least one key-value for refresh or enable refresh of all selected key-values."); | ||
|
||
} | ||
|
||
foreach (var item in refreshOptions.RefreshRegistrations) | ||
// Check if both register methods are called at any point | ||
if (RegisterAllEnabled && (_individualKvWatchers.Any() || isRegisterCalled)) | ||
{ | ||
item.RefreshInterval = refreshOptions.RefreshInterval; | ||
_changeWatchers.Add(item); | ||
throw new ArgumentException($"Cannot call both {nameof(AzureAppConfigurationRefreshOptions.RegisterAll)} and " | ||
|
||
+ $"{nameof(AzureAppConfigurationRefreshOptions.Register)}."); | ||
} | ||
|
||
if (RegisterAllEnabled) | ||
{ | ||
KvCollectionRefreshInterval = refreshOptions.RefreshInterval; | ||
} | ||
else | ||
{ | ||
foreach (KeyValueWatcher item in refreshOptions.RefreshRegistrations) | ||
{ | ||
item.RefreshInterval = refreshOptions.RefreshInterval; | ||
_individualKvWatchers.Add(item); | ||
} | ||
} | ||
|
||
return this; | ||
|
Uh oh!
There was an error while loading. Please reload this page.