From cc5fd4d588692063a1292d5998d3b19dcb0e273f Mon Sep 17 00:00:00 2001 From: Vladimir Petrusevici Date: Fri, 27 Oct 2023 11:38:28 +0300 Subject: [PATCH 1/5] Using factory for provider creation Signed-off-by: Vladimir Petrusevici --- OpenFeature.sln | 6 ++- src/OpenFeature/Api.cs | 48 +++++++++++++++---- src/OpenFeature/OpenFeatureClient.cs | 8 ++-- .../OpenFeatureClientTests.cs | 2 +- 4 files changed, 49 insertions(+), 15 deletions(-) diff --git a/OpenFeature.sln b/OpenFeature.sln index 5ed0e809..6ff20582 100644 --- a/OpenFeature.sln +++ b/OpenFeature.sln @@ -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 @@ -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 diff --git a/src/OpenFeature/Api.cs b/src/OpenFeature/Api.cs index e679bf75..8120ab82 100644 --- a/src/OpenFeature/Api.cs +++ b/src/OpenFeature/Api.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; @@ -15,9 +16,9 @@ namespace OpenFeature public sealed class Api { private EvaluationContext _evaluationContext = EvaluationContext.Empty; - private FeatureProvider _defaultProvider = new NoOpFeatureProvider(); - private readonly ConcurrentDictionary _featureProviders = - new ConcurrentDictionary(); + private Func _defaultProviderFunc = () => new NoOpFeatureProvider(); + private readonly ConcurrentDictionary> _featureProviders = + new ConcurrentDictionary> (); private readonly ConcurrentStack _hooks = new ConcurrentStack(); /// The reader/writer locks are not disposed because the singleton instance should never be disposed. @@ -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 { @@ -59,8 +60,37 @@ public void SetProvider(FeatureProvider featureProvider) /// Implementation of 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); + } + + /// + /// Sets the feature provider + /// + /// A function to create new + public void SetProvider(Func featureProviderFactory) + { + this._featureProviderLock.EnterWriteLock(); + try + { + this._defaultProviderFunc = featureProviderFactory ?? this._defaultProviderFunc; + } + finally + { + this._featureProviderLock.ExitWriteLock(); + } + } + + /// + /// Sets the feature provider to given clientName + /// + /// Name of client + /// A function to create new + public void SetProvider(string clientName, Func featureProviderFactory) + { + this._featureProviders.AddOrUpdate(clientName, featureProviderFactory, + (key, current) => featureProviderFactory); } /// @@ -79,7 +109,7 @@ public FeatureProvider GetProvider() this._featureProviderLock.EnterReadLock(); try { - return this._defaultProvider; + return this._defaultProviderFunc(); } finally { @@ -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(); } diff --git a/src/OpenFeature/OpenFeatureClient.cs b/src/OpenFeature/OpenFeatureClient.cs index 1cc26802..30916488 100644 --- a/src/OpenFeature/OpenFeatureClient.cs +++ b/src/OpenFeature/OpenFeatureClient.cs @@ -23,6 +23,7 @@ public sealed class FeatureClient : IFeatureClient private EvaluationContext _evaluationContext; private readonly object _evaluationContextLock = new object(); + private FeatureProvider _featureProvider; /// /// Get a provider and an associated typed flag resolution method. @@ -40,11 +41,10 @@ public sealed class FeatureClient : IFeatureClient ExtractProvider( Func>>> 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); } /// diff --git a/test/OpenFeature.Tests/OpenFeatureClientTests.cs b/test/OpenFeature.Tests/OpenFeatureClientTests.cs index 4995423e..af662c72 100644 --- a/test/OpenFeature.Tests/OpenFeatureClientTests.cs +++ b/test/OpenFeature.Tests/OpenFeatureClientTests.cs @@ -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); } From ad43085e31c232ceb5fae90f8e157371d7e017d7 Mon Sep 17 00:00:00 2001 From: Vladimir Petrusevici Date: Fri, 27 Oct 2023 12:12:44 +0300 Subject: [PATCH 2/5] fix whitespace Signed-off-by: Vladimir Petrusevici --- src/OpenFeature/Api.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenFeature/Api.cs b/src/OpenFeature/Api.cs index 8120ab82..d377a8c8 100644 --- a/src/OpenFeature/Api.cs +++ b/src/OpenFeature/Api.cs @@ -18,7 +18,7 @@ public sealed class Api private EvaluationContext _evaluationContext = EvaluationContext.Empty; private Func _defaultProviderFunc = () => new NoOpFeatureProvider(); private readonly ConcurrentDictionary> _featureProviders = - new ConcurrentDictionary> (); + new ConcurrentDictionary>(); private readonly ConcurrentStack _hooks = new ConcurrentStack(); /// The reader/writer locks are not disposed because the singleton instance should never be disposed. From b74091070ba29a86846d3d43891438e16c4ac0cf Mon Sep 17 00:00:00 2001 From: Vladimir Petrusevici Date: Fri, 27 Oct 2023 12:22:30 +0300 Subject: [PATCH 3/5] reuse set provider logic Signed-off-by: Vladimir Petrusevici --- src/OpenFeature/Api.cs | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/src/OpenFeature/Api.cs b/src/OpenFeature/Api.cs index d377a8c8..2abffd6c 100644 --- a/src/OpenFeature/Api.cs +++ b/src/OpenFeature/Api.cs @@ -41,17 +41,7 @@ private Api() { } /// /// Implementation of public void SetProvider(FeatureProvider featureProvider) - { - this._featureProviderLock.EnterWriteLock(); - try - { - this._defaultProviderFunc = featureProvider != null ? () => featureProvider : this._defaultProviderFunc; - } - finally - { - this._featureProviderLock.ExitWriteLock(); - } - } + => this.SetProvider(featureProvider == null ? (Func) null : () => featureProvider); /// /// Sets the feature provider to given clientName @@ -59,11 +49,8 @@ public void SetProvider(FeatureProvider featureProvider) /// Name of client /// Implementation of public void SetProvider(string clientName, FeatureProvider featureProvider) - { - FeatureProvider func() => featureProvider; - this._featureProviders.AddOrUpdate(clientName, func, - (key, current) => func); - } + => this.SetProvider(clientName, featureProvider == null ? (Func)null : () => featureProvider); + /// /// Sets the feature provider From 26751250ce747d09800c938bd3497c8a6aa828a2 Mon Sep 17 00:00:00 2001 From: Vladimir Petrusevici Date: Fri, 27 Oct 2023 12:57:44 +0300 Subject: [PATCH 4/5] dotnet format fix Signed-off-by: Vladimir Petrusevici --- src/OpenFeature/Api.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/OpenFeature/Api.cs b/src/OpenFeature/Api.cs index 2abffd6c..7f667cad 100644 --- a/src/OpenFeature/Api.cs +++ b/src/OpenFeature/Api.cs @@ -41,7 +41,7 @@ private Api() { } /// /// Implementation of public void SetProvider(FeatureProvider featureProvider) - => this.SetProvider(featureProvider == null ? (Func) null : () => featureProvider); + => this.SetProvider(featureProvider == null ? (Func)null : () => featureProvider); /// /// Sets the feature provider to given clientName @@ -50,7 +50,6 @@ public void SetProvider(FeatureProvider featureProvider) /// Implementation of public void SetProvider(string clientName, FeatureProvider featureProvider) => this.SetProvider(clientName, featureProvider == null ? (Func)null : () => featureProvider); - /// /// Sets the feature provider From 9aff8a70a62da013e883902aa1337953ea1eaad0 Mon Sep 17 00:00:00 2001 From: Vladimir Petrusevici Date: Fri, 27 Oct 2023 13:04:55 +0300 Subject: [PATCH 5/5] remove unnecessary check Signed-off-by: Vladimir Petrusevici --- src/OpenFeature/Api.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenFeature/Api.cs b/src/OpenFeature/Api.cs index 7f667cad..c4c62db2 100644 --- a/src/OpenFeature/Api.cs +++ b/src/OpenFeature/Api.cs @@ -49,7 +49,7 @@ public void SetProvider(FeatureProvider featureProvider) /// Name of client /// Implementation of public void SetProvider(string clientName, FeatureProvider featureProvider) - => this.SetProvider(clientName, featureProvider == null ? (Func)null : () => featureProvider); + => this.SetProvider(clientName, () => featureProvider); /// /// Sets the feature provider