Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
11 changes: 7 additions & 4 deletions lib/open_feature/sdk/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,7 @@ def run_immediate_handler(event_type, handler, client)
provider_state = @provider_state_registry.get_state(provider)

if event_type == status_to_event[provider_state]
provider_name = extract_provider_name(provider)
event_details = {provider_name: provider_name}
event_details = build_event_details(provider)

begin
handler.call(event_details)
Expand All @@ -253,8 +252,7 @@ def run_immediate_handler(event_type, handler, client)
provider_state = @provider_state_registry.get_state(client_provider)

if event_type == status_to_event[provider_state]
provider_name = extract_provider_name(client_provider)
event_details = {provider_name: provider_name}
event_details = build_event_details(client_provider)

begin
handler.call(event_details)
Expand All @@ -264,6 +262,11 @@ def run_immediate_handler(event_type, handler, client)
end
end
end

def build_event_details(provider)
stored_details = @provider_state_registry.get_details(provider)
{provider_name: extract_provider_name(provider)}.merge(stored_details)
end
end
end
end
16 changes: 13 additions & 3 deletions lib/open_feature/sdk/provider_state_registry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def set_initial_state(provider, state = ProviderState::NOT_READY)
return unless provider

@mutex.synchronize do
@states[provider.object_id] = state
@states[provider.object_id] = {state: state, details: {}}
end
end

Expand All @@ -29,7 +29,7 @@ def update_state_from_event(provider, event_type, event_details = nil)
# Only update state if the event should cause a state change
if new_state
@mutex.synchronize do
@states[provider.object_id] = new_state
@states[provider.object_id] = {state: new_state, details: event_details || {}}
end
new_state
else
Expand All @@ -42,7 +42,17 @@ def get_state(provider)
return ProviderState::NOT_READY unless provider

@mutex.synchronize do
@states[provider.object_id] || ProviderState::NOT_READY
entry = @states[provider.object_id]
entry ? entry[:state] : ProviderState::NOT_READY
end
end

def get_details(provider)
return {} unless provider

@mutex.synchronize do
entry = @states[provider.object_id]
entry ? entry[:details] : {}
end
end

Expand Down
60 changes: 60 additions & 0 deletions spec/specification/events_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,66 @@ def init(_evaluation_context)
end
end

context "Requirement 5.1.4" do
specify "PROVIDER_ERROR events SHOULD populate the error message field" do
event_details_received = nil
handler = ->(event_details) { event_details_received = event_details }

OpenFeature::SDK.add_handler(OpenFeature::SDK::ProviderEvent::PROVIDER_ERROR, handler)

provider = OpenFeature::SDK::Provider::InMemoryProvider.new
allow(provider).to receive(:init).and_raise("Custom init failure")

OpenFeature::SDK.set_provider(provider)
sleep(0.001) until event_details_received

expect(event_details_received[:message]).to eq("Custom init failure")

OpenFeature::SDK.remove_handler(OpenFeature::SDK::ProviderEvent::PROVIDER_ERROR, handler)
end
end

context "Requirement 5.1.5" do
specify "PROVIDER_ERROR events SHOULD populate the error code field" do
event_details_received = nil
handler = ->(event_details) { event_details_received = event_details }

OpenFeature::SDK.add_handler(OpenFeature::SDK::ProviderEvent::PROVIDER_ERROR, handler)

provider = OpenFeature::SDK::Provider::InMemoryProvider.new
allow(provider).to receive(:init).and_raise("Init failed")

OpenFeature::SDK.set_provider(provider)
sleep(0.001) until event_details_received

expect(event_details_received[:error_code]).to eq(OpenFeature::SDK::Provider::ErrorCode::GENERAL)

OpenFeature::SDK.remove_handler(OpenFeature::SDK::ProviderEvent::PROVIDER_ERROR, handler)
end

specify "error details are available in immediate handlers attached after the error" do
provider = OpenFeature::SDK::Provider::InMemoryProvider.new
allow(provider).to receive(:init).and_raise("Delayed failure")

begin
OpenFeature::SDK.set_provider_and_wait(provider)
rescue OpenFeature::SDK::ProviderInitializationError
# Expected — provider init fails, putting provider in ERROR state
end

event_details_received = nil
handler = ->(event_details) { event_details_received = event_details }

OpenFeature::SDK.add_handler(OpenFeature::SDK::ProviderEvent::PROVIDER_ERROR, handler)

expect(event_details_received).not_to be_nil
expect(event_details_received[:error_code]).to eq(OpenFeature::SDK::Provider::ErrorCode::GENERAL)
expect(event_details_received[:message]).to eq("Delayed failure")

OpenFeature::SDK.remove_handler(OpenFeature::SDK::ProviderEvent::PROVIDER_ERROR, handler)
end
end

context "Requirement 5.2.1" do
specify "The client MUST provide a function for associating handler functions with provider event types" do
client = OpenFeature::SDK.build_client(domain: "test-domain")
Expand Down
Loading