diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationOptions.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationOptions.cs
index a3dbfa05..db3b6c3d 100644
--- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationOptions.cs
+++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationOptions.cs
@@ -1,517 +1,524 @@
-// Copyright (c) Microsoft Corporation.
-// Licensed under the MIT license.
-//
-using Azure.Core;
-using Azure.Data.AppConfiguration;
-using Microsoft.Extensions.Configuration.AzureAppConfiguration.AzureKeyVault;
-using Microsoft.Extensions.Configuration.AzureAppConfiguration.Extensions;
-using Microsoft.Extensions.Configuration.AzureAppConfiguration.FeatureManagement;
-using Microsoft.Extensions.Configuration.AzureAppConfiguration.Models;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-
-namespace Microsoft.Extensions.Configuration.AzureAppConfiguration
-{
- ///
- /// Options used to configure the behavior of an Azure App Configuration provider.
- /// If neither nor is ever called, all key-values with no label are included in the configuration provider.
- ///
- public class AzureAppConfigurationOptions
- {
- private const int MaxRetries = 2;
- private static readonly TimeSpan MaxRetryDelay = TimeSpan.FromMinutes(1);
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+//
+using Azure.Core;
+using Azure.Core.Pipeline;
+using Azure.Data.AppConfiguration;
+using Microsoft.Extensions.Configuration.AzureAppConfiguration.AzureKeyVault;
+using Microsoft.Extensions.Configuration.AzureAppConfiguration.Extensions;
+using Microsoft.Extensions.Configuration.AzureAppConfiguration.FeatureManagement;
+using Microsoft.Extensions.Configuration.AzureAppConfiguration.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Threading.Tasks;
+
+namespace Microsoft.Extensions.Configuration.AzureAppConfiguration
+{
+ ///
+ /// Options used to configure the behavior of an Azure App Configuration provider.
+ /// If neither nor is ever called, all key-values with no label are included in the configuration provider.
+ ///
+ public class AzureAppConfigurationOptions
+ {
+ private const int MaxRetries = 2;
+ private static readonly TimeSpan MaxRetryDelay = TimeSpan.FromMinutes(1);
+ private static readonly TimeSpan NetworkTimeout = TimeSpan.FromSeconds(10);
private static readonly KeyValueSelector DefaultQuery = new KeyValueSelector { KeyFilter = KeyFilter.Any, LabelFilter = LabelFilter.Null };
-
- private List _individualKvWatchers = new List();
- private List _ffWatchers = new List();
- private List _adapters;
- private List>> _mappers = new List>>();
- private List _selectors;
- 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.
- private SortedSet _keyPrefixes = new SortedSet(Comparer.Create((k1, k2) => -string.Compare(k1, k2, StringComparison.OrdinalIgnoreCase)));
-
- ///
- /// Flag to indicate whether replica discovery is enabled.
- ///
- public bool ReplicaDiscoveryEnabled { get; set; } = true;
-
- ///
- /// Flag to indicate whether load balancing is enabled.
- ///
- public bool LoadBalancingEnabled { get; set; }
-
- ///
- /// The list of connection strings used to connect to an Azure App Configuration store and its replicas.
- ///
- internal IEnumerable ConnectionStrings { get; private set; }
-
- ///
- /// The list of endpoints of an Azure App Configuration store.
- /// If this property is set, the property also needs to be set.
- ///
- internal IEnumerable Endpoints { get; private set; }
-
- ///
- /// The credential used to connect to the Azure App Configuration.
- /// If this property is set, the property also needs to be set.
- ///
- internal TokenCredential Credential { get; private set; }
-
- ///
- /// A collection of specified by user.
- ///
- internal IEnumerable Selectors => _selectors;
-
- ///
- /// Indicates if was called.
- ///
- internal bool RegisterAllEnabled { get; private set; }
-
- ///
- /// Refresh interval for selected key-value collections when is called.
- ///
- internal TimeSpan KvCollectionRefreshInterval { get; private set; }
-
- ///
- /// A collection of .
- ///
- internal IEnumerable IndividualKvWatchers => _individualKvWatchers;
-
- ///
- /// A collection of .
- ///
- internal IEnumerable FeatureFlagWatchers => _ffWatchers;
-
- ///
- /// A collection of .
- ///
- internal IEnumerable Adapters
- {
- get => _adapters;
- set => _adapters = value?.ToList();
- }
-
- ///
- /// A collection of user defined functions that transform each .
- ///
- internal IEnumerable>> Mappers => _mappers;
-
- ///
- /// A collection of key prefixes to be trimmed.
- ///
- internal IEnumerable KeyPrefixes => _keyPrefixes;
-
- ///
- /// For use in tests only. An optional configuration client manager that can be used to provide clients to communicate with Azure App Configuration.
- ///
- internal IConfigurationClientManager ClientManager { get; set; }
-
- ///
- /// For use in tests only. An optional class used to process pageable results from Azure App Configuration.
- ///
- internal IConfigurationSettingPageIterator ConfigurationSettingPageIterator { get; set; }
-
- ///
- /// An optional timespan value to set the minimum backoff duration to a value other than the default.
- ///
- internal TimeSpan MinBackoffDuration { get; set; } = FailOverConstants.MinBackoffDuration;
-
- ///
- /// Options used to configure the client used to communicate with Azure App Configuration.
- ///
- internal ConfigurationClientOptions ClientOptions { get; } = GetDefaultClientOptions();
-
- ///
- /// Flag to indicate whether Key Vault options have been configured.
- ///
- internal bool IsKeyVaultConfigured { get; private set; } = false;
-
- ///
- /// Flag to indicate whether Key Vault secret values will be refreshed automatically.
- ///
- internal bool IsKeyVaultRefreshConfigured { get; private set; } = false;
-
- ///
- /// Indicates all types of feature filters used by the application.
- ///
- internal FeatureFlagTracing FeatureFlagTracing { get; set; } = new FeatureFlagTracing();
-
- ///
- /// Options used to configure provider startup.
- ///
- internal StartupOptions Startup { get; set; } = new StartupOptions();
-
- ///
- /// Initializes a new instance of the class.
- ///
- public AzureAppConfigurationOptions()
- {
- _adapters = new List()
+
+ private List _individualKvWatchers = new List();
+ private List _ffWatchers = new List();
+ private List _adapters;
+ private List>> _mappers = new List>>();
+ private List _selectors;
+ 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.
+ private SortedSet _keyPrefixes = new SortedSet(Comparer.Create((k1, k2) => -string.Compare(k1, k2, StringComparison.OrdinalIgnoreCase)));
+
+ ///
+ /// Flag to indicate whether replica discovery is enabled.
+ ///
+ public bool ReplicaDiscoveryEnabled { get; set; } = true;
+
+ ///
+ /// Flag to indicate whether load balancing is enabled.
+ ///
+ public bool LoadBalancingEnabled { get; set; }
+
+ ///
+ /// The list of connection strings used to connect to an Azure App Configuration store and its replicas.
+ ///
+ internal IEnumerable ConnectionStrings { get; private set; }
+
+ ///
+ /// The list of endpoints of an Azure App Configuration store.
+ /// If this property is set, the property also needs to be set.
+ ///
+ internal IEnumerable Endpoints { get; private set; }
+
+ ///
+ /// The credential used to connect to the Azure App Configuration.
+ /// If this property is set, the property also needs to be set.
+ ///
+ internal TokenCredential Credential { get; private set; }
+
+ ///
+ /// A collection of specified by user.
+ ///
+ internal IEnumerable Selectors => _selectors;
+
+ ///
+ /// Indicates if was called.
+ ///
+ internal bool RegisterAllEnabled { get; private set; }
+
+ ///
+ /// Refresh interval for selected key-value collections when is called.
+ ///
+ internal TimeSpan KvCollectionRefreshInterval { get; private set; }
+
+ ///
+ /// A collection of .
+ ///
+ internal IEnumerable IndividualKvWatchers => _individualKvWatchers;
+
+ ///
+ /// A collection of .
+ ///
+ internal IEnumerable FeatureFlagWatchers => _ffWatchers;
+
+ ///
+ /// A collection of .
+ ///
+ internal IEnumerable Adapters
+ {
+ get => _adapters;
+ set => _adapters = value?.ToList();
+ }
+
+ ///
+ /// A collection of user defined functions that transform each .
+ ///
+ internal IEnumerable>> Mappers => _mappers;
+
+ ///
+ /// A collection of key prefixes to be trimmed.
+ ///
+ internal IEnumerable KeyPrefixes => _keyPrefixes;
+
+ ///
+ /// For use in tests only. An optional configuration client manager that can be used to provide clients to communicate with Azure App Configuration.
+ ///
+ internal IConfigurationClientManager ClientManager { get; set; }
+
+ ///
+ /// For use in tests only. An optional class used to process pageable results from Azure App Configuration.
+ ///
+ internal IConfigurationSettingPageIterator ConfigurationSettingPageIterator { get; set; }
+
+ ///
+ /// An optional timespan value to set the minimum backoff duration to a value other than the default.
+ ///
+ internal TimeSpan MinBackoffDuration { get; set; } = FailOverConstants.MinBackoffDuration;
+
+ ///
+ /// Options used to configure the client used to communicate with Azure App Configuration.
+ ///
+ internal ConfigurationClientOptions ClientOptions { get; } = GetDefaultClientOptions();
+
+ ///
+ /// Flag to indicate whether Key Vault options have been configured.
+ ///
+ internal bool IsKeyVaultConfigured { get; private set; } = false;
+
+ ///
+ /// Flag to indicate whether Key Vault secret values will be refreshed automatically.
+ ///
+ internal bool IsKeyVaultRefreshConfigured { get; private set; } = false;
+
+ ///
+ /// Indicates all types of feature filters used by the application.
+ ///
+ internal FeatureFlagTracing FeatureFlagTracing { get; set; } = new FeatureFlagTracing();
+
+ ///
+ /// Options used to configure provider startup.
+ ///
+ internal StartupOptions Startup { get; set; } = new StartupOptions();
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AzureAppConfigurationOptions()
+ {
+ _adapters = new List()
+ {
+ new AzureKeyVaultKeyValueAdapter(new AzureKeyVaultSecretProvider()),
+ new JsonKeyValueAdapter(),
+ new FeatureManagementKeyValueAdapter(FeatureFlagTracing)
+ };
+
+ // Adds the default query to App Configuration if and are never called.
+ _selectors = new List { DefaultQuery };
+ }
+
+ ///
+ /// Specify what key-values to include in the configuration provider.
+ /// can be called multiple times to include multiple sets of key-values.
+ ///
+ ///
+ /// The key filter to apply when querying Azure App Configuration for key-values.
+ /// An asterisk (*) can be added to the end to return all key-values whose key begins with the key filter.
+ /// e.g. key filter `abc*` returns all key-values whose key starts with `abc`.
+ /// A comma (,) can be used to select multiple key-values. Comma separated filters must exactly match a key to select it.
+ /// Using asterisk to select key-values that begin with a key filter while simultaneously using comma separated key filters is not supported.
+ /// E.g. the key filter `abc*,def` is not supported. The key filters `abc*` and `abc,def` are supported.
+ /// For all other cases the characters: asterisk (*), comma (,), and backslash (\) are reserved. Reserved characters must be escaped using a backslash (\).
+ /// e.g. the key filter `a\\b\,\*c*` returns all key-values whose key starts with `a\b,*c`.
+ /// Built-in key filter options: .
+ ///
+ ///
+ /// The label filter to apply when querying Azure App Configuration for key-values. By default the null label will be used. Built-in label filter options:
+ /// The characters asterisk (*) and comma (,) are not supported. Backslash (\) character is reserved and must be escaped using another backslash (\).
+ ///
+ public AzureAppConfigurationOptions Select(string keyFilter, string labelFilter = LabelFilter.Null)
+ {
+ if (string.IsNullOrEmpty(keyFilter))
+ {
+ 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;
+ }
+
+ if (!_selectCalled)
+ {
+ _selectors.Remove(DefaultQuery);
+
+ _selectCalled = true;
+ }
+
+ _selectors.AppendUnique(new KeyValueSelector
+ {
+ KeyFilter = keyFilter,
+ LabelFilter = labelFilter
+ });
+
+ return this;
+ }
+
+ ///
+ /// Specify a snapshot and include its contained key-values in the configuration provider.
+ /// can be called multiple times to include key-values from multiple snapshots.
+ ///
+ /// The name of the snapshot in Azure App Configuration.
+ public AzureAppConfigurationOptions SelectSnapshot(string name)
+ {
+ if (string.IsNullOrEmpty(name))
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ if (!_selectCalled)
+ {
+ _selectors.Remove(DefaultQuery);
+
+ _selectCalled = true;
+ }
+
+ _selectors.AppendUnique(new KeyValueSelector
+ {
+ SnapshotName = name
+ });
+
+ return this;
+ }
+
+ ///
+ /// 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 then all feature flags with no label are loaded.
+ /// All loaded feature flags will be automatically registered for refresh as a collection.
+ ///
+ /// A callback used to configure feature flag options.
+ public AzureAppConfigurationOptions UseFeatureFlags(Action configure = null)
+ {
+ FeatureFlagOptions options = new FeatureFlagOptions();
+ configure?.Invoke(options);
+
+ if (options.RefreshInterval < RefreshConstants.MinimumFeatureFlagRefreshInterval)
+ {
+ throw new ArgumentOutOfRangeException(nameof(options.RefreshInterval), options.RefreshInterval.TotalMilliseconds,
+ string.Format(ErrorMessages.RefreshIntervalTooShort, RefreshConstants.MinimumFeatureFlagRefreshInterval.TotalMilliseconds));
+ }
+
+ if (options.FeatureFlagSelectors.Count() != 0 && options.Label != null)
+ {
+ throw new InvalidOperationException($"Please select feature flags by either the {nameof(options.Select)} method or by setting the {nameof(options.Label)} property, not both.");
+ }
+
+ if (options.FeatureFlagSelectors.Count() == 0)
+ {
+ // Select clause is not present
+ options.FeatureFlagSelectors.Add(new KeyValueSelector
+ {
+ KeyFilter = FeatureManagementConstants.FeatureFlagMarker + "*",
+ LabelFilter = string.IsNullOrWhiteSpace(options.Label) ? LabelFilter.Null : options.Label,
+ IsFeatureFlagSelector = true
+ });
+ }
+
+ foreach (KeyValueSelector featureFlagSelector in options.FeatureFlagSelectors)
+ {
+ _selectors.AppendUnique(featureFlagSelector);
+
+ _ffWatchers.AppendUnique(new KeyValueWatcher
+ {
+ 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;
+ }
+
+ ///
+ /// Connect the provider to the Azure App Configuration service via a connection string.
+ ///
+ ///
+ /// Used to authenticate with Azure App Configuration.
+ ///
+ public AzureAppConfigurationOptions Connect(string connectionString)
+ {
+ if (string.IsNullOrWhiteSpace(connectionString))
+ {
+ throw new ArgumentNullException(nameof(connectionString));
+ }
+
+ return Connect(new List { connectionString });
+ }
+
+ ///
+ /// Connect the provider to an Azure App Configuration store and its replicas via a list of connection strings.
+ ///
+ ///
+ /// Used to authenticate with Azure App Configuration.
+ ///
+ public AzureAppConfigurationOptions Connect(IEnumerable connectionStrings)
+ {
+ if (connectionStrings == null || !connectionStrings.Any())
+ {
+ throw new ArgumentNullException(nameof(connectionStrings));
+ }
+
+ if (connectionStrings.Distinct().Count() != connectionStrings.Count())
+ {
+ throw new ArgumentException($"All values in '{nameof(connectionStrings)}' must be unique.");
+ }
+
+ Endpoints = null;
+ Credential = null;
+ ConnectionStrings = connectionStrings;
+ return this;
+ }
+
+ ///
+ /// Connect the provider to Azure App Configuration using endpoint and token credentials.
+ ///
+ /// The endpoint of the Azure App Configuration to connect to.
+ /// Token credentials to use to connect.
+ public AzureAppConfigurationOptions Connect(Uri endpoint, TokenCredential credential)
+ {
+ if (endpoint == null)
+ {
+ throw new ArgumentNullException(nameof(endpoint));
+ }
+
+ if (credential == null)
+ {
+ throw new ArgumentNullException(nameof(credential));
+ }
+
+ return Connect(new List() { endpoint }, credential);
+ }
+
+ ///
+ /// Connect the provider to an Azure App Configuration store and its replicas using a list of endpoints and a token credential.
+ ///
+ /// The list of endpoints of an Azure App Configuration store and its replicas to connect to.
+ /// Token credential to use to connect.
+ public AzureAppConfigurationOptions Connect(IEnumerable endpoints, TokenCredential credential)
+ {
+ if (endpoints == null || !endpoints.Any())
+ {
+ throw new ArgumentNullException(nameof(endpoints));
+ }
+
+ if (endpoints.Distinct(new EndpointComparer()).Count() != endpoints.Count())
+ {
+ throw new ArgumentException($"All values in '{nameof(endpoints)}' must be unique.");
+ }
+
+ Credential = credential ?? throw new ArgumentNullException(nameof(credential));
+
+ Endpoints = endpoints;
+ ConnectionStrings = null;
+ return this;
+ }
+
+ ///
+ /// Trims the provided prefix from the keys of all key-values retrieved from Azure App Configuration.
+ ///
+ /// The prefix to be trimmed.
+ public AzureAppConfigurationOptions TrimKeyPrefix(string prefix)
+ {
+ if (string.IsNullOrEmpty(prefix))
+ {
+ throw new ArgumentNullException(nameof(prefix));
+ }
+
+ _keyPrefixes.Add(prefix);
+ return this;
+ }
+
+ ///
+ /// Configure the client(s) used to communicate with Azure App Configuration.
+ ///
+ /// A callback used to configure Azure App Configuration client options.
+ public AzureAppConfigurationOptions ConfigureClientOptions(Action configure)
+ {
+ configure?.Invoke(ClientOptions);
+ return this;
+ }
+
+ ///
+ /// Configure refresh for key-values in the configuration provider.
+ ///
+ /// A callback used to configure Azure App Configuration refresh options.
+ public AzureAppConfigurationOptions ConfigureRefresh(Action configure)
+ {
+ if (RegisterAllEnabled)
+ {
+ throw new InvalidOperationException($"{nameof(ConfigureRefresh)}() cannot be invoked multiple times when {nameof(AzureAppConfigurationRefreshOptions.RegisterAll)} has been invoked.");
+ }
+
+ var refreshOptions = new AzureAppConfigurationRefreshOptions();
+ configure?.Invoke(refreshOptions);
+
+ bool isRegisterCalled = refreshOptions.RefreshRegistrations.Any();
+ RegisterAllEnabled = refreshOptions.RegisterAllEnabled;
+
+ if (!isRegisterCalled && !RegisterAllEnabled)
+ {
+ throw new InvalidOperationException($"{nameof(ConfigureRefresh)}() must call either {nameof(AzureAppConfigurationRefreshOptions.Register)}()" +
+ $" or {nameof(AzureAppConfigurationRefreshOptions.RegisterAll)}()");
+ }
+
+ // Check if both register methods are called at any point
+ if (RegisterAllEnabled && (_individualKvWatchers.Any() || isRegisterCalled))
+ {
+ throw new InvalidOperationException($"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;
+ }
+
+ ///
+ /// Get an instance of that can be used to trigger a refresh for the registered key-values.
+ ///
+ /// An instance of .
+ public IConfigurationRefresher GetRefresher()
+ {
+ return _refresher;
+ }
+
+ ///
+ /// Configures the Azure App Configuration provider to use the provided Key Vault configuration to resolve key vault references.
+ ///
+ /// A callback used to configure Azure App Configuration key vault options.
+ public AzureAppConfigurationOptions ConfigureKeyVault(Action configure)
+ {
+ var keyVaultOptions = new AzureAppConfigurationKeyVaultOptions();
+ configure?.Invoke(keyVaultOptions);
+
+ if (keyVaultOptions.Credential != null && keyVaultOptions.SecretResolver != null)
+ {
+ throw new InvalidOperationException($"Cannot configure both default credentials and secret resolver for Key Vault references. Please call either {nameof(keyVaultOptions.SetCredential)} or {nameof(keyVaultOptions.SetSecretResolver)} method, not both.");
+ }
+
+ _adapters.RemoveAll(a => a is AzureKeyVaultKeyValueAdapter);
+ _adapters.Add(new AzureKeyVaultKeyValueAdapter(new AzureKeyVaultSecretProvider(keyVaultOptions)));
+
+ IsKeyVaultRefreshConfigured = keyVaultOptions.IsKeyVaultRefreshConfigured;
+ IsKeyVaultConfigured = true;
+ return this;
+ }
+
+ ///
+ /// Provides a way to transform settings retrieved from App Configuration before they are processed by the configuration provider.
+ ///
+ /// A callback registered by the user to transform each configuration setting.
+ public AzureAppConfigurationOptions Map(Func> mapper)
+ {
+ if (mapper == null)
+ {
+ throw new ArgumentNullException(nameof(mapper));
+ }
+
+ _mappers.Add(mapper);
+ return this;
+ }
+
+ ///
+ /// Configure the provider behavior when loading data from Azure App Configuration on startup.
+ ///
+ /// A callback used to configure Azure App Configuration startup options.
+ public AzureAppConfigurationOptions ConfigureStartupOptions(Action configure)
+ {
+ configure?.Invoke(Startup);
+ return this;
+ }
+
+ private static ConfigurationClientOptions GetDefaultClientOptions()
+ {
+ var clientOptions = new ConfigurationClientOptions(ConfigurationClientOptions.ServiceVersion.V2023_10_01);
+ clientOptions.Retry.MaxRetries = MaxRetries;
+ clientOptions.Retry.MaxDelay = MaxRetryDelay;
+ clientOptions.Retry.Mode = RetryMode.Exponential;
+ clientOptions.AddPolicy(new UserAgentHeaderPolicy(), HttpPipelinePosition.PerCall);
+ clientOptions.Transport = new HttpClientTransport(new HttpClient()
{
- new AzureKeyVaultKeyValueAdapter(new AzureKeyVaultSecretProvider()),
- new JsonKeyValueAdapter(),
- new FeatureManagementKeyValueAdapter(FeatureFlagTracing)
- };
-
- // Adds the default query to App Configuration if and are never called.
- _selectors = new List { DefaultQuery };
- }
-
- ///
- /// Specify what key-values to include in the configuration provider.
- /// can be called multiple times to include multiple sets of key-values.
- ///
- ///
- /// The key filter to apply when querying Azure App Configuration for key-values.
- /// An asterisk (*) can be added to the end to return all key-values whose key begins with the key filter.
- /// e.g. key filter `abc*` returns all key-values whose key starts with `abc`.
- /// A comma (,) can be used to select multiple key-values. Comma separated filters must exactly match a key to select it.
- /// Using asterisk to select key-values that begin with a key filter while simultaneously using comma separated key filters is not supported.
- /// E.g. the key filter `abc*,def` is not supported. The key filters `abc*` and `abc,def` are supported.
- /// For all other cases the characters: asterisk (*), comma (,), and backslash (\) are reserved. Reserved characters must be escaped using a backslash (\).
- /// e.g. the key filter `a\\b\,\*c*` returns all key-values whose key starts with `a\b,*c`.
- /// Built-in key filter options: .
- ///
- ///
- /// The label filter to apply when querying Azure App Configuration for key-values. By default the null label will be used. Built-in label filter options:
- /// The characters asterisk (*) and comma (,) are not supported. Backslash (\) character is reserved and must be escaped using another backslash (\).
- ///
- public AzureAppConfigurationOptions Select(string keyFilter, string labelFilter = LabelFilter.Null)
- {
- if (string.IsNullOrEmpty(keyFilter))
- {
- 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;
- }
-
- if (!_selectCalled)
- {
- _selectors.Remove(DefaultQuery);
-
- _selectCalled = true;
- }
-
- _selectors.AppendUnique(new KeyValueSelector
- {
- KeyFilter = keyFilter,
- LabelFilter = labelFilter
- });
-
- return this;
- }
-
- ///
- /// Specify a snapshot and include its contained key-values in the configuration provider.
- /// can be called multiple times to include key-values from multiple snapshots.
- ///
- /// The name of the snapshot in Azure App Configuration.
- public AzureAppConfigurationOptions SelectSnapshot(string name)
- {
- if (string.IsNullOrEmpty(name))
- {
- throw new ArgumentNullException(nameof(name));
- }
-
- if (!_selectCalled)
- {
- _selectors.Remove(DefaultQuery);
-
- _selectCalled = true;
- }
-
- _selectors.AppendUnique(new KeyValueSelector
- {
- SnapshotName = name
+ Timeout = NetworkTimeout
});
-
- return this;
- }
-
- ///
- /// 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 then all feature flags with no label are loaded.
- /// All loaded feature flags will be automatically registered for refresh as a collection.
- ///
- /// A callback used to configure feature flag options.
- public AzureAppConfigurationOptions UseFeatureFlags(Action configure = null)
- {
- FeatureFlagOptions options = new FeatureFlagOptions();
- configure?.Invoke(options);
-
- if (options.RefreshInterval < RefreshConstants.MinimumFeatureFlagRefreshInterval)
- {
- throw new ArgumentOutOfRangeException(nameof(options.RefreshInterval), options.RefreshInterval.TotalMilliseconds,
- string.Format(ErrorMessages.RefreshIntervalTooShort, RefreshConstants.MinimumFeatureFlagRefreshInterval.TotalMilliseconds));
- }
-
- if (options.FeatureFlagSelectors.Count() != 0 && options.Label != null)
- {
- throw new InvalidOperationException($"Please select feature flags by either the {nameof(options.Select)} method or by setting the {nameof(options.Label)} property, not both.");
- }
-
- if (options.FeatureFlagSelectors.Count() == 0)
- {
- // Select clause is not present
- options.FeatureFlagSelectors.Add(new KeyValueSelector
- {
- KeyFilter = FeatureManagementConstants.FeatureFlagMarker + "*",
- LabelFilter = string.IsNullOrWhiteSpace(options.Label) ? LabelFilter.Null : options.Label,
- IsFeatureFlagSelector = true
- });
- }
-
- foreach (KeyValueSelector featureFlagSelector in options.FeatureFlagSelectors)
- {
- _selectors.AppendUnique(featureFlagSelector);
-
- _ffWatchers.AppendUnique(new KeyValueWatcher
- {
- 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;
- }
-
- ///
- /// Connect the provider to the Azure App Configuration service via a connection string.
- ///
- ///
- /// Used to authenticate with Azure App Configuration.
- ///
- public AzureAppConfigurationOptions Connect(string connectionString)
- {
- if (string.IsNullOrWhiteSpace(connectionString))
- {
- throw new ArgumentNullException(nameof(connectionString));
- }
-
- return Connect(new List { connectionString });
- }
-
- ///
- /// Connect the provider to an Azure App Configuration store and its replicas via a list of connection strings.
- ///
- ///
- /// Used to authenticate with Azure App Configuration.
- ///
- public AzureAppConfigurationOptions Connect(IEnumerable connectionStrings)
- {
- if (connectionStrings == null || !connectionStrings.Any())
- {
- throw new ArgumentNullException(nameof(connectionStrings));
- }
-
- if (connectionStrings.Distinct().Count() != connectionStrings.Count())
- {
- throw new ArgumentException($"All values in '{nameof(connectionStrings)}' must be unique.");
- }
-
- Endpoints = null;
- Credential = null;
- ConnectionStrings = connectionStrings;
- return this;
- }
-
- ///
- /// Connect the provider to Azure App Configuration using endpoint and token credentials.
- ///
- /// The endpoint of the Azure App Configuration to connect to.
- /// Token credentials to use to connect.
- public AzureAppConfigurationOptions Connect(Uri endpoint, TokenCredential credential)
- {
- if (endpoint == null)
- {
- throw new ArgumentNullException(nameof(endpoint));
- }
-
- if (credential == null)
- {
- throw new ArgumentNullException(nameof(credential));
- }
-
- return Connect(new List() { endpoint }, credential);
- }
-
- ///
- /// Connect the provider to an Azure App Configuration store and its replicas using a list of endpoints and a token credential.
- ///
- /// The list of endpoints of an Azure App Configuration store and its replicas to connect to.
- /// Token credential to use to connect.
- public AzureAppConfigurationOptions Connect(IEnumerable endpoints, TokenCredential credential)
- {
- if (endpoints == null || !endpoints.Any())
- {
- throw new ArgumentNullException(nameof(endpoints));
- }
-
- if (endpoints.Distinct(new EndpointComparer()).Count() != endpoints.Count())
- {
- throw new ArgumentException($"All values in '{nameof(endpoints)}' must be unique.");
- }
-
- Credential = credential ?? throw new ArgumentNullException(nameof(credential));
-
- Endpoints = endpoints;
- ConnectionStrings = null;
- return this;
- }
-
- ///
- /// Trims the provided prefix from the keys of all key-values retrieved from Azure App Configuration.
- ///
- /// The prefix to be trimmed.
- public AzureAppConfigurationOptions TrimKeyPrefix(string prefix)
- {
- if (string.IsNullOrEmpty(prefix))
- {
- throw new ArgumentNullException(nameof(prefix));
- }
-
- _keyPrefixes.Add(prefix);
- return this;
- }
-
- ///
- /// Configure the client(s) used to communicate with Azure App Configuration.
- ///
- /// A callback used to configure Azure App Configuration client options.
- public AzureAppConfigurationOptions ConfigureClientOptions(Action configure)
- {
- configure?.Invoke(ClientOptions);
- return this;
- }
-
- ///
- /// Configure refresh for key-values in the configuration provider.
- ///
- /// A callback used to configure Azure App Configuration refresh options.
- public AzureAppConfigurationOptions ConfigureRefresh(Action configure)
- {
- if (RegisterAllEnabled)
- {
- throw new InvalidOperationException($"{nameof(ConfigureRefresh)}() cannot be invoked multiple times when {nameof(AzureAppConfigurationRefreshOptions.RegisterAll)} has been invoked.");
- }
-
- var refreshOptions = new AzureAppConfigurationRefreshOptions();
- configure?.Invoke(refreshOptions);
-
- bool isRegisterCalled = refreshOptions.RefreshRegistrations.Any();
- RegisterAllEnabled = refreshOptions.RegisterAllEnabled;
-
- if (!isRegisterCalled && !RegisterAllEnabled)
- {
- throw new InvalidOperationException($"{nameof(ConfigureRefresh)}() must call either {nameof(AzureAppConfigurationRefreshOptions.Register)}()" +
- $" or {nameof(AzureAppConfigurationRefreshOptions.RegisterAll)}()");
- }
-
- // Check if both register methods are called at any point
- if (RegisterAllEnabled && (_individualKvWatchers.Any() || isRegisterCalled))
- {
- throw new InvalidOperationException($"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;
- }
-
- ///
- /// Get an instance of that can be used to trigger a refresh for the registered key-values.
- ///
- /// An instance of .
- public IConfigurationRefresher GetRefresher()
- {
- return _refresher;
- }
-
- ///
- /// Configures the Azure App Configuration provider to use the provided Key Vault configuration to resolve key vault references.
- ///
- /// A callback used to configure Azure App Configuration key vault options.
- public AzureAppConfigurationOptions ConfigureKeyVault(Action configure)
- {
- var keyVaultOptions = new AzureAppConfigurationKeyVaultOptions();
- configure?.Invoke(keyVaultOptions);
-
- if (keyVaultOptions.Credential != null && keyVaultOptions.SecretResolver != null)
- {
- throw new InvalidOperationException($"Cannot configure both default credentials and secret resolver for Key Vault references. Please call either {nameof(keyVaultOptions.SetCredential)} or {nameof(keyVaultOptions.SetSecretResolver)} method, not both.");
- }
-
- _adapters.RemoveAll(a => a is AzureKeyVaultKeyValueAdapter);
- _adapters.Add(new AzureKeyVaultKeyValueAdapter(new AzureKeyVaultSecretProvider(keyVaultOptions)));
-
- IsKeyVaultRefreshConfigured = keyVaultOptions.IsKeyVaultRefreshConfigured;
- IsKeyVaultConfigured = true;
- return this;
- }
-
- ///
- /// Provides a way to transform settings retrieved from App Configuration before they are processed by the configuration provider.
- ///
- /// A callback registered by the user to transform each configuration setting.
- public AzureAppConfigurationOptions Map(Func> mapper)
- {
- if (mapper == null)
- {
- throw new ArgumentNullException(nameof(mapper));
- }
-
- _mappers.Add(mapper);
- return this;
- }
-
- ///
- /// Configure the provider behavior when loading data from Azure App Configuration on startup.
- ///
- /// A callback used to configure Azure App Configuration startup options.
- public AzureAppConfigurationOptions ConfigureStartupOptions(Action configure)
- {
- configure?.Invoke(Startup);
- return this;
- }
-
- private static ConfigurationClientOptions GetDefaultClientOptions()
- {
- var clientOptions = new ConfigurationClientOptions(ConfigurationClientOptions.ServiceVersion.V2023_10_01);
- clientOptions.Retry.MaxRetries = MaxRetries;
- clientOptions.Retry.MaxDelay = MaxRetryDelay;
- clientOptions.Retry.Mode = RetryMode.Exponential;
- clientOptions.AddPolicy(new UserAgentHeaderPolicy(), HttpPipelinePosition.PerCall);
-
- return clientOptions;
- }
- }
-}
+
+ return clientOptions;
+ }
+ }
+}
diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationProvider.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationProvider.cs
index 9d61faca..795aeb10 100644
--- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationProvider.cs
+++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationProvider.cs
@@ -1209,6 +1209,13 @@ await ExecuteWithFailOverPolicyAsync