Skip to content

Commit 8cb80f5

Browse files
committed
feat: run handlers immediately if provider already in associated state
Signed-off-by: Federico Bond <[email protected]>
1 parent 44b6d06 commit 8cb80f5

File tree

4 files changed

+59
-13
lines changed

4 files changed

+59
-13
lines changed

openfeature/event.py

+10
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ def add_client_handler(
7575
handlers = self._client_handlers[client][event]
7676
handlers.append(handler)
7777

78+
self._run_immediate_handler(client, event, handler)
79+
7880
def remove_client_handler(
7981
self, client: OpenFeatureClient, event: ProviderEvent, handler: EventHandler
8082
) -> None:
@@ -84,6 +86,9 @@ def remove_client_handler(
8486
def add_global_handler(self, event: ProviderEvent, handler: EventHandler) -> None:
8587
self._global_handlers[event].append(handler)
8688

89+
from openfeature.api import get_client
90+
self._run_immediate_handler(get_client(), event, handler)
91+
8792
def remove_global_handler(
8893
self, event: ProviderEvent, handler: EventHandler
8994
) -> None:
@@ -104,3 +109,8 @@ def run_handlers_for_provider(
104109
for client in self._client_handlers:
105110
if client.provider == provider:
106111
self.run_client_handlers(client, event, details)
112+
113+
def _run_immediate_handler(self, client: OpenFeatureClient, event: ProviderEvent, handler: EventHandler) -> None:
114+
if event == ProviderEvent.PROVIDER_READY:
115+
# providers are assumed ready because provider status is not yet implemented
116+
handler(EventDetails(provider_name=client.provider.get_metadata().name))

openfeature/exception.py

-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ class ErrorCode(Enum):
1010
TYPE_MISMATCH = "TYPE_MISMATCH"
1111
TARGETING_KEY_MISSING = "TARGETING_KEY_MISSING"
1212
INVALID_CONTEXT = "INVALID_CONTEXT"
13-
PROVIDER_FATAL = "PROVIDER_FATAL"
1413
GENERAL = "GENERAL"
1514

1615

tests/test_api.py

+39-6
Original file line numberDiff line numberDiff line change
@@ -249,12 +249,12 @@ def test_provider_events():
249249
provider.get_metadata().name, provider_details
250250
)
251251

252-
provider.emit_provider_ready(provider_details)
253252
provider.emit_provider_configuration_changed(provider_details)
254253
provider.emit_provider_error(provider_details)
255254
provider.emit_provider_stale(provider_details)
256255

257-
spy.provider_ready.assert_called_once_with(details)
256+
# NOTE: provider_ready is called immediately after adding the handler
257+
spy.provider_ready.assert_called_once()
258258
spy.provider_configuration_changed.assert_called_once_with(details)
259259
spy.provider_error.assert_called_once_with(details)
260260
spy.provider_stale.assert_called_once_with(details)
@@ -266,10 +266,43 @@ def test_add_remove_event_handler():
266266

267267
spy = MagicMock()
268268

269-
add_handler(ProviderEvent.PROVIDER_READY, spy.provider_ready)
270-
remove_handler(ProviderEvent.PROVIDER_READY, spy.provider_ready)
269+
add_handler(
270+
ProviderEvent.PROVIDER_CONFIGURATION_CHANGED, spy.provider_configuration_changed
271+
)
272+
remove_handler(
273+
ProviderEvent.PROVIDER_CONFIGURATION_CHANGED, spy.provider_configuration_changed
274+
)
271275

272276
provider_details = ProviderEventDetails(message="message")
273-
provider.emit_provider_ready(provider_details)
277+
provider.emit_provider_configuration_changed(provider_details)
278+
279+
spy.provider_configuration_changed.assert_not_called()
280+
281+
282+
# Requirement 5.3.3
283+
def test_handlers_attached_to_provider_already_in_associated_state_should_run_immediately():
284+
# Given
285+
provider = NoOpProvider()
286+
set_provider(provider)
287+
spy = MagicMock()
288+
289+
# When
290+
add_handler(ProviderEvent.PROVIDER_READY, spy.provider_ready)
291+
292+
# Then
293+
spy.provider_ready.assert_called_once()
274294

275-
spy.provider_ready.assert_not_called()
295+
296+
def test_provider_ready_handlers_run_if_provider_initialize_function_terminates_normally():
297+
# Given
298+
provider = NoOpProvider()
299+
set_provider(provider)
300+
301+
spy = MagicMock()
302+
add_handler(ProviderEvent.PROVIDER_READY, spy.provider_ready)
303+
304+
# When
305+
provider.initialize(get_evaluation_context())
306+
307+
# Then
308+
spy.provider_ready.assert_called_once()

tests/test_client.py

+10-6
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,6 @@ def test_provider_events():
252252
)
253253

254254
def emit_all_events(provider):
255-
provider.emit_provider_ready(provider_details)
256255
provider.emit_provider_configuration_changed(provider_details)
257256
provider.emit_provider_error(provider_details)
258257
provider.emit_provider_stale(provider_details)
@@ -270,7 +269,8 @@ def emit_all_events(provider):
270269
emit_all_events(provider)
271270
emit_all_events(other_provider)
272271

273-
spy.provider_ready.assert_called_once_with(details)
272+
# NOTE: provider_ready is called immediately after adding the handler
273+
spy.provider_ready.assert_called_once()
274274
spy.provider_configuration_changed.assert_called_once_with(details)
275275
spy.provider_error.assert_called_once_with(details)
276276
spy.provider_stale.assert_called_once_with(details)
@@ -283,10 +283,14 @@ def test_add_remove_event_handler():
283283
spy = MagicMock()
284284

285285
client = get_client()
286-
client.add_handler(ProviderEvent.PROVIDER_READY, spy.provider_ready)
287-
client.remove_handler(ProviderEvent.PROVIDER_READY, spy.provider_ready)
286+
client.add_handler(
287+
ProviderEvent.PROVIDER_CONFIGURATION_CHANGED, spy.provider_configuration_changed
288+
)
289+
client.remove_handler(
290+
ProviderEvent.PROVIDER_CONFIGURATION_CHANGED, spy.provider_configuration_changed
291+
)
288292

289293
provider_details = ProviderEventDetails(message="message")
290-
provider.emit_provider_ready(provider_details)
294+
provider.emit_provider_configuration_changed(provider_details)
291295

292-
spy.provider_ready.assert_not_called()
296+
spy.provider_configuration_changed.assert_not_called()

0 commit comments

Comments
 (0)