Skip to content

Commit 65b81fc

Browse files
committed
merge preview
2 parents 5f16867 + 5e6a012 commit 65b81fc

23 files changed

+1223
-473
lines changed

src/Microsoft.Azure.AppConfiguration.AspNetCore/Microsoft.Azure.AppConfiguration.AspNetCore.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
<!-- Nuget Package Version Settings -->
2222

2323
<PropertyGroup>
24-
<OfficialVersion>8.0.0</OfficialVersion>
24+
<OfficialVersion>8.1.0-preview</OfficialVersion>
2525
</PropertyGroup>
2626

2727
<PropertyGroup Condition="'$(CDP_PATCH_NUMBER)'!='' AND '$(CDP_BUILD_TYPE)'=='Official'">

src/Microsoft.Azure.AppConfiguration.Functions.Worker/Microsoft.Azure.AppConfiguration.Functions.Worker.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
<!-- Nuget Package Version Settings -->
2525

2626
<PropertyGroup>
27-
<OfficialVersion>8.0.0</OfficialVersion>
27+
<OfficialVersion>8.1.0-preview</OfficialVersion>
2828
</PropertyGroup>
2929

3030
<PropertyGroup Condition="'$(CDP_PATCH_NUMBER)'!='' AND '$(CDP_BUILD_TYPE)'=='Official'">

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

Lines changed: 83 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,13 @@ public class AzureAppConfigurationOptions
2424
private const int MaxRetries = 2;
2525
private static readonly TimeSpan MaxRetryDelay = TimeSpan.FromMinutes(1);
2626

27-
private List<KeyValueWatcher> _changeWatchers = new List<KeyValueWatcher>();
28-
private List<KeyValueWatcher> _multiKeyWatchers = new List<KeyValueWatcher>();
27+
private List<KeyValueWatcher> _individualKvWatchers = new List<KeyValueWatcher>();
28+
private List<KeyValueWatcher> _ffWatchers = new List<KeyValueWatcher>();
2929
private List<IKeyValueAdapter> _adapters;
3030
private List<Func<ConfigurationSetting, ValueTask<ConfigurationSetting>>> _mappers = new List<Func<ConfigurationSetting, ValueTask<ConfigurationSetting>>>();
31-
private List<KeyValueSelector> _kvSelectors = new List<KeyValueSelector>();
31+
private List<KeyValueSelector> _selectors;
3232
private IConfigurationRefresher _refresher = new AzureAppConfigurationRefresher();
33+
private bool _selectCalled = false;
3334

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

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

7081
/// <summary>
7182
/// A collection of <see cref="KeyValueWatcher"/>.
7283
/// </summary>
73-
internal IEnumerable<KeyValueWatcher> ChangeWatchers => _changeWatchers;
84+
internal IEnumerable<KeyValueWatcher> IndividualKvWatchers => _individualKvWatchers;
7485

7586
/// <summary>
7687
/// A collection of <see cref="KeyValueWatcher"/>.
7788
/// </summary>
78-
internal IEnumerable<KeyValueWatcher> MultiKeyWatchers => _multiKeyWatchers;
89+
internal IEnumerable<KeyValueWatcher> FeatureFlagWatchers => _ffWatchers;
7990

8091
/// <summary>
8192
/// A collection of <see cref="IKeyValueAdapter"/>.
@@ -97,11 +108,15 @@ internal IEnumerable<IKeyValueAdapter> Adapters
97108
internal IEnumerable<string> KeyPrefixes => _keyPrefixes;
98109

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

115+
/// <summary>
116+
/// For use in tests only. An optional class used to process pageable results from Azure App Configuration.
117+
/// </summary>
118+
internal IConfigurationSettingPageIterator ConfigurationSettingPageIterator { get; set; }
119+
105120
/// <summary>
106121
/// An optional timespan value to set the minimum backoff duration to a value other than the default.
107122
/// </summary>
@@ -148,6 +163,9 @@ public AzureAppConfigurationOptions()
148163
new JsonKeyValueAdapter(),
149164
new FeatureManagementKeyValueAdapter(FeatureFlagTracing)
150165
};
166+
167+
// Adds the default query to App Configuration if <see cref="Select"/> and <see cref="SelectSnapshot"/> are never called.
168+
_selectors = new List<KeyValueSelector> { new KeyValueSelector { KeyFilter = KeyFilter.Any, LabelFilter = LabelFilter.Null } };
151169
}
152170

153171
/// <summary>
@@ -187,22 +205,30 @@ public AzureAppConfigurationOptions Select(string keyFilter, string labelFilter
187205
throw new ArgumentNullException(nameof(keyFilter));
188206
}
189207

208+
// Do not support * and , for label filter for now.
209+
if (labelFilter != null && (labelFilter.Contains('*') || labelFilter.Contains(',')))
210+
{
211+
throw new ArgumentException("The characters '*' and ',' are not supported in label filters.", nameof(labelFilter));
212+
}
213+
190214
if (string.IsNullOrWhiteSpace(labelFilter))
191215
{
192216
labelFilter = LabelFilter.Null;
193217
}
194218

195-
// Do not support * and , for label filter for now.
196-
if (labelFilter.Contains('*') || labelFilter.Contains(','))
219+
if (!_selectCalled)
197220
{
198-
throw new ArgumentException("The characters '*' and ',' are not supported in label filters.", nameof(labelFilter));
221+
_selectors.Clear();
222+
223+
_selectCalled = true;
199224
}
200225

201-
_kvSelectors.AppendUnique(new KeyValueSelector
226+
_selectors.AppendUnique(new KeyValueSelector
202227
{
203228
KeyFilter = keyFilter,
204229
LabelFilter = labelFilter
205230
});
231+
206232
return this;
207233
}
208234

@@ -218,7 +244,14 @@ public AzureAppConfigurationOptions SelectSnapshot(string name)
218244
throw new ArgumentNullException(nameof(name));
219245
}
220246

221-
_kvSelectors.AppendUnique(new KeyValueSelector
247+
if (!_selectCalled)
248+
{
249+
_selectors.Clear();
250+
251+
_selectCalled = true;
252+
}
253+
254+
_selectors.AppendUnique(new KeyValueSelector
222255
{
223256
SnapshotName = name
224257
});
@@ -229,7 +262,7 @@ public AzureAppConfigurationOptions SelectSnapshot(string name)
229262
/// <summary>
230263
/// Configures options for Azure App Configuration feature flags that will be parsed and transformed into feature management configuration.
231264
/// 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.
265+
/// All loaded feature flags will be automatically registered for refresh as a collection.
233266
/// </summary>
234267
/// <param name="configure">A callback used to configure feature flag options.</param>
235268
public AzureAppConfigurationOptions UseFeatureFlags(Action<FeatureFlagOptions> configure = null)
@@ -254,25 +287,22 @@ public AzureAppConfigurationOptions UseFeatureFlags(Action<FeatureFlagOptions> c
254287
options.FeatureFlagSelectors.Add(new KeyValueSelector
255288
{
256289
KeyFilter = FeatureManagementConstants.FeatureFlagMarker + "*",
257-
LabelFilter = options.Label == null ? LabelFilter.Null : options.Label
290+
LabelFilter = string.IsNullOrWhiteSpace(options.Label) ? LabelFilter.Null : options.Label,
291+
IsFeatureFlagSelector = true
258292
});
259293
}
260294

261-
foreach (var featureFlagSelector in options.FeatureFlagSelectors)
295+
foreach (KeyValueSelector featureFlagSelector in options.FeatureFlagSelectors)
262296
{
263-
var featureFlagFilter = featureFlagSelector.KeyFilter;
264-
var labelFilter = featureFlagSelector.LabelFilter;
297+
_selectors.AppendUnique(featureFlagSelector);
265298

266-
Select(featureFlagFilter, labelFilter);
267-
268-
_multiKeyWatchers.AppendUnique(new KeyValueWatcher
299+
_ffWatchers.AppendUnique(new KeyValueWatcher
269300
{
270-
Key = featureFlagFilter,
271-
Label = labelFilter,
301+
Key = featureFlagSelector.KeyFilter,
302+
Label = featureFlagSelector.LabelFilter,
272303
// If UseFeatureFlags is called multiple times for the same key and label filters, last refresh interval wins
273304
RefreshInterval = options.RefreshInterval
274305
});
275-
276306
}
277307

278308
return this;
@@ -424,18 +454,41 @@ public AzureAppConfigurationOptions ConfigureClientOptions(Action<ConfigurationC
424454
/// <param name="configure">A callback used to configure Azure App Configuration refresh options.</param>
425455
public AzureAppConfigurationOptions ConfigureRefresh(Action<AzureAppConfigurationRefreshOptions> configure)
426456
{
457+
if (RegisterAllEnabled)
458+
{
459+
throw new InvalidOperationException($"{nameof(ConfigureRefresh)}() cannot be invoked multiple times when {nameof(AzureAppConfigurationRefreshOptions.RegisterAll)} has been invoked.");
460+
}
461+
427462
var refreshOptions = new AzureAppConfigurationRefreshOptions();
428463
configure?.Invoke(refreshOptions);
429464

430-
if (!refreshOptions.RefreshRegistrations.Any())
465+
bool isRegisterCalled = refreshOptions.RefreshRegistrations.Any();
466+
RegisterAllEnabled = refreshOptions.RegisterAllEnabled;
467+
468+
if (!isRegisterCalled && !RegisterAllEnabled)
469+
{
470+
throw new InvalidOperationException($"{nameof(ConfigureRefresh)}() must call either {nameof(AzureAppConfigurationRefreshOptions.Register)}()" +
471+
$" or {nameof(AzureAppConfigurationRefreshOptions.RegisterAll)}()");
472+
}
473+
474+
// Check if both register methods are called at any point
475+
if (RegisterAllEnabled && (_individualKvWatchers.Any() || isRegisterCalled))
431476
{
432-
throw new ArgumentException($"{nameof(ConfigureRefresh)}() must have at least one key-value registered for refresh.");
477+
throw new InvalidOperationException($"Cannot call both {nameof(AzureAppConfigurationRefreshOptions.RegisterAll)} and "
478+
+ $"{nameof(AzureAppConfigurationRefreshOptions.Register)}.");
433479
}
434480

435-
foreach (var item in refreshOptions.RefreshRegistrations)
481+
if (RegisterAllEnabled)
482+
{
483+
KvCollectionRefreshInterval = refreshOptions.RefreshInterval;
484+
}
485+
else
436486
{
437-
item.RefreshInterval = refreshOptions.RefreshInterval;
438-
_changeWatchers.Add(item);
487+
foreach (KeyValueWatcher item in refreshOptions.RefreshRegistrations)
488+
{
489+
item.RefreshInterval = refreshOptions.RefreshInterval;
490+
_individualKvWatchers.Add(item);
491+
}
439492
}
440493

441494
return this;

0 commit comments

Comments
 (0)