diff --git a/Directory.Build.props b/Directory.Build.props index e1220a8d..a8f39b64 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,23 @@ - - True - + + True + - \ No newline at end of file + + enable + nullable + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/examples/ConfigStoreDemo/Pages/About.cshtml.cs b/examples/ConfigStoreDemo/Pages/About.cshtml.cs index ab5a5b85..326e726f 100644 --- a/examples/ConfigStoreDemo/Pages/About.cshtml.cs +++ b/examples/ConfigStoreDemo/Pages/About.cshtml.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.Examples.Conf { public class AboutModel : PageModel { - public string Message { get; set; } + public string? Message { get; set; } public void OnGet() { diff --git a/examples/ConfigStoreDemo/Pages/Contact.cshtml.cs b/examples/ConfigStoreDemo/Pages/Contact.cshtml.cs index 64453907..7685fed4 100644 --- a/examples/ConfigStoreDemo/Pages/Contact.cshtml.cs +++ b/examples/ConfigStoreDemo/Pages/Contact.cshtml.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.Examples.Conf { public class ContactModel : PageModel { - public string Message { get; set; } + public string? Message { get; set; } public void OnGet() { diff --git a/examples/ConfigStoreDemo/Pages/Error.cshtml.cs b/examples/ConfigStoreDemo/Pages/Error.cshtml.cs index 61725493..10179541 100644 --- a/examples/ConfigStoreDemo/Pages/Error.cshtml.cs +++ b/examples/ConfigStoreDemo/Pages/Error.cshtml.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.Examples.Conf { public class ErrorModel : PageModel { - public string RequestId { get; set; } + public string? RequestId { get; set; } public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); diff --git a/examples/ConfigStoreDemo/Program.cs b/examples/ConfigStoreDemo/Program.cs index 90838b59..59800c00 100644 --- a/examples/ConfigStoreDemo/Program.cs +++ b/examples/ConfigStoreDemo/Program.cs @@ -26,7 +26,13 @@ public static IWebHost BuildWebHost(string[] args) var settings = config.AddJsonFile("appsettings.json").Build(); config.AddAzureAppConfiguration(options => { - options.Connect(settings["connection_string"]) + var connectionString = settings["connection_string"]; + if (string.IsNullOrEmpty(connectionString)) + { + throw new InvalidOperationException("Connection string not found"); + } + + options.Connect(connectionString) .ConfigureRefresh(refresh => { refresh.Register("Settings:BackgroundColor") diff --git a/examples/ConfigStoreDemo/Settings.cs b/examples/ConfigStoreDemo/Settings.cs index 8afd1919..ee8203a7 100644 --- a/examples/ConfigStoreDemo/Settings.cs +++ b/examples/ConfigStoreDemo/Settings.cs @@ -5,12 +5,12 @@ namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.Examples.Conf { public class Settings { - public string AppName { get; set; } + public string? AppName { get; set; } public double Version { get; set; } public long RefreshRate { get; set; } public long FontSize { get; set; } - public string Language { get; set; } - public string Messages { get; set; } - public string BackgroundColor { get; set; } + public string? Language { get; set; } + public string? Messages { get; set; } + public string? BackgroundColor { get; set; } } } diff --git a/examples/ConsoleAppWithFailOver/Program.cs b/examples/ConsoleAppWithFailOver/Program.cs index decbba29..2f13689c 100644 --- a/examples/ConsoleAppWithFailOver/Program.cs +++ b/examples/ConsoleAppWithFailOver/Program.cs @@ -11,7 +11,7 @@ namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.Examples.Cons { class Program { - static IConfiguration Configuration { get; set; } + static IConfiguration? Configuration { get; set; } static void Main(string[] args) { @@ -31,7 +31,7 @@ private static void Configure() IConfiguration configuration = builder.Build(); IConfigurationSection endpointsSection = configuration.GetSection("AppConfig:Endpoints"); - IEnumerable endpoints = endpointsSection.GetChildren().Select(endpoint => new Uri(endpoint.Value)); + IEnumerable endpoints = endpointsSection.GetChildren().Select(endpoint => new Uri(endpoint.Value!)); if (endpoints == null || !endpoints.Any()) { diff --git a/examples/ConsoleApplication/Program.cs b/examples/ConsoleApplication/Program.cs index a3e371b0..0dd12f44 100644 --- a/examples/ConsoleApplication/Program.cs +++ b/examples/ConsoleApplication/Program.cs @@ -6,14 +6,15 @@ namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.Examples.Cons using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.AzureAppConfiguration; using System; + using System.Diagnostics.CodeAnalysis; using System.Text; using System.Threading; using System.Threading.Tasks; class Program { - static IConfiguration Configuration { get; set; } - static IConfigurationRefresher _refresher; + static IConfiguration? Configuration { get; set; } + static IConfigurationRefresher? _refresher; static void Main(string[] args) { @@ -28,6 +29,7 @@ static void Main(string[] args) cts.Cancel(); } + [MemberNotNull(nameof(Configuration))] private static void Configure() { var builder = new ConfigurationBuilder(); @@ -38,18 +40,18 @@ private static void Configure() IConfiguration configuration = builder.Build(); - if (string.IsNullOrEmpty(configuration["connection_string"])) + var connectionString = configuration["connection_string"]; + if (string.IsNullOrEmpty(connectionString)) { - Console.WriteLine("Connection string not found."); Console.WriteLine("Please set the 'connection_string' environment variable to a valid Azure App Configuration connection string and re-run this example."); - return; + throw new InvalidOperationException("Connection string not found"); } // Augment the configuration builder with Azure App Configuration // Pull the connection string from an environment variable builder.AddAzureAppConfiguration(options => { - options.Connect(configuration["connection_string"]) + options.Connect(connectionString) .Select("AppName") .Select("Settings:BackgroundColor") .ConfigureClientOptions(clientOptions => clientOptions.Retry.MaxRetries = 5) @@ -75,9 +77,9 @@ private static async Task Run(CancellationToken token) while (!token.IsCancellationRequested) { // Trigger an async refresh for registered configuration settings without wait - _ = _refresher.TryRefreshAsync(); + _ = _refresher!.TryRefreshAsync(); - sb.AppendLine($"{Configuration["AppName"]} has been configured to run in {Configuration["Language"]}"); + sb.AppendLine($"{Configuration!["AppName"]} has been configured to run in {Configuration["Language"]}"); sb.AppendLine(); sb.AppendLine(string.Equals(Configuration["Language"], "spanish", StringComparison.OrdinalIgnoreCase) ? "Buenos Dias." : "Good morning"); diff --git a/src/Microsoft.Azure.AppConfiguration.AspNetCore/AzureAppConfigurationRefreshExtensions.cs b/src/Microsoft.Azure.AppConfiguration.AspNetCore/AzureAppConfigurationRefreshExtensions.cs index 671c9840..523fba9e 100644 --- a/src/Microsoft.Azure.AppConfiguration.AspNetCore/AzureAppConfigurationRefreshExtensions.cs +++ b/src/Microsoft.Azure.AppConfiguration.AspNetCore/AzureAppConfigurationRefreshExtensions.cs @@ -25,7 +25,7 @@ public static IApplicationBuilder UseAzureAppConfiguration(this IApplicationBuil throw new ArgumentNullException(nameof(builder)); } - IConfigurationRefresherProvider refresherProvider = (IConfigurationRefresherProvider)builder.ApplicationServices.GetService(typeof(IConfigurationRefresherProvider)); + IConfigurationRefresherProvider? refresherProvider = (IConfigurationRefresherProvider?)builder.ApplicationServices.GetService(typeof(IConfigurationRefresherProvider)); // Verify if AddAzureAppConfiguration was done before calling UseAzureAppConfiguration. // We use the IConfigurationRefresherProvider to make sure if the required services were added. diff --git a/src/Microsoft.Azure.AppConfiguration.Functions.Worker/AzureAppConfigurationRefreshExtensions.cs b/src/Microsoft.Azure.AppConfiguration.Functions.Worker/AzureAppConfigurationRefreshExtensions.cs index 2623d59c..a167a74a 100644 --- a/src/Microsoft.Azure.AppConfiguration.Functions.Worker/AzureAppConfigurationRefreshExtensions.cs +++ b/src/Microsoft.Azure.AppConfiguration.Functions.Worker/AzureAppConfigurationRefreshExtensions.cs @@ -24,7 +24,7 @@ public static class AzureAppConfigurationRefreshExtensions public static IFunctionsWorkerApplicationBuilder UseAzureAppConfiguration(this IFunctionsWorkerApplicationBuilder builder) { IServiceProvider serviceProvider = builder.Services.BuildServiceProvider(); - IConfigurationRefresherProvider refresherProvider = serviceProvider.GetService(); + IConfigurationRefresherProvider? refresherProvider = serviceProvider.GetService(); // Verify if AddAzureAppConfiguration was done before calling UseAzureAppConfiguration. // We use the IConfigurationRefresherProvider to make sure if the required services were added. diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationKeyVaultOptions.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationKeyVaultOptions.cs index 7d32f3a7..8b1efc0b 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationKeyVaultOptions.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationKeyVaultOptions.cs @@ -5,6 +5,7 @@ using Azure.Security.KeyVault.Secrets; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; namespace Microsoft.Extensions.Configuration.AzureAppConfiguration @@ -14,10 +15,10 @@ namespace Microsoft.Extensions.Configuration.AzureAppConfiguration /// public class AzureAppConfigurationKeyVaultOptions { - internal TokenCredential Credential; + internal TokenCredential? Credential; internal SecretClientOptions ClientOptions = new SecretClientOptions(); internal List SecretClients = new List(); - internal Func> SecretResolver; + internal Func>? SecretResolver; internal Dictionary SecretRefreshIntervals = new Dictionary(); internal TimeSpan? DefaultSecretRefreshInterval = null; internal bool IsKeyVaultRefreshConfigured = false; @@ -26,6 +27,7 @@ public class AzureAppConfigurationKeyVaultOptions /// Sets the credentials used to authenticate to key vaults that have no registered . /// /// Default token credentials. + [MemberNotNull(nameof(Credential))] public AzureAppConfigurationKeyVaultOptions SetCredential(TokenCredential credential) { Credential = credential; @@ -57,6 +59,7 @@ public AzureAppConfigurationKeyVaultOptions Register(SecretClient secretClient) /// Sets the callback used to resolve key vault references that have no registered . /// /// A callback that maps the of the key vault secret to its value. + [MemberNotNull(nameof(SecretResolver))] public AzureAppConfigurationKeyVaultOptions SetSecretResolver(Func> secretResolver) { if (secretResolver == null) diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationOptions.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationOptions.cs index d79838da..9f367239 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationOptions.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationOptions.cs @@ -42,19 +42,19 @@ public class AzureAppConfigurationOptions /// /// The list of connection strings used to connect to an Azure App Configuration store and its replicas. /// - internal IEnumerable ConnectionStrings { get; private set; } + 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; } + 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; } + internal TokenCredential? Credential { get; private set; } /// /// A collection of . @@ -77,7 +77,7 @@ public class AzureAppConfigurationOptions internal IEnumerable Adapters { get => _adapters; - set => _adapters = value?.ToList(); + set => _adapters = value.ToList(); } /// @@ -94,7 +94,7 @@ internal IEnumerable Adapters /// An optional configuration client manager that can be used to provide clients to communicate with Azure App Configuration. /// /// This property is used only for unit testing. - internal IConfigurationClientManager ClientManager { get; set; } + internal IConfigurationClientManager? ClientManager { get; set; } /// /// An optional timespan value to set the minimum backoff duration to a value other than the default. @@ -158,7 +158,7 @@ public AzureAppConfigurationOptions() /// 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) + public AzureAppConfigurationOptions Select(string keyFilter, string? labelFilter = LabelFilter.Null) { if (string.IsNullOrEmpty(keyFilter)) { @@ -177,10 +177,10 @@ public AzureAppConfigurationOptions Select(string keyFilter, string labelFilter } _kvSelectors.AppendUnique(new KeyValueSelector - { - KeyFilter = keyFilter, - LabelFilter = labelFilter - }); + ( + keyFilter: keyFilter, + labelFilter: labelFilter! + )); return this; } @@ -196,10 +196,7 @@ public AzureAppConfigurationOptions SelectSnapshot(string name) throw new ArgumentNullException(nameof(name)); } - _kvSelectors.AppendUnique(new KeyValueSelector - { - SnapshotName = name - }); + _kvSelectors.AppendUnique(new KeyValueSelector(snapshotName: name)); return this; } @@ -210,7 +207,7 @@ public AzureAppConfigurationOptions SelectSnapshot(string name) /// All loaded feature flags will be automatically registered for refresh on an individual flag level. /// /// A callback used to configure feature flag options. - public AzureAppConfigurationOptions UseFeatureFlags(Action configure = null) + public AzureAppConfigurationOptions UseFeatureFlags(Action? configure = null) { FeatureFlagOptions options = new FeatureFlagOptions(); configure?.Invoke(options); @@ -230,10 +227,10 @@ public AzureAppConfigurationOptions UseFeatureFlags(Action c { // Select clause is not present options.FeatureFlagSelectors.Add(new KeyValueSelector - { - KeyFilter = FeatureManagementConstants.FeatureFlagMarker + "*", - LabelFilter = options.Label == null ? LabelFilter.Null : options.Label - }); + ( + keyFilter: FeatureManagementConstants.FeatureFlagMarker + "*", + labelFilter: options.Label == null ? LabelFilter.Null : options.Label + )); } foreach (var featureFlagSelector in options.FeatureFlagSelectors) @@ -241,12 +238,12 @@ public AzureAppConfigurationOptions UseFeatureFlags(Action c var featureFlagFilter = featureFlagSelector.KeyFilter; var labelFilter = featureFlagSelector.LabelFilter; - Select(featureFlagFilter, labelFilter); + Select(featureFlagFilter!, labelFilter!); _multiKeyWatchers.AppendUnique(new KeyValueWatcher { - Key = featureFlagFilter, - Label = labelFilter, + Key = featureFlagFilter!, + Label = labelFilter!, // If UseFeatureFlags is called multiple times for the same key and label filters, last cache expiration time wins CacheExpirationInterval = options.CacheExpirationInterval }); diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationProvider.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationProvider.cs index d6613246..3e8f5220 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationProvider.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationProvider.cs @@ -30,9 +30,9 @@ internal class AzureAppConfigurationProvider : ConfigurationProvider, IConfigura private readonly bool _requestTracingEnabled; private readonly IConfigurationClientManager _configClientManager; private AzureAppConfigurationOptions _options; - private Dictionary _mappedData; + private Dictionary? _mappedData; private Dictionary _watchedSettings = new Dictionary(); - private RequestTracingOptions _requestTracingOptions; + private RequestTracingOptions? _requestTracingOptions; private Dictionary _configClientBackoffs = new Dictionary(); private readonly TimeSpan MinCacheExpirationInterval; @@ -46,7 +46,7 @@ internal class AzureAppConfigurationProvider : ConfigurationProvider, IConfigura // To avoid concurrent network operations, this flag is used to achieve synchronization between multiple threads. private int _networkOperationsInProgress = 0; private Logger _logger = new Logger(); - private ILoggerFactory _loggerFactory; + private ILoggerFactory? _loggerFactory; private class ConfigurationClientBackoffStatus { @@ -54,7 +54,7 @@ private class ConfigurationClientBackoffStatus public DateTimeOffset BackoffEndTime { get; set; } } - public Uri AppConfigurationEndpoint + public Uri? AppConfigurationEndpoint { get { @@ -79,7 +79,7 @@ public Uri AppConfigurationEndpoint } } - public ILoggerFactory LoggerFactory + public ILoggerFactory? LoggerFactory { get { @@ -119,7 +119,7 @@ public AzureAppConfigurationProvider(IConfigurationClientManager configClientMan } // Enable request tracing if not opt-out - string requestTracingDisabled = null; + string? requestTracingDisabled = null; try { requestTracingDisabled = Environment.GetEnvironmentVariable(RequestTracingConstants.RequestTracingDisabledEnvironmentVariable); @@ -211,9 +211,9 @@ public async Task RefreshAsync(CancellationToken cancellationToken) // Filter clients based on their backoff status clients = clients.Where(client => { - Uri endpoint = _configClientManager.GetEndpointForClient(client); + Uri endpoint = _configClientManager.GetEndpointForClient(client)!; - if (!_configClientBackoffs.TryGetValue(endpoint, out ConfigurationClientBackoffStatus clientBackoffStatus)) + if (!_configClientBackoffs.TryGetValue(endpoint, out ConfigurationClientBackoffStatus? clientBackoffStatus)) { clientBackoffStatus = new ConfigurationClientBackoffStatus(); @@ -248,10 +248,10 @@ public async Task RefreshAsync(CancellationToken cancellationToken) // // Avoid instance state modification - Dictionary watchedSettings = null; - List keyValueChanges = null; - List changedKeyValuesCollection = null; - Dictionary data = null; + Dictionary? watchedSettings = null; + List? keyValueChanges = null; + List? changedKeyValuesCollection = null; + Dictionary? data = null; bool refreshAll = false; StringBuilder logInfoBuilder = new StringBuilder(); StringBuilder logDebugBuilder = new StringBuilder(); @@ -263,7 +263,7 @@ await ExecuteWithFailOverPolicyAsync(clients, async (client) => keyValueChanges = new List(); changedKeyValuesCollection = null; refreshAll = false; - Uri endpoint = _configClientManager.GetEndpointForClient(client); + Uri endpoint = _configClientManager.GetEndpointForClient(client)!; logDebugBuilder.Clear(); logInfoBuilder.Clear(); @@ -278,7 +278,7 @@ await ExecuteWithFailOverPolicyAsync(clients, async (client) => // // Find if there is a change associated with watcher - if (_watchedSettings.TryGetValue(watchedKeyLabel, out ConfigurationSetting watchedKv)) + if (_watchedSettings.TryGetValue(watchedKeyLabel, out ConfigurationSetting? watchedKv)) { await TracingUtils.CallWithRequestTracing(_requestTracingEnabled, RequestType.Watch, _requestTracingOptions, async () => change = await client.GetKeyValueChange(watchedKv, cancellationToken).ConfigureAwait(false)).ConfigureAwait(false); @@ -348,8 +348,12 @@ await CallWithRequestTracing( cancellationToken) .ConfigureAwait(false); + Debug.Assert(keyValueChanges != null); + if (!refreshAll) { + Debug.Assert(changedKeyValuesCollection != null); + watchedSettings = new Dictionary(_watchedSettings); foreach (KeyValueWatcher changeWatcher in cacheExpiredWatchers.Concat(cacheExpiredMultiKeyWatchers)) @@ -362,7 +366,7 @@ await CallWithRequestTracing( KeyValueIdentifier changeIdentifier = new KeyValueIdentifier(change.Key, change.Label); if (change.ChangeType == KeyValueChangeType.Modified) { - ConfigurationSetting setting = change.Current; + ConfigurationSetting setting = change.Current!; ConfigurationSetting settingCopy = new ConfigurationSetting(setting.Key, setting.Value, setting.Label, setting.ETag); watchedSettings[changeIdentifier] = settingCopy; @@ -395,7 +399,9 @@ await CallWithRequestTracing( } else { - _mappedData = await MapConfigurationSettings(data).ConfigureAwait(false); + Debug.Assert(data != null); + + _mappedData = await MapConfigurationSettings(data!).ConfigureAwait(false); // Invalidate all the cached KeyVault secrets foreach (IKeyValueAdapter adapter in _options.Adapters) @@ -412,7 +418,7 @@ await CallWithRequestTracing( if (_options.Adapters.Any(adapter => adapter.NeedsRefresh()) || changedKeyValuesCollection?.Any() == true || keyValueChanges.Any()) { - _watchedSettings = watchedSettings; + _watchedSettings = watchedSettings!; if (logDebugBuilder.Length > 0) { @@ -557,19 +563,19 @@ private void SetDirty(TimeSpan? maxDelay) } } - private async Task> PrepareData(Dictionary data, CancellationToken cancellationToken = default) + private async Task> PrepareData(Dictionary data, CancellationToken cancellationToken = default) { - var applicationData = new Dictionary(StringComparer.OrdinalIgnoreCase); + var applicationData = new Dictionary(StringComparer.OrdinalIgnoreCase); // Reset old filter tracing in order to track the filter types present in the current response from server. _options.FeatureFilterTracing.ResetFeatureFilterTracing(); foreach (KeyValuePair kvp in data) { - IEnumerable> keyValuePairs = null; + IEnumerable>? keyValuePairs = null; keyValuePairs = await ProcessAdapters(kvp.Value, cancellationToken).ConfigureAwait(false); - foreach (KeyValuePair kv in keyValuePairs) + foreach (KeyValuePair kv in keyValuePairs) { string key = kv.Key; @@ -708,8 +714,8 @@ private async Task TryInitializeAsync(IEnumerable cli private async Task InitializeAsync(IEnumerable clients, CancellationToken cancellationToken = default) { - Dictionary data = null; - Dictionary watchedSettings = null; + Dictionary? data = null; + Dictionary? watchedSettings = null; await ExecuteWithFailOverPolicyAsync( clients, @@ -731,6 +737,8 @@ await ExecuteWithFailOverPolicyAsync( cancellationToken) .ConfigureAwait(false); + Debug.Assert(watchedSettings != null); + // Update the cache expiration time for all refresh registered settings and feature flags foreach (KeyValueWatcher changeWatcher in _options.ChangeWatchers.Concat(_options.MultiKeyWatchers)) { @@ -747,7 +755,7 @@ await ExecuteWithFailOverPolicyAsync( Dictionary mappedData = await MapConfigurationSettings(data).ConfigureAwait(false); SetData(await PrepareData(mappedData, cancellationToken).ConfigureAwait(false)); - _watchedSettings = watchedSettings; + _watchedSettings = watchedSettings!; _mappedData = mappedData; } } @@ -838,7 +846,7 @@ private async Task> LoadKey KeyValueIdentifier watchedKeyLabel = new KeyValueIdentifier(watchedKey, watchedLabel); // Skip the loading for the key-value in case it has already been loaded - if (existingSettings.TryGetValue(watchedKey, out ConfigurationSetting loadedKv) + if (existingSettings.TryGetValue(watchedKey, out ConfigurationSetting? loadedKv) && watchedKeyLabel.Equals(new KeyValueIdentifier(loadedKv.Key, loadedKv.Label))) { watchedSettings[watchedKeyLabel] = new ConfigurationSetting(loadedKv.Key, loadedKv.Value, loadedKv.Label, loadedKv.ETag); @@ -846,7 +854,7 @@ private async Task> LoadKey } // Send a request to retrieve key-value since it may be either not loaded or loaded with a different label or different casing - ConfigurationSetting watchedKv = null; + ConfigurationSetting? watchedKv = null; try { await CallWithRequestTracing(async () => watchedKv = await client.GetConfigurationSettingAsync(watchedKey, watchedLabel, cancellationToken).ConfigureAwait(false)).ConfigureAwait(false); @@ -916,7 +924,7 @@ await client.GetKeyValueChangeCollection( return keyValueChanges; } - private void SetData(IDictionary data) + private void SetData(IDictionary data) { // Set the application data for the configuration provider Data = data; @@ -925,9 +933,9 @@ private void SetData(IDictionary data) OnReload(); } - private async Task>> ProcessAdapters(ConfigurationSetting setting, CancellationToken cancellationToken) + private async Task>> ProcessAdapters(ConfigurationSetting setting, CancellationToken cancellationToken) { - List> keyValues = null; + List>? keyValues = null; foreach (IKeyValueAdapter adapter in _options.Adapters) { @@ -936,17 +944,17 @@ private async Task>> ProcessAdapters(Co continue; } - IEnumerable> kvs = await adapter.ProcessKeyValue(setting, _logger, cancellationToken).ConfigureAwait(false); + IEnumerable> kvs = await adapter.ProcessKeyValue(setting, _logger, cancellationToken).ConfigureAwait(false); if (kvs != null) { - keyValues = keyValues ?? new List>(); + keyValues = keyValues ?? new List>(); keyValues.AddRange(kvs); } } - return keyValues ?? Enumerable.Repeat(new KeyValuePair(setting.Key, setting.Value), 1); + return keyValues ?? Enumerable.Repeat(new KeyValuePair(setting.Key, setting.Value), 1); } private Task CallWithRequestTracing(Func clientCall) @@ -1004,7 +1012,7 @@ private async Task ExecuteWithFailOverPolicyAsync( clientEnumerator.MoveNext(); - Uri previousEndpoint = _configClientManager.GetEndpointForClient(clientEnumerator.Current); + Uri previousEndpoint = _configClientManager.GetEndpointForClient(clientEnumerator.Current)!; ConfigurationClient currentClient; while (true) @@ -1053,7 +1061,7 @@ private async Task ExecuteWithFailOverPolicyAsync( { if (!success && backoffAllClients) { - _logger.LogWarning(LogHelper.BuildLastEndpointFailedMessage(previousEndpoint?.ToString())); + _logger.LogWarning(LogHelper.BuildLastEndpointFailedMessage(previousEndpoint.ToString())); do { @@ -1071,11 +1079,11 @@ private async Task ExecuteWithFailOverPolicyAsync( } } - Uri currentEndpoint = _configClientManager.GetEndpointForClient(clientEnumerator.Current); + Uri currentEndpoint = _configClientManager.GetEndpointForClient(clientEnumerator.Current)!; if (previousEndpoint != currentEndpoint) { - _logger.LogWarning(LogHelper.BuildFailoverMessage(previousEndpoint?.ToString(), currentEndpoint?.ToString())); + _logger.LogWarning(LogHelper.BuildFailoverMessage(previousEndpoint.ToString(), currentEndpoint.ToString())); } previousEndpoint = currentEndpoint; @@ -1087,7 +1095,7 @@ private async Task ExecuteWithFailOverPolicyAsync( Func funcToExecute, CancellationToken cancellationToken = default) { - await ExecuteWithFailOverPolicyAsync(clients, async (client) => + await ExecuteWithFailOverPolicyAsync(clients, async (client) => { await funcToExecute(client).ConfigureAwait(false); return null; @@ -1097,7 +1105,7 @@ await ExecuteWithFailOverPolicyAsync(clients, async (client) => private bool IsFailOverable(AggregateException ex) { - RequestFailedException rfe = ex.InnerExceptions?.LastOrDefault(e => e is RequestFailedException) as RequestFailedException; + RequestFailedException? rfe = ex.InnerExceptions?.LastOrDefault(e => e is RequestFailedException) as RequestFailedException; return rfe != null ? IsFailOverable(rfe) : false; } @@ -1111,7 +1119,7 @@ private bool IsFailOverable(RequestFailedException rfe) return true; } - Exception innerException; + Exception? innerException; if (rfe.InnerException is HttpRequestException hre) { @@ -1210,7 +1218,7 @@ private void EnsureAssemblyInspected() private void UpdateClientBackoffStatus(Uri endpoint, bool successful) { - if (!_configClientBackoffs.TryGetValue(endpoint, out ConfigurationClientBackoffStatus clientBackoffStatus)) + if (!_configClientBackoffs.TryGetValue(endpoint, out ConfigurationClientBackoffStatus? clientBackoffStatus)) { clientBackoffStatus = new ConfigurationClientBackoffStatus(); } diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationRefresher.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationRefresher.cs index 0cee38ca..1072e7d9 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationRefresher.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationRefresher.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. // using System; +using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; @@ -9,9 +10,9 @@ namespace Microsoft.Extensions.Configuration.AzureAppConfiguration { internal class AzureAppConfigurationRefresher : IConfigurationRefresher { - private AzureAppConfigurationProvider _provider = null; + private AzureAppConfigurationProvider? _provider = null; - public Uri AppConfigurationEndpoint { get; private set; } = null; + public Uri? AppConfigurationEndpoint { get; private set; } = null; public void SetProvider(AzureAppConfigurationProvider provider) { @@ -42,6 +43,7 @@ public void ProcessPushNotification(PushNotification pushNotification, TimeSpan? _provider.ProcessPushNotification(pushNotification, maxDelay); } + [MemberNotNull(nameof(_provider))] private void ThrowIfNullProvider(string operation) { if (_provider == null) diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationRefresherProvider.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationRefresherProvider.cs index e86f6cc1..2ba8d7c4 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationRefresherProvider.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationRefresherProvider.cs @@ -11,7 +11,7 @@ namespace Microsoft.Extensions.Configuration.AzureAppConfiguration { internal class AzureAppConfigurationRefresherProvider : IConfigurationRefresherProvider { - private static readonly PropertyInfo _propertyInfo = typeof(ChainedConfigurationProvider).GetProperty("Configuration", BindingFlags.Public | BindingFlags.Instance); + private static readonly PropertyInfo? _propertyInfo = typeof(ChainedConfigurationProvider).GetProperty("Configuration", BindingFlags.Public | BindingFlags.Instance); public IEnumerable Refreshers { get; } @@ -30,7 +30,7 @@ public AzureAppConfigurationRefresherProvider(IConfiguration configuration, ILog Refreshers = refreshers; } - private void FindRefreshers(IConfigurationRoot configurationRoot, ILoggerFactory loggerFactory, List refreshers) + private void FindRefreshers(IConfigurationRoot? configurationRoot, ILoggerFactory loggerFactory, List refreshers) { if (configurationRoot != null) { diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationSource.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationSource.cs index 09544029..62504f1d 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationSource.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationSource.cs @@ -24,7 +24,7 @@ public AzureAppConfigurationSource(Action optionsI public IConfigurationProvider Build(IConfigurationBuilder builder) { - IConfigurationProvider provider = null; + IConfigurationProvider? provider = null; try { diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureKeyVaultReference/AzureKeyVaultKeyValueAdapter.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureKeyVaultReference/AzureKeyVaultKeyValueAdapter.cs index 9580a1cc..dc6ef17e 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureKeyVaultReference/AzureKeyVaultKeyValueAdapter.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureKeyVaultReference/AzureKeyVaultKeyValueAdapter.cs @@ -27,17 +27,17 @@ public AzureKeyVaultKeyValueAdapter(AzureKeyVaultSecretProvider secretProvider) /// Uses the Azure Key Vault secret provider to resolve Key Vault references retrieved from Azure App Configuration. /// inputs the IKeyValue /// returns the keyname and actual value - public async Task>> ProcessKeyValue(ConfigurationSetting setting, Logger logger, CancellationToken cancellationToken) + public async Task>> ProcessKeyValue(ConfigurationSetting setting, Logger logger, CancellationToken cancellationToken) { - string secretRefUri = ParseSecretReferenceUri(setting); + string? secretRefUri = ParseSecretReferenceUri(setting); // Uri validation - if (string.IsNullOrEmpty(secretRefUri) || !Uri.TryCreate(secretRefUri, UriKind.Absolute, out Uri secretUri) || !KeyVaultSecretIdentifier.TryCreate(secretUri, out KeyVaultSecretIdentifier secretIdentifier)) + if (string.IsNullOrEmpty(secretRefUri) || !Uri.TryCreate(secretRefUri, UriKind.Absolute, out Uri? secretUri) || !KeyVaultSecretIdentifier.TryCreate(secretUri, out KeyVaultSecretIdentifier secretIdentifier)) { throw CreateKeyVaultReferenceException("Invalid Key vault secret identifier.", setting, null, secretRefUri); } - string secret; + string? secret; try { @@ -52,13 +52,13 @@ public async Task>> ProcessKeyValue(Con throw CreateKeyVaultReferenceException("Key vault error.", setting, e, secretRefUri); } - return new KeyValuePair[] + return new KeyValuePair[] { - new KeyValuePair(setting.Key, secret) + new KeyValuePair(setting.Key, secret) }; } - KeyVaultReferenceException CreateKeyVaultReferenceException(string message, ConfigurationSetting setting, Exception inner, string secretRefUri = null) + KeyVaultReferenceException CreateKeyVaultReferenceException(string message, ConfigurationSetting setting, Exception? inner, string? secretRefUri = null) { return new KeyVaultReferenceException(message, inner) { @@ -70,13 +70,13 @@ KeyVaultReferenceException CreateKeyVaultReferenceException(string message, Conf }; } - public bool CanProcess(ConfigurationSetting setting) + public bool CanProcess(ConfigurationSetting? setting) { - string contentType = setting?.ContentType?.Split(';')[0].Trim(); + string? contentType = setting?.ContentType?.Split(';')[0].Trim(); return string.Equals(contentType, KeyVaultConstants.ContentType); } - public void InvalidateCache(ConfigurationSetting setting = null) + public void InvalidateCache(ConfigurationSetting? setting = null) { if (setting == null) { @@ -93,9 +93,9 @@ public bool NeedsRefresh() return _secretProvider.ShouldRefreshKeyVaultSecrets(); } - private string ParseSecretReferenceUri(ConfigurationSetting setting) + private string? ParseSecretReferenceUri(ConfigurationSetting setting) { - string secretRefUri = null; + string? secretRefUri = null; try { diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureKeyVaultReference/AzureKeyVaultSecretProvider.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureKeyVaultReference/AzureKeyVaultSecretProvider.cs index 86e61429..d8f415f7 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureKeyVaultReference/AzureKeyVaultSecretProvider.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureKeyVaultReference/AzureKeyVaultSecretProvider.cs @@ -16,10 +16,10 @@ internal class AzureKeyVaultSecretProvider private readonly AzureAppConfigurationKeyVaultOptions _keyVaultOptions; private readonly IDictionary _secretClients; private readonly Dictionary _cachedKeyVaultSecrets; - private string _nextRefreshKey; + private string? _nextRefreshKey; private DateTimeOffset? _nextRefreshTime; - public AzureKeyVaultSecretProvider(AzureAppConfigurationKeyVaultOptions keyVaultOptions = null) + public AzureKeyVaultSecretProvider(AzureAppConfigurationKeyVaultOptions? keyVaultOptions = null) { _keyVaultOptions = keyVaultOptions ?? new AzureAppConfigurationKeyVaultOptions(); _cachedKeyVaultSecrets = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -35,17 +35,17 @@ public AzureKeyVaultSecretProvider(AzureAppConfigurationKeyVaultOptions keyVault } } - public async Task GetSecretValue(KeyVaultSecretIdentifier secretIdentifier, string key, string label, Logger logger, CancellationToken cancellationToken) + public async Task GetSecretValue(KeyVaultSecretIdentifier secretIdentifier, string key, string? label, Logger logger, CancellationToken cancellationToken) { - string secretValue = null; + string? secretValue = null; - if (_cachedKeyVaultSecrets.TryGetValue(key, out CachedKeyVaultSecret cachedSecret) && + if (_cachedKeyVaultSecrets.TryGetValue(key, out CachedKeyVaultSecret? cachedSecret) && (!cachedSecret.RefreshAt.HasValue || DateTimeOffset.UtcNow < cachedSecret.RefreshAt.Value)) { return cachedSecret.SecretValue; } - SecretClient client = GetSecretClient(secretIdentifier.SourceId); + SecretClient? client = GetSecretClient(secretIdentifier.SourceId); if (client == null && _keyVaultOptions.SecretResolver == null) { @@ -101,11 +101,11 @@ public void RemoveSecretFromCache(string key) } } - private SecretClient GetSecretClient(Uri secretUri) + private SecretClient? GetSecretClient(Uri secretUri) { string keyVaultId = secretUri.Host; - if (_secretClients.TryGetValue(keyVaultId, out SecretClient client)) + if (_secretClients.TryGetValue(keyVaultId, out SecretClient? client)) { return client; } @@ -125,7 +125,7 @@ private SecretClient GetSecretClient(Uri secretUri) return client; } - private void SetSecretInCache(string key, CachedKeyVaultSecret cachedSecret, bool success = true) + private void SetSecretInCache(string key, CachedKeyVaultSecret? cachedSecret, bool success = true) { if (cachedSecret == null) { diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureKeyVaultReference/CachedKeyVaultSecret.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureKeyVaultReference/CachedKeyVaultSecret.cs index 1b813d36..08414845 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureKeyVaultReference/CachedKeyVaultSecret.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureKeyVaultReference/CachedKeyVaultSecret.cs @@ -10,7 +10,7 @@ internal class CachedKeyVaultSecret ///// ///// The value of the Key Vault secret. ///// - public string SecretValue { get; set; } + public string? SecretValue { get; set; } /// /// The time when this secret should be reloaded from Key Vault. @@ -22,7 +22,7 @@ internal class CachedKeyVaultSecret /// public int RefreshAttempts { get; set; } - public CachedKeyVaultSecret(string secretValue = null, DateTimeOffset? refreshAt = null, int refreshAttempts = 0) + public CachedKeyVaultSecret(string? secretValue = null, DateTimeOffset? refreshAt = null, int refreshAttempts = 0) { SecretValue = secretValue; RefreshAt = refreshAt; diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/ConfigurationClientManager.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/ConfigurationClientManager.cs index 06f916b0..d1238907 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/ConfigurationClientManager.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/ConfigurationClientManager.cs @@ -28,15 +28,15 @@ internal class ConfigurationClientManager : IConfigurationClientManager, IDispos { private readonly IList _clients; private readonly Uri _endpoint; - private readonly string _secret; - private readonly string _id; - private readonly TokenCredential _credential; + private readonly string? _secret; + private readonly string? _id; + private readonly TokenCredential? _credential; private readonly ConfigurationClientOptions _clientOptions; private readonly bool _replicaDiscoveryEnabled; private readonly SrvLookupClient _srvLookupClient; private readonly string _validDomain; - private IList _dynamicClients; + private IList? _dynamicClients; private DateTimeOffset _lastFallbackClientRefresh = default; private DateTimeOffset _lastFallbackClientRefreshAttempt = default; private Logger _logger = new Logger(); @@ -112,8 +112,9 @@ public ConfigurationClientManager( /// /// Internal constructor; Only used for unit testing. /// - /// +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. internal ConfigurationClientManager(IList clients) +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. { _clients = clients; } @@ -169,7 +170,7 @@ public bool UpdateSyncToken(Uri endpoint, string syncToken) throw new ArgumentNullException(nameof(syncToken)); } - ConfigurationClientWrapper clientWrapper = _clients.SingleOrDefault(c => new EndpointComparer().Equals(c.Endpoint, endpoint)); + ConfigurationClientWrapper? clientWrapper = _clients.SingleOrDefault(c => new EndpointComparer().Equals(c.Endpoint, endpoint)); if (_dynamicClients != null && clientWrapper == null) { @@ -185,14 +186,14 @@ public bool UpdateSyncToken(Uri endpoint, string syncToken) return false; } - public Uri GetEndpointForClient(ConfigurationClient client) + public Uri? GetEndpointForClient(ConfigurationClient client) { if (client == null) { throw new ArgumentNullException(nameof(client)); } - ConfigurationClientWrapper currentClient = _clients.FirstOrDefault(c => c.Client == client); + ConfigurationClientWrapper? currentClient = _clients.FirstOrDefault(c => c.Client == client); if (_dynamicClients != null && currentClient == null) { @@ -275,7 +276,7 @@ private async Task RefreshFallbackClients(CancellationToken cancellationToken) var targetEndpoint = new Uri($"https://{host}"); var configClient = _credential == null - ? new ConfigurationClient(ConnectionStringUtils.Build(targetEndpoint, _id, _secret), _clientOptions) + ? new ConfigurationClient(ConnectionStringUtils.Build(targetEndpoint, _id!, _secret!), _clientOptions) : new ConfigurationClient(targetEndpoint, _credential, _clientOptions); newDynamicClients.Add(new ConfigurationClientWrapper(targetEndpoint, configClient)); @@ -291,7 +292,7 @@ private string GetValidDomain(Uri endpoint) { Debug.Assert(endpoint != null); - string hostName = endpoint.Host; + string hostName = endpoint!.Host; foreach (string label in TrustedDomainLabels) { diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/EndpointComparer.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/EndpointComparer.cs index 362190ed..3371918f 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/EndpointComparer.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/EndpointComparer.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Configuration.AzureAppConfiguration { internal class EndpointComparer : IEqualityComparer { - public bool Equals(Uri endpoint1, Uri endpoint2) + public bool Equals(Uri? endpoint1, Uri? endpoint2) { return Uri.Compare(endpoint1, endpoint2, @@ -17,12 +17,12 @@ public bool Equals(Uri endpoint1, Uri endpoint2) StringComparison.OrdinalIgnoreCase) == 0; } - public int GetHashCode(Uri obj) + public int GetHashCode(Uri? obj) { if (obj is Uri uri) { // Have to convert the normalizedHost to lower case to ensure case insensetive comparison. string.GetHashCode(StringComparison.OrdinalIgnoreCase) isn't available in netstandard2.0 - string componentsToHash = uri.GetComponents(UriComponents.NormalizedHost | UriComponents.Port, UriFormat.SafeUnescaped)?.ToLower(); + string? componentsToHash = uri.GetComponents(UriComponents.NormalizedHost | UriComponents.Port, UriFormat.SafeUnescaped)?.ToLower(); return componentsToHash != null ? componentsToHash.GetHashCode() : -1; } diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Exceptions/KeyVaultReferenceException.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Exceptions/KeyVaultReferenceException.cs index 7d549a06..228773ac 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Exceptions/KeyVaultReferenceException.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Exceptions/KeyVaultReferenceException.cs @@ -18,8 +18,7 @@ public class KeyVaultReferenceException : Exception /// /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. /// - public KeyVaultReferenceException(string message, - Exception inner) + public KeyVaultReferenceException(string message, Exception? inner) : base(string.Empty, inner) { _message = message; @@ -34,26 +33,26 @@ public KeyVaultReferenceException(string message, /// /// The key of the Key Vault reference that caused the exception. /// - public string Key { get; set; } + public string? Key { get; set; } /// /// The label of the Key Vault reference that caused the exception. /// - public string Label { get; set; } + public string? Label { get; set; } /// /// The etag of the Key Vault reference that caused the exception. /// - public string Etag { get; set; } + public string? Etag { get; set; } /// /// The secret identifier used by the Azure Key Vault reference that caused the exception. /// - public string SecretIdentifier { get; set; } + public string? SecretIdentifier { get; set; } /// /// The error code, if available, describing the cause of the exception. /// - public string ErrorCode { get; set; } + public string? ErrorCode { get; set; } } } diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/ConfigurationClientExtensions.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/ConfigurationClientExtensions.cs index 18c03178..df23d0ad 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/ConfigurationClientExtensions.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/ConfigurationClientExtensions.cs @@ -64,7 +64,7 @@ public static async Task GetKeyValueChange(this ConfigurationCli public static async Task> GetKeyValueChangeCollection( this ConfigurationClient client, - IEnumerable keyValues, + IEnumerable? keyValues, GetKeyValueChangeCollectionOptions options, StringBuilder logDebugBuilder, StringBuilder logInfoBuilder, diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/EventGridEventExtensions.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/EventGridEventExtensions.cs index ee904324..5a510bb4 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/EventGridEventExtensions.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/EventGridEventExtensions.cs @@ -3,6 +3,7 @@ // using Azure.Messaging.EventGrid; using System; +using System.Diagnostics.CodeAnalysis; using System.Text.Json; namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.Extensions @@ -20,7 +21,7 @@ public static class EventGridEventExtensions /// EventGridEvent from EventGrid /// If this method returns true, the pushNotification object contains details populated from eventGridEvent. If this method returns false, the pushNotification object is null. /// - public static bool TryCreatePushNotification(this EventGridEvent eventGridEvent, out PushNotification pushNotification) + public static bool TryCreatePushNotification(this EventGridEvent eventGridEvent, [NotNullWhen(true)] out PushNotification? pushNotification) { pushNotification = null; @@ -29,9 +30,9 @@ public static bool TryCreatePushNotification(this EventGridEvent eventGridEvent, return false; } - if (Uri.TryCreate(eventGridEvent.Subject, UriKind.Absolute, out Uri resourceUri)) + if (Uri.TryCreate(eventGridEvent.Subject, UriKind.Absolute, out Uri? resourceUri)) { - string syncToken = null; + string? syncToken = null; try { diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/ListExtensions.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/ListExtensions.cs index 99024c3c..458eeddf 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/ListExtensions.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/ListExtensions.cs @@ -53,7 +53,7 @@ public static void AppendUnique(this List items, T item) throw new ArgumentNullException(nameof(item)); } - T existingItem = items.FirstOrDefault(s => Equals(s, item)); + T? existingItem = items.FirstOrDefault(s => Equals(s, item)); if (existingItem != null) { diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/StringExtensions.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/StringExtensions.cs index 7bcf7212..6522b33f 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/StringExtensions.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/StringExtensions.cs @@ -12,7 +12,7 @@ internal static class LabelFilters internal static class StringExtensions { - public static string NormalizeNull(this string s) + public static string? NormalizeNull(this string? s) { return s == LabelFilters.Null ? null : s; } diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/ClientFilter.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/ClientFilter.cs index 12145cd7..5f64ab19 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/ClientFilter.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/ClientFilter.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.FeatureManage { internal class ClientFilter { - public string Name { get; set; } + public string? Name { get; set; } public JsonElement Parameters { get; set; } } diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureConditions.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureConditions.cs index d1c23003..98a873f9 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureConditions.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureConditions.cs @@ -9,6 +9,6 @@ internal class FeatureConditions { public List ClientFilters { get; set; } = new List(); - public string RequirementType { get; set; } + public string? RequirementType { get; set; } } } diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureFilterTracing.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureFilterTracing.cs index deb6db49..1a484a71 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureFilterTracing.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureFilterTracing.cs @@ -43,7 +43,7 @@ public void ResetFeatureFilterTracing() UsesTargetingFilter = false; } - public void UpdateFeatureFilterTracing(string filterName) + public void UpdateFeatureFilterTracing(string? filterName) { if (PercentageFilterNames.Any(name => string.Equals(name, filterName, StringComparison.OrdinalIgnoreCase))) { diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureFlag.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureFlag.cs index aaf38540..0074130d 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureFlag.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureFlag.cs @@ -6,10 +6,10 @@ namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.FeatureManage { internal class FeatureFlag { - public string Id { get; set; } + public string? Id { get; set; } public bool Enabled { get; set; } - public FeatureConditions Conditions { get; set; } + public FeatureConditions? Conditions { get; set; } } } diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureFlagOptions.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureFlagOptions.cs index c63fddc1..c14e58b3 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureFlagOptions.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureFlagOptions.cs @@ -22,7 +22,7 @@ public class FeatureFlagOptions /// /// The label that feature flags will be selected from. /// - public string Label { get; set; } + public string? Label { get; set; } /// /// The time after which the cached values of the feature flags expire. Must be greater than or equal to 1 second. @@ -69,10 +69,10 @@ public FeatureFlagOptions Select(string featureFlagFilter, string labelFilter = string featureFlagPrefix = FeatureManagementConstants.FeatureFlagMarker + featureFlagFilter; FeatureFlagSelectors.AppendUnique(new KeyValueSelector - { - KeyFilter = featureFlagPrefix, - LabelFilter = labelFilter - }); + ( + keyFilter: featureFlagPrefix, + labelFilter: labelFilter + )); return this; } diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs index 058f353c..481c3663 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs @@ -20,11 +20,11 @@ public FeatureManagementKeyValueAdapter(FeatureFilterTracing featureFilterTracin _featureFilterTracing = featureFilterTracing ?? throw new ArgumentNullException(nameof(featureFilterTracing)); } - public Task>> ProcessKeyValue(ConfigurationSetting setting, Logger logger, CancellationToken cancellationToken) + public Task>> ProcessKeyValue(ConfigurationSetting setting, Logger? logger, CancellationToken cancellationToken) { FeatureFlag featureFlag = ParseFeatureFlag(setting.Key, setting.Value); - var keyValues = new List>(); + var keyValues = new List>(); if (!string.IsNullOrEmpty(featureFlag.Id)) { @@ -32,7 +32,7 @@ public Task>> ProcessKeyValue(Configura { if (featureFlag.Conditions?.ClientFilters == null || !featureFlag.Conditions.ClientFilters.Any()) { - keyValues.Add(new KeyValuePair($"{FeatureManagementConstants.SectionName}:{featureFlag.Id}", true.ToString())); + keyValues.Add(new KeyValuePair($"{FeatureManagementConstants.SectionName}:{featureFlag.Id}", true.ToString())); } else { @@ -42,11 +42,11 @@ public Task>> ProcessKeyValue(Configura _featureFilterTracing.UpdateFeatureFilterTracing(clientFilter.Name); - keyValues.Add(new KeyValuePair($"{FeatureManagementConstants.SectionName}:{featureFlag.Id}:{FeatureManagementConstants.EnabledFor}:{i}:Name", clientFilter.Name)); + keyValues.Add(new KeyValuePair($"{FeatureManagementConstants.SectionName}:{featureFlag.Id}:{FeatureManagementConstants.EnabledFor}:{i}:Name", clientFilter.Name)); - foreach (KeyValuePair kvp in new JsonFlattener().FlattenJson(clientFilter.Parameters)) + foreach (KeyValuePair kvp in new JsonFlattener().FlattenJson(clientFilter.Parameters)) { - keyValues.Add(new KeyValuePair($"{FeatureManagementConstants.SectionName}:{featureFlag.Id}:{FeatureManagementConstants.EnabledFor}:{i}:Parameters:{kvp.Key}", kvp.Value)); + keyValues.Add(new KeyValuePair($"{FeatureManagementConstants.SectionName}:{featureFlag.Id}:{FeatureManagementConstants.EnabledFor}:{i}:Parameters:{kvp.Key}", kvp.Value)); } } @@ -54,7 +54,7 @@ public Task>> ProcessKeyValue(Configura // process RequirementType only when filters are not empty if (featureFlag.Conditions.RequirementType != null) { - keyValues.Add(new KeyValuePair( + keyValues.Add(new KeyValuePair( $"{FeatureManagementConstants.SectionName}:{featureFlag.Id}:{FeatureManagementConstants.RequirementType}", featureFlag.Conditions.RequirementType)); } @@ -62,22 +62,22 @@ public Task>> ProcessKeyValue(Configura } else { - keyValues.Add(new KeyValuePair($"{FeatureManagementConstants.SectionName}:{featureFlag.Id}", false.ToString())); + keyValues.Add(new KeyValuePair($"{FeatureManagementConstants.SectionName}:{featureFlag.Id}", false.ToString())); } } - return Task.FromResult>>(keyValues); + return Task.FromResult>>(keyValues); } public bool CanProcess(ConfigurationSetting setting) { - string contentType = setting?.ContentType?.Split(';')[0].Trim(); + string? contentType = setting.ContentType?.Split(';')[0].Trim(); return string.Equals(contentType, FeatureManagementConstants.ContentType) || setting.Key.StartsWith(FeatureManagementConstants.FeatureFlagMarker); } - public void InvalidateCache(ConfigurationSetting setting = null) + public void InvalidateCache(ConfigurationSetting? setting = null) { return; } @@ -117,7 +117,7 @@ private FeatureFlag ParseFeatureFlag(string settingKey, string settingValue) continue; } - string propertyName = reader.GetString(); + string? propertyName = reader.GetString(); switch (propertyName) { @@ -125,7 +125,7 @@ private FeatureFlag ParseFeatureFlag(string settingKey, string settingValue) { if (reader.Read() && reader.TokenType == JsonTokenType.String) { - featureFlag.Id = reader.GetString(); + featureFlag.Id = reader.GetString()!; } else if (reader.TokenType != JsonTokenType.Null) { @@ -205,7 +205,7 @@ private FeatureConditions ParseFeatureConditions(ref Utf8JsonReader reader, stri continue; } - string conditionsPropertyName = reader.GetString(); + string? conditionsPropertyName = reader.GetString(); switch (conditionsPropertyName) { @@ -248,7 +248,7 @@ private FeatureConditions ParseFeatureConditions(ref Utf8JsonReader reader, stri { if (reader.Read() && reader.TokenType == JsonTokenType.String) { - featureConditions.RequirementType = reader.GetString(); + featureConditions.RequirementType = reader.GetString()!; } else if (reader.TokenType != JsonTokenType.Null) { @@ -283,7 +283,7 @@ private ClientFilter ParseClientFilter(ref Utf8JsonReader reader, string setting continue; } - string clientFiltersPropertyName = reader.GetString(); + string? clientFiltersPropertyName = reader.GetString(); switch (clientFiltersPropertyName) { @@ -291,7 +291,7 @@ private ClientFilter ParseClientFilter(ref Utf8JsonReader reader, string setting { if (reader.Read() && reader.TokenType == JsonTokenType.String) { - clientFilter.Name = reader.GetString(); + clientFilter.Name = reader.GetString()!; } else if (reader.TokenType != JsonTokenType.Null) { diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/JsonFlattener.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/JsonFlattener.cs index fa5983c1..6d7f60f5 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/JsonFlattener.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/JsonFlattener.cs @@ -3,6 +3,7 @@ // using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text.Json; @@ -10,16 +11,17 @@ namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.FeatureManage { internal class JsonFlattener { - private readonly List> _data = new List>(); + private readonly List> _data = new List>(); private readonly Stack _context = new Stack(); - private string _currentPath; + private string? _currentPath; - public List> FlattenJson(JsonElement rootElement) + public List> FlattenJson(JsonElement rootElement) { VisitJsonElement(rootElement); return _data; } + [MemberNotNull(nameof(_currentPath))] private void VisitJsonProperty(JsonProperty property) { EnterContext(property.Name); @@ -54,7 +56,7 @@ private void VisitJsonElement(JsonElement element) case JsonValueKind.True: case JsonValueKind.False: case JsonValueKind.Null: - _data.Add(new KeyValuePair(_currentPath, element.ToString())); + _data.Add(new KeyValuePair(_currentPath!, element.ToString())); break; default: @@ -62,12 +64,14 @@ private void VisitJsonElement(JsonElement element) } } + [MemberNotNull(nameof(_currentPath))] private void EnterContext(string context) { _context.Push(context); _currentPath = ConfigurationPath.Combine(_context.Reverse()); } + [MemberNotNull(nameof(_currentPath))] private void ExitContext() { _context.Pop(); diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/GetKeyValueChangeCollectionOptions.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/GetKeyValueChangeCollectionOptions.cs index 5cb9b83d..18ab2b09 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/GetKeyValueChangeCollectionOptions.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/GetKeyValueChangeCollectionOptions.cs @@ -5,9 +5,9 @@ namespace Microsoft.Extensions.Configuration.AzureAppConfiguration { internal class GetKeyValueChangeCollectionOptions { - public string KeyFilter { get; set; } - public string Label { get; set; } + public string? KeyFilter { get; set; } + public string? Label { get; set; } public bool RequestTracingEnabled { get; set; } - public RequestTracingOptions RequestTracingOptions { get; set; } + public RequestTracingOptions? RequestTracingOptions { get; set; } } } diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/IConfigurationClientManager.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/IConfigurationClientManager.cs index 798e35e6..8e8bfd08 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/IConfigurationClientManager.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/IConfigurationClientManager.cs @@ -16,6 +16,7 @@ internal interface IConfigurationClientManager bool UpdateSyncToken(Uri endpoint, string syncToken); - Uri GetEndpointForClient(ConfigurationClient client); + /// null if client is not found + Uri? GetEndpointForClient(ConfigurationClient client); } } diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/IConfigurationRefresher.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/IConfigurationRefresher.cs index b47184a2..96ce06de 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/IConfigurationRefresher.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/IConfigurationRefresher.cs @@ -17,7 +17,7 @@ public interface IConfigurationRefresher /// /// The App Configuration endpoint. /// - Uri AppConfigurationEndpoint { get; } + Uri? AppConfigurationEndpoint { get; } /// /// Refreshes the data from App Configuration asynchronously. diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/IKeyValueAdapter.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/IKeyValueAdapter.cs index 48fc85c5..f1fdb39d 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/IKeyValueAdapter.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/IKeyValueAdapter.cs @@ -10,11 +10,11 @@ namespace Microsoft.Extensions.Configuration.AzureAppConfiguration { internal interface IKeyValueAdapter { - Task>> ProcessKeyValue(ConfigurationSetting setting, Logger logger, CancellationToken cancellationToken); + Task>> ProcessKeyValue(ConfigurationSetting setting, Logger logger, CancellationToken cancellationToken); bool CanProcess(ConfigurationSetting setting); - void InvalidateCache(ConfigurationSetting setting = null); + void InvalidateCache(ConfigurationSetting? setting = null); bool NeedsRefresh(); } diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/JsonKeyValueAdapter.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/JsonKeyValueAdapter.cs index 979811d7..c3e48d5b 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/JsonKeyValueAdapter.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/JsonKeyValueAdapter.cs @@ -22,7 +22,7 @@ internal class JsonKeyValueAdapter : IKeyValueAdapter KeyVaultConstants.ContentType }; - public Task>> ProcessKeyValue(ConfigurationSetting setting, Logger logger, CancellationToken cancellationToken) + public Task>> ProcessKeyValue(ConfigurationSetting setting, Logger? logger, CancellationToken cancellationToken) { if (setting == null) { @@ -31,7 +31,7 @@ public Task>> ProcessKeyValue(Configura string rootJson = $"{{\"{setting.Key}\":{setting.Value}}}"; - List> keyValuePairs = new List>(); + List> keyValuePairs = new List>(); try { @@ -43,10 +43,10 @@ public Task>> ProcessKeyValue(Configura catch (JsonException) { // If the value is not a valid JSON, treat it like regular string value - return Task.FromResult>>(new[] { new KeyValuePair(setting.Key, setting.Value) }); + return Task.FromResult>>(new[] { new KeyValuePair(setting.Key, setting.Value) }); } - return Task.FromResult>>(keyValuePairs); + return Task.FromResult>>(keyValuePairs); } public bool CanProcess(ConfigurationSetting setting) @@ -95,7 +95,7 @@ public bool CanProcess(ConfigurationSetting setting) return false; } - public void InvalidateCache(ConfigurationSetting setting = null) + public void InvalidateCache(ConfigurationSetting? setting = null) { return; } diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/KeyValueChange.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/KeyValueChange.cs index 7d41e107..170066c9 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/KeyValueChange.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/KeyValueChange.cs @@ -18,8 +18,11 @@ internal struct KeyValueChange public string Key { get; set; } - public string Label { get; set; } + public string? Label { get; set; } - public ConfigurationSetting Current { get; set; } + /// + /// Is null only when ChangeType is KeyValueChangeType.Deleted + /// + public ConfigurationSetting? Current { get; set; } } } diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/LogHelper.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/LogHelper.cs index e7429a9e..798158eb 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/LogHelper.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/LogHelper.cs @@ -5,7 +5,7 @@ namespace Microsoft.Extensions.Configuration.AzureAppConfiguration { internal static class LogHelper { - public static string BuildKeyValueReadMessage(KeyValueChangeType changeType, string key, string label, string endpoint) + public static string BuildKeyValueReadMessage(KeyValueChangeType changeType, string key, string? label, string? endpoint) { return $"{LoggingConstants.RefreshKeyValueRead} Change:'{changeType}' Key:'{key}' Label:'{label}' Endpoint:'{endpoint?.TrimEnd('/')}'"; } @@ -20,12 +20,12 @@ public static string BuildConfigurationUpdatedMessage() return LoggingConstants.RefreshConfigurationUpdatedSuccess; } - public static string BuildFeatureFlagsUnchangedMessage(string endpoint) + public static string BuildFeatureFlagsUnchangedMessage(string? endpoint) { return $"{LoggingConstants.RefreshFeatureFlagsUnchanged} Endpoint:'{endpoint?.TrimEnd('/')}'"; } - public static string BuildFeatureFlagReadMessage(string key, string label, string endpoint) + public static string BuildFeatureFlagReadMessage(string key, string? label, string? endpoint) { return $"{LoggingConstants.RefreshFeatureFlagRead} Key:'{key}' Label:'{label}' Endpoint:'{endpoint?.TrimEnd('/')}'"; } @@ -35,7 +35,7 @@ public static string BuildFeatureFlagUpdatedMessage(string key) return $"{LoggingConstants.RefreshFeatureFlagUpdated} Key:'{key}'"; } - public static string BuildKeyVaultSecretReadMessage(string key, string label) + public static string BuildKeyVaultSecretReadMessage(string key, string? label) { return $"{LoggingConstants.RefreshKeyVaultSecretRead} Key:'{key}' Label:'{label}'"; } @@ -75,12 +75,12 @@ public static string BuildPushNotificationUnregisteredEndpointMessage(string res return $"{LoggingConstants.PushNotificationUnregisteredEndpoint} '{resourceUri}'."; } - public static string BuildFailoverMessage(string originalEndpoint, string currentEndpoint) + public static string BuildFailoverMessage(string? originalEndpoint, string? currentEndpoint) { return $"{LoggingConstants.RefreshFailedToGetSettingsFromEndpoint} '{originalEndpoint?.TrimEnd('/')}'. {LoggingConstants.FailingOverToEndpoint} '{currentEndpoint?.TrimEnd('/')}'."; } - public static string BuildLastEndpointFailedMessage(string endpoint) + public static string BuildLastEndpointFailedMessage(string? endpoint) { return $"{LoggingConstants.RefreshFailedToGetSettingsFromEndpoint} '{endpoint?.TrimEnd('/')}'."; } diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Logger.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Logger.cs index e6862408..e0f72e4a 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Logger.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Logger.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Configuration.AzureAppConfiguration { internal class Logger { - ILogger _logger; + ILogger? _logger; public Logger() { } diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Microsoft.Extensions.Configuration.AzureAppConfiguration.csproj b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Microsoft.Extensions.Configuration.AzureAppConfiguration.csproj index a2cfca3c..c6cb96ca 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Microsoft.Extensions.Configuration.AzureAppConfiguration.csproj +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Microsoft.Extensions.Configuration.AzureAppConfiguration.csproj @@ -4,7 +4,7 @@ netstandard2.0;netstandard2.1 - 8.0 + 11 Microsoft.Extensions.Configuration.AzureAppConfiguration is a configuration provider for the .NET Core framework that allows developers to use Microsoft Azure App Configuration service as a configuration source in their applications. true false diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Models/KeyValueIdentifier.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Models/KeyValueIdentifier.cs index 9a77346a..c35b4cd0 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Models/KeyValueIdentifier.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Models/KeyValueIdentifier.cs @@ -15,15 +15,15 @@ internal struct KeyValueIdentifier /// /// Label of the key-value in App Configuration. /// - public string Label { get; set; } + public string? Label { get; set; } - public KeyValueIdentifier(string key, string label) + public KeyValueIdentifier(string key, string? label) { Key = key; Label = label.NormalizeNull(); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (obj is KeyValueIdentifier keyLabel) { diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Models/KeyValueSelector.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Models/KeyValueSelector.cs index 5491d04d..0c8de357 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Models/KeyValueSelector.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Models/KeyValueSelector.cs @@ -8,28 +8,45 @@ namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.Models /// public class KeyValueSelector { + /// + /// Snapshot selector + /// + public KeyValueSelector(string snapshotName) + { + SnapshotName = snapshotName; + } + + /// + /// Key selector + /// + public KeyValueSelector(string keyFilter, string labelFilter) + { + KeyFilter = keyFilter; + LabelFilter = labelFilter; + } + /// /// A filter that determines the set of keys that are included in the configuration provider. /// /// See the documentation for this provider for details on the format of filter expressions - public string KeyFilter { get; set; } + public string? KeyFilter { get; set; } /// - /// A filter that determines what label to use when selecting key-values for the the configuration provider. + /// A filter that determines what label to use when selecting key-values for the configuration provider. /// - public string LabelFilter { get; set; } + public string? LabelFilter { get; set; } /// /// The name of the Azure App Configuration snapshot to use when selecting key-values for the configuration provider. /// - public string SnapshotName { get; set; } + public string? SnapshotName { get; set; } /// /// Determines whether the specified object is equal to the current object. /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (obj is KeyValueSelector selector) { diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Models/KeyValueWatcher.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Models/KeyValueWatcher.cs index 121a35be..0dcce894 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Models/KeyValueWatcher.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Models/KeyValueWatcher.cs @@ -11,12 +11,12 @@ internal class KeyValueWatcher /// /// Key of the key-value to be watched. /// - public string Key { get; set; } + public required string Key { get; set; } /// /// Label of the key-value to be watched. /// - public string Label { get; set; } + public required string Label { get; set; } /// /// A flag to refresh all key-values. @@ -33,7 +33,7 @@ internal class KeyValueWatcher /// public DateTimeOffset CacheExpires { get; set; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (obj is KeyValueWatcher kvWatcher) { diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/PushNotification.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/PushNotification.cs index 1690645d..3723e7ab 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/PushNotification.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/PushNotification.cs @@ -13,16 +13,16 @@ public class PushNotification /// /// The URI of the resource which triggered the . /// - public Uri ResourceUri { get; set; } + public required Uri ResourceUri { get; set; } /// /// The Synchronization Token to be added to the next request to the App Configuration Service. /// - public string SyncToken { get; set; } + public required string SyncToken { get; set; } /// /// The Type of Event which triggered the . /// - public string EventType { get; set; } + public required string EventType { get; set; } } } diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/RandomGenerator.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/RandomGenerator.cs index dc122519..fb9e9a08 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/RandomGenerator.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/RandomGenerator.cs @@ -20,7 +20,7 @@ static class RandomGenerator public static double NextDouble() { - return _rnd.Value.NextDouble(); + return _rnd.Value!.NextDouble(); } } } diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/RequestTracingOptions.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/RequestTracingOptions.cs index b5769b9d..fb783d41 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/RequestTracingOptions.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/RequestTracingOptions.cs @@ -40,12 +40,12 @@ internal class RequestTracingOptions /// /// Version of the Microsoft.FeatureManagement assembly, if present in the application. /// - public string FeatureManagementVersion { get; set; } + public string? FeatureManagementVersion { get; set; } /// /// Version of the Microsoft.FeatureManagement.AspNetCore assembly, if present in the application. /// - public string FeatureManagementAspNetCoreVersion { get; set; } + public string? FeatureManagementAspNetCoreVersion { get; set; } /// /// Flag to indicate whether Microsoft.AspNetCore.SignalR assembly is present in the application. diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/TracingUtils.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/TracingUtils.cs index 2c8d9869..1728888d 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/TracingUtils.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/TracingUtils.cs @@ -63,7 +63,7 @@ public static bool IsDevEnvironment() { try { - string envType = Environment.GetEnvironmentVariable(RequestTracingConstants.AspNetCoreEnvironmentVariable) ?? + string? envType = Environment.GetEnvironmentVariable(RequestTracingConstants.AspNetCoreEnvironmentVariable) ?? Environment.GetEnvironmentVariable(RequestTracingConstants.DotNetCoreEnvironmentVariable); if (envType != null && envType.Equals(RequestTracingConstants.DevelopmentEnvironmentName, StringComparison.OrdinalIgnoreCase)) { @@ -75,7 +75,7 @@ public static bool IsDevEnvironment() return false; } - public static string GetAssemblyVersion(string assemblyName) + public static string? GetAssemblyVersion(string? assemblyName) { if (!string.IsNullOrEmpty(assemblyName)) { @@ -86,7 +86,7 @@ public static string GetAssemblyVersion(string assemblyName) return null; } - public static async Task CallWithRequestTracing(bool tracingEnabled, RequestType requestType, RequestTracingOptions requestTracingOptions, Func clientCall) + public static async Task CallWithRequestTracing(bool tracingEnabled, RequestType requestType, RequestTracingOptions? requestTracingOptions, Func clientCall) { string correlationContextHeader = ""; @@ -115,39 +115,39 @@ public static async Task CallWithRequestTracing(bool tracingEnabled, RequestType private static string CreateCorrelationContextHeader(RequestType requestType, RequestTracingOptions requestTracingOptions) { - IList> correlationContextKeyValues = new List>(); + IList> correlationContextKeyValues = new List>(); IList correlationContextTags = new List(); - correlationContextKeyValues.Add(new KeyValuePair(RequestTracingConstants.RequestTypeKey, Enum.GetName(typeof(RequestType), requestType))); + correlationContextKeyValues.Add(new KeyValuePair(RequestTracingConstants.RequestTypeKey, Enum.GetName(typeof(RequestType), requestType))); if (requestTracingOptions.ReplicaCount > 0) { - correlationContextKeyValues.Add(new KeyValuePair(RequestTracingConstants.ReplicaCountKey, requestTracingOptions.ReplicaCount.ToString())); + correlationContextKeyValues.Add(new KeyValuePair(RequestTracingConstants.ReplicaCountKey, requestTracingOptions.ReplicaCount.ToString())); } if (requestTracingOptions.HostType != HostType.Unidentified) { - correlationContextKeyValues.Add(new KeyValuePair(RequestTracingConstants.HostTypeKey, Enum.GetName(typeof(HostType), requestTracingOptions.HostType))); + correlationContextKeyValues.Add(new KeyValuePair(RequestTracingConstants.HostTypeKey, Enum.GetName(typeof(HostType), requestTracingOptions.HostType))); } if (requestTracingOptions.IsDevEnvironment) { - correlationContextKeyValues.Add(new KeyValuePair(RequestTracingConstants.EnvironmentKey, RequestTracingConstants.DevEnvironmentValue)); + correlationContextKeyValues.Add(new KeyValuePair(RequestTracingConstants.EnvironmentKey, RequestTracingConstants.DevEnvironmentValue)); } if (requestTracingOptions.FilterTracing.UsesAnyFeatureFilter()) { - correlationContextKeyValues.Add(new KeyValuePair(RequestTracingConstants.FilterTypeKey, requestTracingOptions.FilterTracing.ToString())); + correlationContextKeyValues.Add(new KeyValuePair(RequestTracingConstants.FilterTypeKey, requestTracingOptions.FilterTracing.ToString())); } if (requestTracingOptions.FeatureManagementVersion != null) { - correlationContextKeyValues.Add(new KeyValuePair(RequestTracingConstants.FeatureManagementVersionKey, requestTracingOptions.FeatureManagementVersion)); + correlationContextKeyValues.Add(new KeyValuePair(RequestTracingConstants.FeatureManagementVersionKey, requestTracingOptions.FeatureManagementVersion)); } if (requestTracingOptions.FeatureManagementAspNetCoreVersion != null) { - correlationContextKeyValues.Add(new KeyValuePair(RequestTracingConstants.FeatureManagementAspNetCoreVersionKey, requestTracingOptions.FeatureManagementAspNetCoreVersion)); + correlationContextKeyValues.Add(new KeyValuePair(RequestTracingConstants.FeatureManagementAspNetCoreVersionKey, requestTracingOptions.FeatureManagementAspNetCoreVersion)); } if (requestTracingOptions.IsKeyVaultConfigured) @@ -167,7 +167,7 @@ private static string CreateCorrelationContextHeader(RequestType requestType, Re var sb = new StringBuilder(); - foreach (KeyValuePair kvp in correlationContextKeyValues) + foreach (KeyValuePair kvp in correlationContextKeyValues) { if (sb.Length > 0) { diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/UserAgentHeaderPolicy.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/UserAgentHeaderPolicy.cs index 3c8878a4..44b011e9 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/UserAgentHeaderPolicy.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/UserAgentHeaderPolicy.cs @@ -25,7 +25,7 @@ public override ValueTask ProcessAsync(HttpMessage message, ReadOnlyMemory().InformationalVersion; + string informationalVersion = assembly.GetCustomAttribute()!.InformationalVersion; return $"{assembly.GetName().Name}/{informationalVersion}"; } diff --git a/tests/Tests.AzureAppConfiguration.AspNetCore/Tests.AzureAppConfiguration.AspNetCore.csproj b/tests/Tests.AzureAppConfiguration.AspNetCore/Tests.AzureAppConfiguration.AspNetCore.csproj index f15c5eb7..785b6c27 100644 --- a/tests/Tests.AzureAppConfiguration.AspNetCore/Tests.AzureAppConfiguration.AspNetCore.csproj +++ b/tests/Tests.AzureAppConfiguration.AspNetCore/Tests.AzureAppConfiguration.AspNetCore.csproj @@ -7,6 +7,7 @@ true ..\..\build\AzureAppConfiguration.snk false + disable diff --git a/tests/Tests.AzureAppConfiguration.Functions.Worker/Tests.AzureAppConfiguration.Functions.Worker.csproj b/tests/Tests.AzureAppConfiguration.Functions.Worker/Tests.AzureAppConfiguration.Functions.Worker.csproj index 02f3f7e2..b7c61210 100644 --- a/tests/Tests.AzureAppConfiguration.Functions.Worker/Tests.AzureAppConfiguration.Functions.Worker.csproj +++ b/tests/Tests.AzureAppConfiguration.Functions.Worker/Tests.AzureAppConfiguration.Functions.Worker.csproj @@ -7,6 +7,7 @@ true ..\..\build\AzureAppConfiguration.snk false + disable diff --git a/tests/Tests.AzureAppConfiguration/Tests.AzureAppConfiguration.csproj b/tests/Tests.AzureAppConfiguration/Tests.AzureAppConfiguration.csproj index 8849645f..0ef08b3b 100644 --- a/tests/Tests.AzureAppConfiguration/Tests.AzureAppConfiguration.csproj +++ b/tests/Tests.AzureAppConfiguration/Tests.AzureAppConfiguration.csproj @@ -8,6 +8,7 @@ ..\..\build\AzureAppConfiguration.snk false false + disable