Skip to content

Commit

Permalink
Using factory for provider creation
Browse files Browse the repository at this point in the history
  • Loading branch information
Vladimir Petrusevici authored and Vladimir Petrusevici committed Oct 27, 2023
1 parent 2cbdba8 commit de9d787
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 15 deletions.
6 changes: 5 additions & 1 deletion OpenFeature.sln
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenFeature.Tests", "test\O
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenFeature.Benchmarks", "test\OpenFeature.Benchmarks\OpenFeature.Benchmarks.csproj", "{90E7EAD3-251E-4490-AF78-E758E33518E5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenFeature.E2ETests", "test\OpenFeature.E2ETests\OpenFeature.E2ETests.csproj", "{90E7EAD3-251E-4490-AF78-E758E33518E5}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenFeature.E2ETests", "test\OpenFeature.E2ETests\OpenFeature.E2ETests.csproj", "{CB82B298-040A-4E8B-B74E-1636DC5C2457}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -56,6 +56,10 @@ Global
{90E7EAD3-251E-4490-AF78-E758E33518E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{90E7EAD3-251E-4490-AF78-E758E33518E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{90E7EAD3-251E-4490-AF78-E758E33518E5}.Release|Any CPU.Build.0 = Release|Any CPU
{CB82B298-040A-4E8B-B74E-1636DC5C2457}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CB82B298-040A-4E8B-B74E-1636DC5C2457}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CB82B298-040A-4E8B-B74E-1636DC5C2457}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CB82B298-040A-4E8B-B74E-1636DC5C2457}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
48 changes: 39 additions & 9 deletions src/OpenFeature/Api.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
Expand All @@ -15,9 +16,9 @@ namespace OpenFeature
public sealed class Api
{
private EvaluationContext _evaluationContext = EvaluationContext.Empty;
private FeatureProvider _defaultProvider = new NoOpFeatureProvider();
private readonly ConcurrentDictionary<string, FeatureProvider> _featureProviders =
new ConcurrentDictionary<string, FeatureProvider>();
private Func<FeatureProvider> _defaultProviderFunc = () => new NoOpFeatureProvider();
private readonly ConcurrentDictionary<string, Func<FeatureProvider>> _featureProviders =
new ConcurrentDictionary<string, Func<FeatureProvider>> ();
private readonly ConcurrentStack<Hook> _hooks = new ConcurrentStack<Hook>();

/// The reader/writer locks are not disposed because the singleton instance should never be disposed.
Expand All @@ -44,7 +45,7 @@ public void SetProvider(FeatureProvider featureProvider)
this._featureProviderLock.EnterWriteLock();
try
{
this._defaultProvider = featureProvider ?? this._defaultProvider;
this._defaultProviderFunc = featureProvider != null ? () => featureProvider : this._defaultProviderFunc;
}
finally
{
Expand All @@ -59,8 +60,37 @@ public void SetProvider(FeatureProvider featureProvider)
/// <param name="featureProvider">Implementation of <see cref="FeatureProvider"/></param>
public void SetProvider(string clientName, FeatureProvider featureProvider)
{
this._featureProviders.AddOrUpdate(clientName, featureProvider,
(key, current) => featureProvider);
FeatureProvider func() => featureProvider;
this._featureProviders.AddOrUpdate(clientName, func,
(key, current) => func);
}

/// <summary>
/// Sets the feature provider
/// </summary>
/// <param name="featureProviderFactory">A function to create new <see cref="FeatureProvider"/></param>
public void SetProvider(Func<FeatureProvider> featureProviderFactory)
{
this._featureProviderLock.EnterWriteLock();
try
{
this._defaultProviderFunc = featureProviderFactory ?? this._defaultProviderFunc;
}
finally
{
this._featureProviderLock.ExitWriteLock();
}
}

/// <summary>
/// Sets the feature provider to given clientName
/// </summary>
/// <param name="clientName">Name of client</param>
/// <param name="featureProviderFactory">A function to create new <see cref="FeatureProvider"/></param>
public void SetProvider(string clientName, Func<FeatureProvider> featureProviderFactory)
{
this._featureProviders.AddOrUpdate(clientName, featureProviderFactory,
(key, current) => featureProviderFactory);
}

/// <summary>
Expand All @@ -79,7 +109,7 @@ public FeatureProvider GetProvider()
this._featureProviderLock.EnterReadLock();
try
{
return this._defaultProvider;
return this._defaultProviderFunc();
}
finally
{
Expand All @@ -100,8 +130,8 @@ public FeatureProvider GetProvider(string clientName)
return this.GetProvider();
}

return this._featureProviders.TryGetValue(clientName, out var featureProvider)
? featureProvider
return this._featureProviders.TryGetValue(clientName, out var featureProviderFunc)
? featureProviderFunc()
: this.GetProvider();
}

Expand Down
8 changes: 4 additions & 4 deletions src/OpenFeature/OpenFeatureClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public sealed class FeatureClient : IFeatureClient
private EvaluationContext _evaluationContext;

private readonly object _evaluationContextLock = new object();
private FeatureProvider _featureProvider;

/// <summary>
/// Get a provider and an associated typed flag resolution method.
Expand All @@ -40,11 +41,10 @@ public sealed class FeatureClient : IFeatureClient
ExtractProvider<T>(
Func<FeatureProvider, Func<string, T, EvaluationContext, Task<ResolutionDetails<T>>>> method)
{
// 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);
// Will use factory to get provider if it's not created for this client.
this._featureProvider = this._featureProvider ?? Api.Instance.GetProvider(this._metadata.Name);

return (method(provider), provider);
return (method(this._featureProvider), this._featureProvider);
}

/// <inheritdoc />
Expand Down
2 changes: 1 addition & 1 deletion test/OpenFeature.Tests/OpenFeatureClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ public async Task When_Exception_Occurs_During_Evaluation_Should_Return_Error()
[Fact]
public async Task Should_Use_No_Op_When_Provider_Is_Null()
{
Api.Instance.SetProvider(null);
Api.Instance.SetProvider((FeatureProvider)null);
var client = new FeatureClient("test", "test");
(await client.GetIntegerValue("some-key", 12)).Should().Be(12);
}
Expand Down

0 comments on commit de9d787

Please sign in to comment.