Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public static IServiceCollection AddOpenFeature(this IServiceCollection services
Guard.ThrowIfNull(configure);

// Register core OpenFeature services as singletons.
services.TryAddSingleton(Api.Instance);
services.TryAddSingleton(new Api());
services.TryAddSingleton<IFeatureLifecycleManager, FeatureLifecycleManager>();

var builder = new OpenFeatureBuilder(services);
Expand Down
4 changes: 2 additions & 2 deletions src/OpenFeature/Api.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public sealed class Api : IEventBus
// not to mark type as beforeFieldInit
// IE Lazy way of ensuring this is thread safe without using locks
static Api() { }
private Api() { }
internal Api() { }

/// <summary>
/// Sets the default feature provider. In order to wait for the provider to be set, and initialization to complete,
Expand Down Expand Up @@ -121,7 +121,7 @@ public FeatureProvider GetProvider(string domain)
/// <returns><see cref="FeatureClient"/></returns>
public FeatureClient GetClient(string? name = null, string? version = null, ILogger? logger = null,
EvaluationContext? context = null) =>
new FeatureClient(() => this._repository.GetProvider(name), name, version, logger, context);
new FeatureClient(() => this._repository.GetProvider(name), name, version, this, logger, context);

/// <summary>
/// Appends list of hooks to global hooks list
Expand Down
3 changes: 2 additions & 1 deletion src/OpenFeature/OpenFeature.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
<InternalsVisibleTo Include="OpenFeature.Benchmarks" />
<InternalsVisibleTo Include="OpenFeature.Tests" />
<InternalsVisibleTo Include="OpenFeature.E2ETests" />
<InternalsVisibleTo Include="OpenFeature.DependencyInjection" />
<None Include="../../README.md" Pack="true" PackagePath="/" />
</ItemGroup>

</Project>
</Project>
19 changes: 11 additions & 8 deletions src/OpenFeature/OpenFeatureClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public sealed partial class FeatureClient : IFeatureClient
private readonly ConcurrentStack<Hook> _hooks = new ConcurrentStack<Hook>();
private readonly ILogger _logger;
private readonly Func<FeatureProvider> _providerAccessor;
private readonly Api _api;
private EvaluationContext _evaluationContext;

private readonly object _evaluationContextLock = new object();
Expand All @@ -40,7 +41,7 @@ public sealed partial class FeatureClient : IFeatureClient
{
// Alias the provider reference so getting the method and returning the provider are
// guaranteed to be the same object.
var provider = Api.Instance.GetProvider(this._metadata.Name!);
var provider = this._api.GetProvider(this._metadata.Name!);

return (method(provider), provider);
}
Expand Down Expand Up @@ -72,15 +73,17 @@ public void SetContext(EvaluationContext? context)
/// <param name="providerAccessor">Function to retrieve current provider</param>
/// <param name="name">Name of client <see cref="ClientMetadata"/></param>
/// <param name="version">Version of client <see cref="ClientMetadata"/></param>
/// <param name="api">The API instance for accessing global state and providers</param>
/// <param name="logger">Logger used by client</param>
/// <param name="context">Context given to this client</param>
/// <exception cref="ArgumentNullException">Throws if any of the required parameters are null</exception>
internal FeatureClient(Func<FeatureProvider> providerAccessor, string? name, string? version, ILogger? logger = null, EvaluationContext? context = null)
internal FeatureClient(Func<FeatureProvider> providerAccessor, string? name, string? version, Api? api = null, ILogger? logger = null, EvaluationContext? context = null)
{
this._metadata = new ClientMetadata(name, version);
this._logger = logger ?? NullLogger<FeatureClient>.Instance;
this._evaluationContext = context ?? EvaluationContext.Empty;
this._providerAccessor = providerAccessor;
this._api = api ?? Api.Instance;
}

/// <inheritdoc />
Expand All @@ -99,13 +102,13 @@ internal FeatureClient(Func<FeatureProvider> providerAccessor, string? name, str
/// <inheritdoc />
public void AddHandler(ProviderEventTypes eventType, EventHandlerDelegate handler)
{
Api.Instance.AddClientHandler(this._metadata.Name!, eventType, handler);
this._api.AddClientHandler(this._metadata.Name!, eventType, handler);
}

/// <inheritdoc />
public void RemoveHandler(ProviderEventTypes type, EventHandlerDelegate handler)
{
Api.Instance.RemoveClientHandler(this._metadata.Name!, type, handler);
this._api.RemoveClientHandler(this._metadata.Name!, type, handler);
}

/// <inheritdoc />
Expand Down Expand Up @@ -213,13 +216,13 @@ private async Task<FlagEvaluationDetails<T>> EvaluateFlagAsync<T>(

// merge api, client, transaction and invocation context
var evaluationContextBuilder = EvaluationContext.Builder();
evaluationContextBuilder.Merge(Api.Instance.GetContext()); // API context
evaluationContextBuilder.Merge(this._api.GetContext()); // API context
evaluationContextBuilder.Merge(this.GetContext()); // Client context
evaluationContextBuilder.Merge(Api.Instance.GetTransactionContext()); // Transaction context
evaluationContextBuilder.Merge(this._api.GetTransactionContext()); // Transaction context
evaluationContextBuilder.Merge(context); // Invocation context

var allHooks = ImmutableList.CreateBuilder<Hook>()
.Concat(Api.Instance.GetHooks())
.Concat(this._api.GetHooks())
.Concat(this.GetHooks())
.Concat(options?.Hooks ?? Enumerable.Empty<Hook>())
.Concat(provider.GetProviderHooks())
Expand Down Expand Up @@ -310,7 +313,7 @@ public void Track(string trackingEventName, EvaluationContext? evaluationContext
throw new ArgumentException("Tracking event cannot be null or empty.", nameof(trackingEventName));
}

var globalContext = Api.Instance.GetContext();
var globalContext = this._api.GetContext();
var clientContext = this.GetContext();

var evaluationContextBuilder = EvaluationContext.Builder()
Expand Down