diff --git a/src/OpenFeature/Api.cs b/src/OpenFeature/Api.cs index 5440151f..3fa38916 100644 --- a/src/OpenFeature/Api.cs +++ b/src/OpenFeature/Api.cs @@ -32,7 +32,7 @@ public sealed class Api : IEventBus public static Api Instance { get; } = new Api(); // Explicit static constructor to tell C# compiler - // not to mark type as beforefieldinit + // not to mark type as beforeFieldInit // IE Lazy way of ensuring this is thread safe without using locks static Api() { } private Api() { } @@ -46,7 +46,7 @@ private Api() { } public async Task SetProviderAsync(FeatureProvider featureProvider) { this._eventExecutor.RegisterDefaultFeatureProvider(featureProvider); - await this._repository.SetProviderAsync(featureProvider, this.GetContext(), AfterInitialization, AfterError).ConfigureAwait(false); + await this._repository.SetProviderAsync(featureProvider, this.GetContext(), this.AfterInitialization, this.AfterError).ConfigureAwait(false); } /// @@ -62,7 +62,7 @@ public async Task SetProviderAsync(string clientName, FeatureProvider featurePro throw new ArgumentNullException(nameof(clientName)); } this._eventExecutor.RegisterClientFeatureProvider(clientName, featureProvider); - await this._repository.SetProviderAsync(clientName, featureProvider, this.GetContext(), AfterInitialization, AfterError).ConfigureAwait(false); + await this._repository.SetProviderAsync(clientName, featureProvider, this.GetContext(), this.AfterInitialization, this.AfterError).ConfigureAwait(false); } /// @@ -101,7 +101,7 @@ public FeatureProvider GetProvider(string clientName) /// /// /// - public Metadata GetProviderMetadata() => this.GetProvider().GetMetadata(); + public Metadata? GetProviderMetadata() => this.GetProvider().GetMetadata(); /// /// Gets providers metadata assigned to the given clientName. If the clientName has no provider @@ -109,7 +109,7 @@ public FeatureProvider GetProvider(string clientName) /// /// Name of client /// Metadata assigned to provider - public Metadata GetProviderMetadata(string clientName) => this.GetProvider(clientName).GetMetadata(); + public Metadata? GetProviderMetadata(string clientName) => this.GetProvider(clientName).GetMetadata(); /// /// Create a new instance of using the current provider @@ -121,7 +121,7 @@ public FeatureProvider GetProvider(string clientName) /// public FeatureClient GetClient(string? name = null, string? version = null, ILogger? logger = null, EvaluationContext? context = null) => - new FeatureClient(() => _repository.GetProvider(name), name, version, logger, context); + new FeatureClient(() => this._repository.GetProvider(name), name, version, logger, context); /// /// Appends list of hooks to global hooks list @@ -277,7 +277,7 @@ private async Task AfterInitialization(FeatureProvider provider) { Type = ProviderEventTypes.ProviderReady, Message = "Provider initialization complete", - ProviderName = provider.GetMetadata().Name, + ProviderName = provider.GetMetadata()?.Name, }; await this._eventExecutor.EventChannel.Writer.WriteAsync(new Event { Provider = provider, EventPayload = eventPayload }).ConfigureAwait(false); @@ -286,10 +286,9 @@ private async Task AfterInitialization(FeatureProvider provider) /// /// Update the provider state to ERROR and emit an ERROR after failed init. /// - private async Task AfterError(FeatureProvider provider, Exception ex) - + private async Task AfterError(FeatureProvider provider, Exception? ex) { - provider.Status = typeof(ProviderFatalException) == ex.GetType() ? ProviderStatus.Fatal : ProviderStatus.Error; + provider.Status = typeof(ProviderFatalException) == ex?.GetType() ? ProviderStatus.Fatal : ProviderStatus.Error; var eventPayload = new ProviderEventPayload { Type = ProviderEventTypes.ProviderError, diff --git a/src/OpenFeature/Constant/Reason.cs b/src/OpenFeature/Constant/Reason.cs index a60ce78a..eac06c1e 100644 --- a/src/OpenFeature/Constant/Reason.cs +++ b/src/OpenFeature/Constant/Reason.cs @@ -9,42 +9,42 @@ public static class Reason /// /// Use when the flag is matched based on the evaluation context user data /// - public static string TargetingMatch = "TARGETING_MATCH"; + public const string TargetingMatch = "TARGETING_MATCH"; /// /// Use when the flag is matched based on a split rule in the feature flag provider /// - public static string Split = "SPLIT"; + public const string Split = "SPLIT"; /// /// Use when the flag is disabled in the feature flag provider /// - public static string Disabled = "DISABLED"; + public const string Disabled = "DISABLED"; /// /// Default reason when evaluating flag /// - public static string Default = "DEFAULT"; + public const string Default = "DEFAULT"; /// /// The resolved value is static (no dynamic evaluation) /// - public static string Static = "STATIC"; + public const string Static = "STATIC"; /// /// The resolved value was retrieved from cache /// - public static string Cached = "CACHED"; + public const string Cached = "CACHED"; /// /// Use when an unknown reason is encountered when evaluating flag. /// An example of this is if the feature provider returns a reason that is not defined in the spec /// - public static string Unknown = "UNKNOWN"; + public const string Unknown = "UNKNOWN"; /// /// Use this flag when abnormal execution is encountered. /// - public static string Error = "ERROR"; + public const string Error = "ERROR"; } } diff --git a/src/OpenFeature/Error/ProviderFatalException.cs b/src/OpenFeature/Error/ProviderFatalException.cs index fae8712a..894a583d 100644 --- a/src/OpenFeature/Error/ProviderFatalException.cs +++ b/src/OpenFeature/Error/ProviderFatalException.cs @@ -4,7 +4,7 @@ namespace OpenFeature.Error { - /// the + /// /// An exception that signals the provider has entered an irrecoverable error state. /// [ExcludeFromCodeCoverage] diff --git a/src/OpenFeature/EventExecutor.cs b/src/OpenFeature/EventExecutor.cs index 5dfd7dbe..ad53a949 100644 --- a/src/OpenFeature/EventExecutor.cs +++ b/src/OpenFeature/EventExecutor.cs @@ -206,7 +206,7 @@ private void EmitOnRegistration(FeatureProvider? provider, ProviderEventTypes ev { handler.Invoke(new ProviderEventPayload { - ProviderName = provider.GetMetadata().Name, + ProviderName = provider.GetMetadata()?.Name, Type = eventType, Message = message }); @@ -322,6 +322,7 @@ private void UpdateProviderStatus(FeatureProvider provider, ProviderEventPayload case ProviderEventTypes.ProviderError: provider.Status = eventPayload.ErrorType == ErrorType.ProviderFatal ? ProviderStatus.Fatal : ProviderStatus.Error; break; + case ProviderEventTypes.ProviderConfigurationChanged: default: break; } } diff --git a/src/OpenFeature/FeatureProvider.cs b/src/OpenFeature/FeatureProvider.cs index 32635d95..c4ce8783 100644 --- a/src/OpenFeature/FeatureProvider.cs +++ b/src/OpenFeature/FeatureProvider.cs @@ -11,14 +11,14 @@ namespace OpenFeature { /// /// The provider interface describes the abstraction layer for a feature flag provider. - /// A provider acts as the translates layer between the generic feature flag structure to a target feature flag system. + /// A provider acts as it translates layer between the generic feature flag structure to a target feature flag system. /// /// Provider specification public abstract class FeatureProvider { /// - /// Gets a immutable list of hooks that belong to the provider. - /// By default return a empty list + /// Gets an immutable list of hooks that belong to the provider. + /// By default, return an empty list /// /// Executed in the order of hooks /// before: API, Client, Invocation, Provider @@ -38,7 +38,7 @@ public abstract class FeatureProvider /// Metadata describing the provider. /// /// - public abstract Metadata GetMetadata(); + public abstract Metadata? GetMetadata(); /// /// Resolves a boolean feature flag diff --git a/src/OpenFeature/Model/EvaluationContextBuilder.cs b/src/OpenFeature/Model/EvaluationContextBuilder.cs index 1afb02fc..c672c401 100644 --- a/src/OpenFeature/Model/EvaluationContextBuilder.cs +++ b/src/OpenFeature/Model/EvaluationContextBuilder.cs @@ -140,9 +140,9 @@ public EvaluationContextBuilder Merge(EvaluationContext context) { string? newTargetingKey = ""; - if (!string.IsNullOrWhiteSpace(TargetingKey)) + if (!string.IsNullOrWhiteSpace(this.TargetingKey)) { - newTargetingKey = TargetingKey; + newTargetingKey = this.TargetingKey; } if (!string.IsNullOrWhiteSpace(context.TargetingKey)) diff --git a/src/OpenFeature/Model/FlagEvaluationOptions.cs b/src/OpenFeature/Model/FlagEvaluationOptions.cs index 7bde600c..8bba0aef 100644 --- a/src/OpenFeature/Model/FlagEvaluationOptions.cs +++ b/src/OpenFeature/Model/FlagEvaluationOptions.cs @@ -10,12 +10,12 @@ namespace OpenFeature.Model public sealed class FlagEvaluationOptions { /// - /// A immutable list of + /// An immutable list of /// public IImmutableList Hooks { get; } /// - /// A immutable dictionary of hook hints + /// An immutable dictionary of hook hints /// public IImmutableDictionary HookHints { get; } diff --git a/src/OpenFeature/Model/ImmutableMetadata.cs b/src/OpenFeature/Model/ImmutableMetadata.cs index 40d452d0..1f2c6f8a 100644 --- a/src/OpenFeature/Model/ImmutableMetadata.cs +++ b/src/OpenFeature/Model/ImmutableMetadata.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Collections.Immutable; -#nullable enable namespace OpenFeature.Model; /// diff --git a/src/OpenFeature/Model/Value.cs b/src/OpenFeature/Model/Value.cs index 5af3b8b3..88fb0734 100644 --- a/src/OpenFeature/Model/Value.cs +++ b/src/OpenFeature/Model/Value.cs @@ -139,49 +139,49 @@ public Value(Object value) public object? AsObject => this._innerValue; /// - /// Returns the underlying int value - /// Value will be null if it isn't a integer + /// Returns the underlying int value. + /// Value will be null if it isn't an integer /// /// Value as int - public int? AsInteger => this.IsNumber ? (int?)Convert.ToInt32((double?)this._innerValue) : null; + public int? AsInteger => this.IsNumber ? Convert.ToInt32((double?)this._innerValue) : null; /// - /// Returns the underlying bool value + /// Returns the underlying bool value. /// Value will be null if it isn't a bool /// /// Value as bool public bool? AsBoolean => this.IsBoolean ? (bool?)this._innerValue : null; /// - /// Returns the underlying double value + /// Returns the underlying double value. /// Value will be null if it isn't a double /// /// Value as int public double? AsDouble => this.IsNumber ? (double?)this._innerValue : null; /// - /// Returns the underlying string value + /// Returns the underlying string value. /// Value will be null if it isn't a string /// /// Value as string public string? AsString => this.IsString ? (string?)this._innerValue : null; /// - /// Returns the underlying Structure value + /// Returns the underlying Structure value. /// Value will be null if it isn't a Structure /// /// Value as Structure public Structure? AsStructure => this.IsStructure ? (Structure?)this._innerValue : null; /// - /// Returns the underlying List value + /// Returns the underlying List value. /// Value will be null if it isn't a List /// /// Value as List public IImmutableList? AsList => this.IsList ? (IImmutableList?)this._innerValue : null; /// - /// Returns the underlying DateTime value + /// Returns the underlying DateTime value. /// Value will be null if it isn't a DateTime /// /// Value as DateTime diff --git a/src/OpenFeature/OpenFeatureClient.cs b/src/OpenFeature/OpenFeatureClient.cs index 767e8b11..08e29533 100644 --- a/src/OpenFeature/OpenFeatureClient.cs +++ b/src/OpenFeature/OpenFeatureClient.cs @@ -212,11 +212,8 @@ private async Task> EvaluateFlagAsync( var resolveValueDelegate = providerInfo.Item1; var provider = providerInfo.Item2; - // New up a evaluation context if one was not provided. - if (context == null) - { - context = EvaluationContext.Empty; - } + // New up an evaluation context if one was not provided. + context ??= EvaluationContext.Empty; // merge api, client, and invocation context. var evaluationContext = Api.Instance.GetContext(); @@ -253,11 +250,11 @@ private async Task> EvaluateFlagAsync( var contextFromHooks = await this.TriggerBeforeHooksAsync(allHooks, hookContext, options, cancellationToken).ConfigureAwait(false); // short circuit evaluation entirely if provider is in a bad state - if (provider.Status == ProviderStatus.NotReady) + if (provider.Status == ProviderStatus.NotReady) { throw new ProviderNotReadyException("Provider has not yet completed initialization."); - } - else if (provider.Status == ProviderStatus.Fatal) + } + else if (provider.Status == ProviderStatus.Fatal) { throw new ProviderFatalException("Provider is in an irrecoverable error state."); } @@ -349,7 +346,7 @@ private async Task TriggerFinallyHooksAsync(IReadOnlyList hooks, HookCo } catch (Exception e) { - this._logger.LogError(e, "Error while executing Finally hook {HookName}", hook.GetType().Name); + this.FinallyHookError(hook.GetType().Name, e); } } } diff --git a/src/OpenFeature/ProviderRepository.cs b/src/OpenFeature/ProviderRepository.cs index 1656fdd3..760503b6 100644 --- a/src/OpenFeature/ProviderRepository.cs +++ b/src/OpenFeature/ProviderRepository.cs @@ -14,9 +14,9 @@ namespace OpenFeature /// /// This class manages the collection of providers, both default and named, contained by the API. /// - internal sealed class ProviderRepository : IAsyncDisposable + internal sealed partial class ProviderRepository : IAsyncDisposable { - private ILogger _logger; + private ILogger _logger = NullLogger.Instance; private FeatureProvider _defaultProvider = new NoOpFeatureProvider(); @@ -26,20 +26,15 @@ internal sealed class ProviderRepository : IAsyncDisposable /// The reader/writer locks is not disposed because the singleton instance should never be disposed. /// /// Mutations of the _defaultProvider or _featureProviders are done within this lock even though - /// _featureProvider is a concurrent collection. This is for a couple reasons, the first is that + /// _featureProvider is a concurrent collection. This is for a couple of reasons, the first is that /// a provider should only be shutdown if it is not in use, and it could be in use as either a named or /// default provider. /// - /// The second is that a concurrent collection doesn't provide any ordering so we could check a provider + /// The second is that a concurrent collection doesn't provide any ordering, so we could check a provider /// as it was being added or removed such as two concurrent calls to SetProvider replacing multiple instances - /// of that provider under different names.. + /// of that provider under different names. private readonly ReaderWriterLockSlim _providersLock = new ReaderWriterLockSlim(); - public ProviderRepository() - { - this._logger = NullLogger.Instance; - } - public async ValueTask DisposeAsync() { using (this._providersLock) @@ -201,7 +196,7 @@ private async Task ShutdownIfUnusedAsync( return; } - await SafeShutdownProviderAsync(targetProvider).ConfigureAwait(false); + await this.SafeShutdownProviderAsync(targetProvider).ConfigureAwait(false); } /// @@ -209,7 +204,7 @@ private async Task ShutdownIfUnusedAsync( /// Shut down the provider and capture any exceptions thrown. /// /// - /// The provider is set either to a name or default before the old provider it shutdown, so + /// The provider is set either to a name or default before the old provider it shut down, so /// it would not be meaningful to emit an error. /// /// @@ -226,7 +221,7 @@ private async Task SafeShutdownProviderAsync(FeatureProvider? targetProvider) } catch (Exception ex) { - this._logger.LogError(ex, $"Error shutting down provider: {targetProvider.GetMetadata().Name}"); + this.ErrorShuttingDownProvider(targetProvider.GetMetadata()?.Name, ex); } } @@ -287,8 +282,11 @@ public async Task ShutdownAsync(Action? afterError = foreach (var targetProvider in providers) { // We don't need to take any actions after shutdown. - await SafeShutdownProviderAsync(targetProvider).ConfigureAwait(false); + await this.SafeShutdownProviderAsync(targetProvider).ConfigureAwait(false); } } + + [LoggerMessage(EventId = 105, Level = LogLevel.Error, Message = "Error shutting down provider: {TargetProviderName}`")] + partial void ErrorShuttingDownProvider(string? targetProviderName, Exception exception); } } diff --git a/src/OpenFeature/Providers/Memory/Flag.cs b/src/OpenFeature/Providers/Memory/Flag.cs index 1a16bfe3..5cee86ea 100644 --- a/src/OpenFeature/Providers/Memory/Flag.cs +++ b/src/OpenFeature/Providers/Memory/Flag.cs @@ -9,19 +9,16 @@ namespace OpenFeature.Providers.Memory /// /// Flag representation for the in-memory provider. /// - public interface Flag - { - - } + public interface Flag; /// /// Flag representation for the in-memory provider. /// public sealed class Flag : Flag { - private Dictionary Variants; - private string DefaultVariant; - private Func? ContextEvaluator; + private readonly Dictionary _variants; + private readonly string _defaultVariant; + private readonly Func? _contextEvaluator; /// /// Flag representation for the in-memory provider. @@ -31,34 +28,34 @@ public sealed class Flag : Flag /// optional context-sensitive evaluation function public Flag(Dictionary variants, string defaultVariant, Func? contextEvaluator = null) { - this.Variants = variants; - this.DefaultVariant = defaultVariant; - this.ContextEvaluator = contextEvaluator; + this._variants = variants; + this._defaultVariant = defaultVariant; + this._contextEvaluator = contextEvaluator; } internal ResolutionDetails Evaluate(string flagKey, T _, EvaluationContext? evaluationContext) { - T? value = default; - if (this.ContextEvaluator == null) + T? value; + if (this._contextEvaluator == null) { - if (this.Variants.TryGetValue(this.DefaultVariant, out value)) + if (this._variants.TryGetValue(this._defaultVariant, out value)) { return new ResolutionDetails( flagKey, value, - variant: this.DefaultVariant, + variant: this._defaultVariant, reason: Reason.Static ); } else { - throw new GeneralException($"variant {this.DefaultVariant} not found"); + throw new GeneralException($"variant {this._defaultVariant} not found"); } } else { - var variant = this.ContextEvaluator.Invoke(evaluationContext ?? EvaluationContext.Empty); - if (!this.Variants.TryGetValue(variant, out value)) + var variant = this._contextEvaluator.Invoke(evaluationContext ?? EvaluationContext.Empty); + if (!this._variants.TryGetValue(variant, out value)) { throw new GeneralException($"variant {variant} not found"); } diff --git a/src/OpenFeature/Providers/Memory/InMemoryProvider.cs b/src/OpenFeature/Providers/Memory/InMemoryProvider.cs index e56acdb5..771e2210 100644 --- a/src/OpenFeature/Providers/Memory/InMemoryProvider.cs +++ b/src/OpenFeature/Providers/Memory/InMemoryProvider.cs @@ -61,7 +61,7 @@ public async Task UpdateFlags(IDictionary? flags = null) var @event = new ProviderEventPayload { Type = ProviderEventTypes.ProviderConfigurationChanged, - ProviderName = _metadata.Name, + ProviderName = this._metadata.Name, FlagsChanged = changed, // emit all Message = "flags changed", }; @@ -71,31 +71,31 @@ public async Task UpdateFlags(IDictionary? flags = null) /// public override Task> ResolveBooleanValueAsync(string flagKey, bool defaultValue, EvaluationContext? context = null, CancellationToken cancellationToken = default) { - return Task.FromResult(Resolve(flagKey, defaultValue, context)); + return Task.FromResult(this.Resolve(flagKey, defaultValue, context)); } /// public override Task> ResolveStringValueAsync(string flagKey, string defaultValue, EvaluationContext? context = null, CancellationToken cancellationToken = default) { - return Task.FromResult(Resolve(flagKey, defaultValue, context)); + return Task.FromResult(this.Resolve(flagKey, defaultValue, context)); } /// public override Task> ResolveIntegerValueAsync(string flagKey, int defaultValue, EvaluationContext? context = null, CancellationToken cancellationToken = default) { - return Task.FromResult(Resolve(flagKey, defaultValue, context)); + return Task.FromResult(this.Resolve(flagKey, defaultValue, context)); } /// public override Task> ResolveDoubleValueAsync(string flagKey, double defaultValue, EvaluationContext? context = null, CancellationToken cancellationToken = default) { - return Task.FromResult(Resolve(flagKey, defaultValue, context)); + return Task.FromResult(this.Resolve(flagKey, defaultValue, context)); } /// public override Task> ResolveStructureValueAsync(string flagKey, Value defaultValue, EvaluationContext? context = null, CancellationToken cancellationToken = default) { - return Task.FromResult(Resolve(flagKey, defaultValue, context)); + return Task.FromResult(this.Resolve(flagKey, defaultValue, context)); } private ResolutionDetails Resolve(string flagKey, T defaultValue, EvaluationContext? context) @@ -104,19 +104,15 @@ private ResolutionDetails Resolve(string flagKey, T defaultValue, Evaluati { throw new FlagNotFoundException($"flag {flagKey} not found"); } - else + + // This check returns False if a floating point flag is evaluated as an integer flag, and vice-versa. + // In a production provider, such behavior is probably not desirable; consider supporting conversion. + if (flag is Flag value) { - // This check returns False if a floating point flag is evaluated as an integer flag, and vice-versa. - // In a production provider, such behavior is probably not desirable; consider supporting conversion. - if (typeof(Flag).Equals(flag.GetType())) - { - return ((Flag)flag).Evaluate(flagKey, defaultValue, context); - } - else - { - throw new TypeMismatchException($"flag {flagKey} is not of type ${typeof(T)}"); - } + return value.Evaluate(flagKey, defaultValue, context); } + + throw new TypeMismatchException($"flag {flagKey} is not of type ${typeof(T)}"); } } } diff --git a/test/OpenFeature.Benchmarks/OpenFeatureClientBenchmarks.cs b/test/OpenFeature.Benchmarks/OpenFeatureClientBenchmarks.cs index 7f2e5b30..3796821e 100644 --- a/test/OpenFeature.Benchmarks/OpenFeatureClientBenchmarks.cs +++ b/test/OpenFeature.Benchmarks/OpenFeatureClientBenchmarks.cs @@ -30,77 +30,77 @@ public class OpenFeatureClientBenchmarks public OpenFeatureClientBenchmarks() { var fixture = new Fixture(); - _clientName = fixture.Create(); - _clientVersion = fixture.Create(); - _flagName = fixture.Create(); - _defaultBoolValue = fixture.Create(); - _defaultStringValue = fixture.Create(); - _defaultIntegerValue = fixture.Create(); - _defaultDoubleValue = fixture.Create(); - _defaultStructureValue = fixture.Create(); - _emptyFlagOptions = new FlagEvaluationOptions(ImmutableList.Empty, ImmutableDictionary.Empty); - - _client = Api.Instance.GetClient(_clientName, _clientVersion); + this._clientName = fixture.Create(); + this._clientVersion = fixture.Create(); + this._flagName = fixture.Create(); + this._defaultBoolValue = fixture.Create(); + this._defaultStringValue = fixture.Create(); + this._defaultIntegerValue = fixture.Create(); + this._defaultDoubleValue = fixture.Create(); + this._defaultStructureValue = fixture.Create(); + this._emptyFlagOptions = new FlagEvaluationOptions(ImmutableList.Empty, ImmutableDictionary.Empty); + + this._client = Api.Instance.GetClient(this._clientName, this._clientVersion); } [Benchmark] public async Task OpenFeatureClient_GetBooleanValue_WithoutEvaluationContext_WithoutFlagEvaluationOptions() => - await _client.GetBooleanValueAsync(_flagName, _defaultBoolValue); + await this._client.GetBooleanValueAsync(this._flagName, this._defaultBoolValue); [Benchmark] public async Task OpenFeatureClient_GetBooleanValue_WithEmptyEvaluationContext_WithoutFlagEvaluationOptions() => - await _client.GetBooleanValueAsync(_flagName, _defaultBoolValue, EvaluationContext.Empty); + await this._client.GetBooleanValueAsync(this._flagName, this._defaultBoolValue, EvaluationContext.Empty); [Benchmark] public async Task OpenFeatureClient_GetBooleanValue_WithEmptyEvaluationContext_WithEmptyFlagEvaluationOptions() => - await _client.GetBooleanValueAsync(_flagName, _defaultBoolValue, EvaluationContext.Empty, _emptyFlagOptions); + await this._client.GetBooleanValueAsync(this._flagName, this._defaultBoolValue, EvaluationContext.Empty, this._emptyFlagOptions); [Benchmark] public async Task OpenFeatureClient_GetStringValue_WithoutEvaluationContext_WithoutFlagEvaluationOptions() => - await _client.GetStringValueAsync(_flagName, _defaultStringValue); + await this._client.GetStringValueAsync(this._flagName, this._defaultStringValue); [Benchmark] public async Task OpenFeatureClient_GetStringValue_WithEmptyEvaluationContext_WithoutFlagEvaluationOptions() => - await _client.GetStringValueAsync(_flagName, _defaultStringValue, EvaluationContext.Empty); + await this._client.GetStringValueAsync(this._flagName, this._defaultStringValue, EvaluationContext.Empty); [Benchmark] public async Task OpenFeatureClient_GetStringValue_WithoutEvaluationContext_WithEmptyFlagEvaluationOptions() => - await _client.GetStringValueAsync(_flagName, _defaultStringValue, EvaluationContext.Empty, _emptyFlagOptions); + await this._client.GetStringValueAsync(this._flagName, this._defaultStringValue, EvaluationContext.Empty, this._emptyFlagOptions); [Benchmark] public async Task OpenFeatureClient_GetIntegerValue_WithoutEvaluationContext_WithoutFlagEvaluationOptions() => - await _client.GetIntegerValueAsync(_flagName, _defaultIntegerValue); + await this._client.GetIntegerValueAsync(this._flagName, this._defaultIntegerValue); [Benchmark] public async Task OpenFeatureClient_GetIntegerValue_WithEmptyEvaluationContext_WithoutFlagEvaluationOptions() => - await _client.GetIntegerValueAsync(_flagName, _defaultIntegerValue, EvaluationContext.Empty); + await this._client.GetIntegerValueAsync(this._flagName, this._defaultIntegerValue, EvaluationContext.Empty); [Benchmark] public async Task OpenFeatureClient_GetIntegerValue_WithEmptyEvaluationContext_WithEmptyFlagEvaluationOptions() => - await _client.GetIntegerValueAsync(_flagName, _defaultIntegerValue, EvaluationContext.Empty, _emptyFlagOptions); + await this._client.GetIntegerValueAsync(this._flagName, this._defaultIntegerValue, EvaluationContext.Empty, this._emptyFlagOptions); [Benchmark] public async Task OpenFeatureClient_GetDoubleValue_WithoutEvaluationContext_WithoutFlagEvaluationOptions() => - await _client.GetDoubleValueAsync(_flagName, _defaultDoubleValue); + await this._client.GetDoubleValueAsync(this._flagName, this._defaultDoubleValue); [Benchmark] public async Task OpenFeatureClient_GetDoubleValue_WithEmptyEvaluationContext_WithoutFlagEvaluationOptions() => - await _client.GetDoubleValueAsync(_flagName, _defaultDoubleValue, EvaluationContext.Empty); + await this._client.GetDoubleValueAsync(this._flagName, this._defaultDoubleValue, EvaluationContext.Empty); [Benchmark] public async Task OpenFeatureClient_GetDoubleValue_WithEmptyEvaluationContext_WithEmptyFlagEvaluationOptions() => - await _client.GetDoubleValueAsync(_flagName, _defaultDoubleValue, EvaluationContext.Empty, _emptyFlagOptions); + await this._client.GetDoubleValueAsync(this._flagName, this._defaultDoubleValue, EvaluationContext.Empty, this._emptyFlagOptions); [Benchmark] public async Task OpenFeatureClient_GetObjectValue_WithoutEvaluationContext_WithoutFlagEvaluationOptions() => - await _client.GetObjectValueAsync(_flagName, _defaultStructureValue); + await this._client.GetObjectValueAsync(this._flagName, this._defaultStructureValue); [Benchmark] public async Task OpenFeatureClient_GetObjectValue_WithEmptyEvaluationContext_WithoutFlagEvaluationOptions() => - await _client.GetObjectValueAsync(_flagName, _defaultStructureValue, EvaluationContext.Empty); + await this._client.GetObjectValueAsync(this._flagName, this._defaultStructureValue, EvaluationContext.Empty); [Benchmark] public async Task OpenFeatureClient_GetObjectValue_WithEmptyEvaluationContext_WithEmptyFlagEvaluationOptions() => - await _client.GetObjectValueAsync(_flagName, _defaultStructureValue, EvaluationContext.Empty, _emptyFlagOptions); + await this._client.GetObjectValueAsync(this._flagName, this._defaultStructureValue, EvaluationContext.Empty, this._emptyFlagOptions); } } diff --git a/test/OpenFeature.E2ETests/Steps/EvaluationStepDefinitions.cs b/test/OpenFeature.E2ETests/Steps/EvaluationStepDefinitions.cs index a50f3945..d0870ec3 100644 --- a/test/OpenFeature.E2ETests/Steps/EvaluationStepDefinitions.cs +++ b/test/OpenFeature.E2ETests/Steps/EvaluationStepDefinitions.cs @@ -41,7 +41,7 @@ public EvaluationStepDefinitions(ScenarioContext scenarioContext) [Given(@"a provider is registered")] public void GivenAProviderIsRegistered() { - var memProvider = new InMemoryProvider(e2eFlagConfig); + var memProvider = new InMemoryProvider(this.e2eFlagConfig); Api.Instance.SetProviderAsync(memProvider).Wait(); client = Api.Instance.GetClient("TestClient", "1.0.0"); } @@ -204,9 +204,9 @@ public void Whencontextcontainskeyswithvalues(string field1, string field2, stri [When(@"a flag with key ""(.*)"" is evaluated with default value ""(.*)""")] public void Givenaflagwithkeyisevaluatedwithdefaultvalue(string flagKey, string defaultValue) { - contextAwareFlagKey = flagKey; - contextAwareDefaultValue = defaultValue; - contextAwareValue = client?.GetStringValueAsync(flagKey, contextAwareDefaultValue, context)?.Result; + this.contextAwareFlagKey = flagKey; + this.contextAwareDefaultValue = defaultValue; + this.contextAwareValue = client?.GetStringValueAsync(flagKey, this.contextAwareDefaultValue, this.context)?.Result; } [Then(@"the resolved string response should be ""(.*)""")] @@ -218,7 +218,7 @@ public void Thentheresolvedstringresponseshouldbe(string expected) [Then(@"the resolved flag value is ""(.*)"" when the context is empty")] public void Giventheresolvedflagvalueiswhenthecontextisempty(string expected) { - string? emptyContextValue = client?.GetStringValueAsync(contextAwareFlagKey!, contextAwareDefaultValue!, EvaluationContext.Empty).Result; + string? emptyContextValue = client?.GetStringValueAsync(this.contextAwareFlagKey!, this.contextAwareDefaultValue!, EvaluationContext.Empty).Result; Assert.Equal(expected, emptyContextValue); } @@ -239,8 +239,8 @@ public void Thenthedefaultstringvalueshouldbereturned() [Then(@"the reason should indicate an error and the error code should indicate a missing flag with ""(.*)""")] public void Giventhereasonshouldindicateanerrorandtheerrorcodeshouldindicateamissingflagwith(string errorCode) { - Assert.Equal(Reason.Error.ToString(), notFoundDetails?.Reason); - Assert.Equal(errorCode, notFoundDetails?.ErrorType.GetDescription()); + Assert.Equal(Reason.Error.ToString(), this.notFoundDetails?.Reason); + Assert.Equal(errorCode, this.notFoundDetails?.ErrorType.GetDescription()); } [When(@"a string flag with key ""(.*)"" is evaluated as an integer, with details and a default value (.*)")] @@ -260,8 +260,8 @@ public void Thenthedefaultintegervalueshouldbereturned() [Then(@"the reason should indicate an error and the error code should indicate a type mismatch with ""(.*)""")] public void Giventhereasonshouldindicateanerrorandtheerrorcodeshouldindicateatypemismatchwith(string errorCode) { - Assert.Equal(Reason.Error.ToString(), typeErrorDetails?.Reason); - Assert.Equal(errorCode, typeErrorDetails?.ErrorType.GetDescription()); + Assert.Equal(Reason.Error.ToString(), this.typeErrorDetails?.Reason); + Assert.Equal(errorCode, this.typeErrorDetails?.ErrorType.GetDescription()); } private IDictionary e2eFlagConfig = new Dictionary(){ diff --git a/test/OpenFeature.Tests/OpenFeatureTests.cs b/test/OpenFeature.Tests/OpenFeatureTests.cs index 1df3c976..2f778ada 100644 --- a/test/OpenFeature.Tests/OpenFeatureTests.cs +++ b/test/OpenFeature.Tests/OpenFeatureTests.cs @@ -103,8 +103,8 @@ public async Task OpenFeature_Should_Not_Change_Named_Providers_When_Setting_Def var defaultClient = openFeature.GetProviderMetadata(); var namedClient = openFeature.GetProviderMetadata(TestProvider.DefaultName); - defaultClient.Name.Should().Be(NoOpProvider.NoOpProviderName); - namedClient.Name.Should().Be(TestProvider.DefaultName); + defaultClient?.Name.Should().Be(NoOpProvider.NoOpProviderName); + namedClient?.Name.Should().Be(TestProvider.DefaultName); } [Fact] @@ -117,7 +117,7 @@ public async Task OpenFeature_Should_Set_Default_Provide_When_No_Name_Provided() var defaultClient = openFeature.GetProviderMetadata(); - defaultClient.Name.Should().Be(TestProvider.DefaultName); + defaultClient?.Name.Should().Be(TestProvider.DefaultName); } [Fact] @@ -130,7 +130,7 @@ public async Task OpenFeature_Should_Assign_Provider_To_Existing_Client() await openFeature.SetProviderAsync(name, new TestProvider()); await openFeature.SetProviderAsync(name, new NoOpFeatureProvider()); - openFeature.GetProviderMetadata(name).Name.Should().Be(NoOpProvider.NoOpProviderName); + openFeature.GetProviderMetadata(name)?.Name.Should().Be(NoOpProvider.NoOpProviderName); } [Fact] @@ -187,7 +187,7 @@ public async Task OpenFeature_Should_Get_Metadata() var metadata = openFeature.GetProviderMetadata(); metadata.Should().NotBeNull(); - metadata.Name.Should().Be(NoOpProvider.NoOpProviderName); + metadata?.Name.Should().Be(NoOpProvider.NoOpProviderName); } [Theory] diff --git a/test/OpenFeature.Tests/TestImplementations.cs b/test/OpenFeature.Tests/TestImplementations.cs index a4fe51a4..7a1dff10 100644 --- a/test/OpenFeature.Tests/TestImplementations.cs +++ b/test/OpenFeature.Tests/TestImplementations.cs @@ -103,10 +103,10 @@ public override Task> ResolveStructureValueAsync(string public override async Task InitializeAsync(EvaluationContext context, CancellationToken cancellationToken = default) { - await Task.Delay(initDelay).ConfigureAwait(false); - if (initException != null) + await Task.Delay(this.initDelay).ConfigureAwait(false); + if (this.initException != null) { - throw initException; + throw this.initException; } }