diff --git a/README.md b/README.md index 421997a..218cf6b 100644 --- a/README.md +++ b/README.md @@ -42,13 +42,15 @@ require 'json' # For JSON.dump # API Initialization and configuration OpenFeature::SDK.configure do |config| - # your provider of choice - config.provider = OpenFeature::SDK::Provider::InMemoryProvider.new( - { - "flag1" => true, - "flag2" => 1 - } - ) + # your provider of choice, which will be used as the default provider + config.set_provider(OpenFeature::SDK::Provider::InMemoryProvider.new( + { + "flag1" => true, + "flag2" => 1 + } + )) + # alternatively, you can bind multiple providers to different domains + config.set_provider(OpenFeature::SDK::Provider::NoOpProvider.new, domain: "legacy_flags") end # Create a client diff --git a/lib/open_feature/sdk/api.rb b/lib/open_feature/sdk/api.rb index e58198d..7ad7e2f 100644 --- a/lib/open_feature/sdk/api.rb +++ b/lib/open_feature/sdk/api.rb @@ -19,7 +19,7 @@ module SDK # To use the SDK, you can optionally configure a Provider, with Hook # # OpenFeature::SDK::API.instance.configure do |config| - # config.provider = NoOpProvider.new + # config.set_provider NoOpProvider.new # end # # If no provider is specified, the NoOpProvider is set as the default Provider. @@ -30,7 +30,7 @@ class API include Singleton # Satisfies Flag Evaluation API Requirement 1.1.1 extend Forwardable - def_delegators :configuration, :provider, :provider=, :hooks, :context + def_delegators :configuration, :provider, :set_provider, :hooks, :context def configuration @configuration ||= Configuration.new diff --git a/lib/open_feature/sdk/configuration.rb b/lib/open_feature/sdk/configuration.rb index ceed507..19e2c0a 100644 --- a/lib/open_feature/sdk/configuration.rb +++ b/lib/open_feature/sdk/configuration.rb @@ -14,24 +14,28 @@ class Configuration extend Forwardable attr_accessor :context, :hooks - attr_reader :provider - def_delegator :@provider, :metadata + def_delegator :provider, :metadata def initialize @hooks = [] + @providers = {} + end + + def provider(domain: nil) + @providers[domain] end # When switching providers, there are a few lifecycle methods that need to be taken care of. # 1. If a provider is already set, we need to call `shutdown` on it. # 2. On the new provider, call `init`. # 3. Finally, set the internal provider to the new provider - def provider=(provider) - @provider.shutdown if @provider.respond_to?(:shutdown) + def set_provider(provider, domain: nil) + @providers[domain].shutdown if @providers[domain].respond_to?(:shutdown) provider.init if provider.respond_to?(:init) - @provider = provider + @providers[domain] = provider end end end diff --git a/lib/open_feature/sdk/provider/no_op_provider.rb b/lib/open_feature/sdk/provider/no_op_provider.rb index c80118b..3939272 100644 --- a/lib/open_feature/sdk/provider/no_op_provider.rb +++ b/lib/open_feature/sdk/provider/no_op_provider.rb @@ -11,7 +11,7 @@ module Provider # To use NoOpProvider, it can be set during the configuration of the SDK # # OpenFeature::SDK.configure do |config| - # config.provider = NoOpProvider.new + # config.set_provider NoOpProvider.new # end # # Within the NoOpProvider, the following methods exist diff --git a/spec/open_feature/sdk/api_spec.rb b/spec/open_feature/sdk/api_spec.rb index f0163c0..1b3be34 100644 --- a/spec/open_feature/sdk/api_spec.rb +++ b/spec/open_feature/sdk/api_spec.rb @@ -10,7 +10,7 @@ context "with Requirement 1.1.3" do before do api.configure do |config| - config.provider = OpenFeature::SDK::Provider::NoOpProvider.new + config.set_provider(OpenFeature::SDK::Provider::NoOpProvider.new) config.hooks << hook1 config.hooks << hook2 end @@ -28,7 +28,7 @@ context "with Requirement 1.1.4" do before do api.configure do |config| - config.provider = OpenFeature::SDK::Provider::NoOpProvider.new + config.set_provider(OpenFeature::SDK::Provider::NoOpProvider.new) end end @@ -52,7 +52,7 @@ context "with Requirement 1.1.5" do before do api.configure do |config| - config.provider = OpenFeature::SDK::Provider::NoOpProvider.new + config.set_provider(OpenFeature::SDK::Provider::NoOpProvider.new) end api.build_client(name: "requirement-1.1.5") diff --git a/spec/open_feature/sdk/configuration_spec.rb b/spec/open_feature/sdk/configuration_spec.rb index 8d98ddc..1d0eb4f 100644 --- a/spec/open_feature/sdk/configuration_spec.rb +++ b/spec/open_feature/sdk/configuration_spec.rb @@ -5,27 +5,38 @@ RSpec.describe OpenFeature::SDK::Configuration do subject(:configuration) { described_class.new } - describe "#provider=" do + describe "#set_provider" do context "when provider has an init method" do let(:provider) { OpenFeature::SDK::Provider::InMemoryProvider.new } it "inits and sets the provider" do expect(provider).to receive(:init) - configuration.provider = provider + configuration.set_provider(provider) expect(configuration.provider).to be(provider) end end context "when provider does not have an init method" do - it "sets the provider" do + it "sets the default provider" do provider = OpenFeature::SDK::Provider::NoOpProvider.new - configuration.provider = provider + configuration.set_provider(provider) expect(configuration.provider).to be(provider) end end + + context "when name is given" do + it "binds the provider to that name" do + provider = OpenFeature::SDK::Provider::InMemoryProvider.new + expect(provider).to receive(:init) + + configuration.set_provider(provider, domain: "testing") + + expect(configuration.provider(domain: "testing")).to be(provider) + end + end end end diff --git a/spec/specification/flag_evaluation_api_spec.rb b/spec/specification/flag_evaluation_api_spec.rb index 8f573fc..d76fa4e 100644 --- a/spec/specification/flag_evaluation_api_spec.rb +++ b/spec/specification/flag_evaluation_api_spec.rb @@ -14,7 +14,7 @@ specify "the API must define a provider mutator" do provider = OpenFeature::SDK::Provider::NoOpProvider.new - OpenFeature::SDK.provider = provider + OpenFeature::SDK.set_provider(provider) expect(OpenFeature::SDK.provider).to be(provider) end @@ -25,7 +25,7 @@ provider = OpenFeature::SDK::Provider::InMemoryProvider.new expect(provider).to receive(:init) - OpenFeature::SDK.provider = provider + OpenFeature::SDK.set_provider(provider) end end @@ -37,8 +37,32 @@ expect(previous_provider).to receive(:shutdown) expect(new_provider).not_to receive(:shutdown) - OpenFeature::SDK.provider = previous_provider - OpenFeature::SDK.provider = new_provider + OpenFeature::SDK.set_provider(previous_provider) + OpenFeature::SDK.set_provider(new_provider) + end + end + + context "Requirement 1.1.3" do + specify "the API must provide a function to bind a given provider to one or more client names" do + first_provider = OpenFeature::SDK::Provider::InMemoryProvider.new + second_provider = OpenFeature::SDK::Provider::InMemoryProvider.new + + OpenFeature::SDK.set_provider(first_provider, domain: "first") + OpenFeature::SDK.set_provider(second_provider, domain: "second") + + expect(OpenFeature::SDK.provider(domain: "first")).to be(first_provider) + expect(OpenFeature::SDK.provider(domain: "second")).to be(second_provider) + end + + specify "if client name is already bound, it is overwritten" do + previous_provider = OpenFeature::SDK::Provider::InMemoryProvider.new + new_provider = OpenFeature::SDK::Provider::InMemoryProvider.new + + OpenFeature::SDK.set_provider(previous_provider, domain: "testing") + expect(OpenFeature::SDK.provider(domain: "testing")).to be(previous_provider) + + OpenFeature::SDK.set_provider(new_provider, domain: "testing") + expect(OpenFeature::SDK.provider(domain: "testing")).to be(new_provider) end end end