Skip to content

Commit 4dd994f

Browse files
Merge pull request #619 from Azure/refactor-preview-to-main
Revert ConfigurationClient factory changes, add RegisterAll
2 parents 40768ff + c740e7c commit 4dd994f

22 files changed

+943
-597
lines changed

src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationClientFactory.cs

Lines changed: 0 additions & 74 deletions
This file was deleted.

src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationOptions.cs

Lines changed: 82 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
//
44
using Azure.Core;
55
using Azure.Data.AppConfiguration;
6-
using Microsoft.Extensions.Azure;
76
using Microsoft.Extensions.Configuration.AzureAppConfiguration.AzureKeyVault;
87
using Microsoft.Extensions.Configuration.AzureAppConfiguration.Extensions;
98
using Microsoft.Extensions.Configuration.AzureAppConfiguration.FeatureManagement;
@@ -24,12 +23,13 @@ public class AzureAppConfigurationOptions
2423
private const int MaxRetries = 2;
2524
private static readonly TimeSpan MaxRetryDelay = TimeSpan.FromMinutes(1);
2625

27-
private List<KeyValueWatcher> _changeWatchers = new List<KeyValueWatcher>();
28-
private List<KeyValueWatcher> _multiKeyWatchers = new List<KeyValueWatcher>();
26+
private List<KeyValueWatcher> _individualKvWatchers = new List<KeyValueWatcher>();
27+
private List<KeyValueWatcher> _ffWatchers = new List<KeyValueWatcher>();
2928
private List<IKeyValueAdapter> _adapters;
3029
private List<Func<ConfigurationSetting, ValueTask<ConfigurationSetting>>> _mappers = new List<Func<ConfigurationSetting, ValueTask<ConfigurationSetting>>>();
31-
private List<KeyValueSelector> _kvSelectors = new List<KeyValueSelector>();
30+
private List<KeyValueSelector> _selectors;
3231
private IConfigurationRefresher _refresher = new AzureAppConfigurationRefresher();
32+
private bool _selectCalled = false;
3333

3434
// The following set is sorted in descending order.
3535
// Since multiple prefixes could start with the same characters, we need to trim the longest prefix first.
@@ -63,19 +63,29 @@ public class AzureAppConfigurationOptions
6363
internal TokenCredential Credential { get; private set; }
6464

6565
/// <summary>
66-
/// A collection of <see cref="KeyValueSelector"/>.
66+
/// A collection of <see cref="KeyValueSelector"/> specified by user.
6767
/// </summary>
68-
internal IEnumerable<KeyValueSelector> KeyValueSelectors => _kvSelectors;
68+
internal IEnumerable<KeyValueSelector> Selectors => _selectors;
69+
70+
/// <summary>
71+
/// Indicates if <see cref="AzureAppConfigurationRefreshOptions.RegisterAll"/> was called.
72+
/// </summary>
73+
internal bool RegisterAllEnabled { get; private set; }
74+
75+
/// <summary>
76+
/// Refresh interval for selected key-value collections when <see cref="AzureAppConfigurationRefreshOptions.RegisterAll"/> is called.
77+
/// </summary>
78+
internal TimeSpan KvCollectionRefreshInterval { get; private set; }
6979

7080
/// <summary>
7181
/// A collection of <see cref="KeyValueWatcher"/>.
7282
/// </summary>
73-
internal IEnumerable<KeyValueWatcher> ChangeWatchers => _changeWatchers;
83+
internal IEnumerable<KeyValueWatcher> IndividualKvWatchers => _individualKvWatchers;
7484

7585
/// <summary>
7686
/// A collection of <see cref="KeyValueWatcher"/>.
7787
/// </summary>
78-
internal IEnumerable<KeyValueWatcher> MultiKeyWatchers => _multiKeyWatchers;
88+
internal IEnumerable<KeyValueWatcher> FeatureFlagWatchers => _ffWatchers;
7989

8090
/// <summary>
8191
/// A collection of <see cref="IKeyValueAdapter"/>.
@@ -97,11 +107,15 @@ internal IEnumerable<IKeyValueAdapter> Adapters
97107
internal IEnumerable<string> KeyPrefixes => _keyPrefixes;
98108

99109
/// <summary>
100-
/// An optional configuration client manager that can be used to provide clients to communicate with Azure App Configuration.
110+
/// For use in tests only. An optional configuration client manager that can be used to provide clients to communicate with Azure App Configuration.
101111
/// </summary>
102-
/// <remarks>This property is used only for unit testing.</remarks>
103112
internal IConfigurationClientManager ClientManager { get; set; }
104113

114+
/// <summary>
115+
/// For use in tests only. An optional class used to process pageable results from Azure App Configuration.
116+
/// </summary>
117+
internal IConfigurationSettingPageIterator ConfigurationSettingPageIterator { get; set; }
118+
105119
/// <summary>
106120
/// An optional timespan value to set the minimum backoff duration to a value other than the default.
107121
/// </summary>
@@ -132,11 +146,6 @@ internal IEnumerable<IKeyValueAdapter> Adapters
132146
/// </summary>
133147
internal StartupOptions Startup { get; set; } = new StartupOptions();
134148

135-
/// <summary>
136-
/// Client factory that is responsible for creating instances of ConfigurationClient.
137-
/// </summary>
138-
internal IAzureClientFactory<ConfigurationClient> ClientFactory { get; private set; }
139-
140149
/// <summary>
141150
/// Initializes a new instance of the <see cref="AzureAppConfigurationOptions"/> class.
142151
/// </summary>
@@ -148,17 +157,9 @@ public AzureAppConfigurationOptions()
148157
new JsonKeyValueAdapter(),
149158
new FeatureManagementKeyValueAdapter(FeatureFlagTracing)
150159
};
151-
}
152160

153-
/// <summary>
154-
/// Sets the client factory used to create ConfigurationClient instances.
155-
/// </summary>
156-
/// <param name="factory">The client factory.</param>
157-
/// <returns>The current <see cref="AzureAppConfigurationOptions"/> instance.</returns>
158-
public AzureAppConfigurationOptions SetClientFactory(IAzureClientFactory<ConfigurationClient> factory)
159-
{
160-
ClientFactory = factory ?? throw new ArgumentNullException(nameof(factory));
161-
return this;
161+
// Adds the default query to App Configuration if <see cref="Select"/> and <see cref="SelectSnapshot"/> are never called.
162+
_selectors = new List<KeyValueSelector> { new KeyValueSelector { KeyFilter = KeyFilter.Any, LabelFilter = LabelFilter.Null } };
162163
}
163164

164165
/// <summary>
@@ -187,22 +188,30 @@ public AzureAppConfigurationOptions Select(string keyFilter, string labelFilter
187188
throw new ArgumentNullException(nameof(keyFilter));
188189
}
189190

191+
// Do not support * and , for label filter for now.
192+
if (labelFilter != null && (labelFilter.Contains('*') || labelFilter.Contains(',')))
193+
{
194+
throw new ArgumentException("The characters '*' and ',' are not supported in label filters.", nameof(labelFilter));
195+
}
196+
190197
if (string.IsNullOrWhiteSpace(labelFilter))
191198
{
192199
labelFilter = LabelFilter.Null;
193200
}
194201

195-
// Do not support * and , for label filter for now.
196-
if (labelFilter.Contains('*') || labelFilter.Contains(','))
202+
if (!_selectCalled)
197203
{
198-
throw new ArgumentException("The characters '*' and ',' are not supported in label filters.", nameof(labelFilter));
204+
_selectors.Clear();
205+
206+
_selectCalled = true;
199207
}
200208

201-
_kvSelectors.AppendUnique(new KeyValueSelector
209+
_selectors.AppendUnique(new KeyValueSelector
202210
{
203211
KeyFilter = keyFilter,
204212
LabelFilter = labelFilter
205213
});
214+
206215
return this;
207216
}
208217

@@ -218,7 +227,14 @@ public AzureAppConfigurationOptions SelectSnapshot(string name)
218227
throw new ArgumentNullException(nameof(name));
219228
}
220229

221-
_kvSelectors.AppendUnique(new KeyValueSelector
230+
if (!_selectCalled)
231+
{
232+
_selectors.Clear();
233+
234+
_selectCalled = true;
235+
}
236+
237+
_selectors.AppendUnique(new KeyValueSelector
222238
{
223239
SnapshotName = name
224240
});
@@ -229,7 +245,7 @@ public AzureAppConfigurationOptions SelectSnapshot(string name)
229245
/// <summary>
230246
/// Configures options for Azure App Configuration feature flags that will be parsed and transformed into feature management configuration.
231247
/// If no filtering is specified via the <see cref="FeatureFlagOptions"/> then all feature flags with no label are loaded.
232-
/// All loaded feature flags will be automatically registered for refresh on an individual flag level.
248+
/// All loaded feature flags will be automatically registered for refresh as a collection.
233249
/// </summary>
234250
/// <param name="configure">A callback used to configure feature flag options.</param>
235251
public AzureAppConfigurationOptions UseFeatureFlags(Action<FeatureFlagOptions> configure = null)
@@ -254,25 +270,22 @@ public AzureAppConfigurationOptions UseFeatureFlags(Action<FeatureFlagOptions> c
254270
options.FeatureFlagSelectors.Add(new KeyValueSelector
255271
{
256272
KeyFilter = FeatureManagementConstants.FeatureFlagMarker + "*",
257-
LabelFilter = options.Label == null ? LabelFilter.Null : options.Label
273+
LabelFilter = string.IsNullOrWhiteSpace(options.Label) ? LabelFilter.Null : options.Label,
274+
IsFeatureFlagSelector = true
258275
});
259276
}
260277

261-
foreach (var featureFlagSelector in options.FeatureFlagSelectors)
278+
foreach (KeyValueSelector featureFlagSelector in options.FeatureFlagSelectors)
262279
{
263-
var featureFlagFilter = featureFlagSelector.KeyFilter;
264-
var labelFilter = featureFlagSelector.LabelFilter;
280+
_selectors.AppendUnique(featureFlagSelector);
265281

266-
Select(featureFlagFilter, labelFilter);
267-
268-
_multiKeyWatchers.AppendUnique(new KeyValueWatcher
282+
_ffWatchers.AppendUnique(new KeyValueWatcher
269283
{
270-
Key = featureFlagFilter,
271-
Label = labelFilter,
284+
Key = featureFlagSelector.KeyFilter,
285+
Label = featureFlagSelector.LabelFilter,
272286
// If UseFeatureFlags is called multiple times for the same key and label filters, last refresh interval wins
273287
RefreshInterval = options.RefreshInterval
274288
});
275-
276289
}
277290

278291
return this;
@@ -393,18 +406,41 @@ public AzureAppConfigurationOptions ConfigureClientOptions(Action<ConfigurationC
393406
/// <param name="configure">A callback used to configure Azure App Configuration refresh options.</param>
394407
public AzureAppConfigurationOptions ConfigureRefresh(Action<AzureAppConfigurationRefreshOptions> configure)
395408
{
409+
if (RegisterAllEnabled)
410+
{
411+
throw new InvalidOperationException($"{nameof(ConfigureRefresh)}() cannot be invoked multiple times when {nameof(AzureAppConfigurationRefreshOptions.RegisterAll)} has been invoked.");
412+
}
413+
396414
var refreshOptions = new AzureAppConfigurationRefreshOptions();
397415
configure?.Invoke(refreshOptions);
398416

399-
if (!refreshOptions.RefreshRegistrations.Any())
417+
bool isRegisterCalled = refreshOptions.RefreshRegistrations.Any();
418+
RegisterAllEnabled = refreshOptions.RegisterAllEnabled;
419+
420+
if (!isRegisterCalled && !RegisterAllEnabled)
421+
{
422+
throw new InvalidOperationException($"{nameof(ConfigureRefresh)}() must call either {nameof(AzureAppConfigurationRefreshOptions.Register)}()" +
423+
$" or {nameof(AzureAppConfigurationRefreshOptions.RegisterAll)}()");
424+
}
425+
426+
// Check if both register methods are called at any point
427+
if (RegisterAllEnabled && (_individualKvWatchers.Any() || isRegisterCalled))
400428
{
401-
throw new ArgumentException($"{nameof(ConfigureRefresh)}() must have at least one key-value registered for refresh.");
429+
throw new InvalidOperationException($"Cannot call both {nameof(AzureAppConfigurationRefreshOptions.RegisterAll)} and "
430+
+ $"{nameof(AzureAppConfigurationRefreshOptions.Register)}.");
402431
}
403432

404-
foreach (var item in refreshOptions.RefreshRegistrations)
433+
if (RegisterAllEnabled)
405434
{
406-
item.RefreshInterval = refreshOptions.RefreshInterval;
407-
_changeWatchers.Add(item);
435+
KvCollectionRefreshInterval = refreshOptions.RefreshInterval;
436+
}
437+
else
438+
{
439+
foreach (KeyValueWatcher item in refreshOptions.RefreshRegistrations)
440+
{
441+
item.RefreshInterval = refreshOptions.RefreshInterval;
442+
_individualKvWatchers.Add(item);
443+
}
408444
}
409445

410446
return this;

0 commit comments

Comments
 (0)