Skip to content

Commit 1af041a

Browse files
committed
feat(provider): proper Unleash lifecycle event handling
Signed-off-by: Kiki L Hakiem <[email protected]>
1 parent 84d0272 commit 1af041a

File tree

4 files changed

+36
-35
lines changed

4 files changed

+36
-35
lines changed

providers/openfeature-provider-unleash/src/openfeature/contrib/provider/unleash/__init__.py

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from UnleashClient import UnleashClient
55
from UnleashClient.cache import BaseCache
6-
from UnleashClient.events import BaseEvent, UnleashReadyEvent
6+
from UnleashClient.events import BaseEvent, UnleashEventType
77

88
from openfeature.evaluation_context import EvaluationContext
99
from openfeature.event import ProviderEvent
@@ -44,8 +44,16 @@ def __init__(
4444
self.api_token = api_token
4545
self.environment = environment
4646
self.cache = cache
47-
self.client: Optional[UnleashClient] = None
4847
self._status = ProviderStatus.NOT_READY
48+
49+
self.client: UnleashClient = UnleashClient(
50+
url=self.url,
51+
app_name=self.app_name,
52+
environment=self.environment,
53+
custom_headers={"Authorization": self.api_token},
54+
event_callback=self._unleash_event_callback,
55+
cache=self.cache,
56+
)
4957
self._last_context: Optional[EvaluationContext] = None
5058
self._event_handlers: dict[ProviderEvent, list[Callable]] = {
5159
ProviderEvent.PROVIDER_READY: [],
@@ -67,17 +75,7 @@ def initialize(
6775
evaluation_context: Optional evaluation context (not used for initialization)
6876
"""
6977
try:
70-
self.client = UnleashClient(
71-
url=self.url,
72-
app_name=self.app_name,
73-
environment=self.environment,
74-
custom_headers={"Authorization": self.api_token},
75-
event_callback=self._unleash_event_callback,
76-
cache=self.cache,
77-
)
7878
self.client.initialize_client(fetch_toggles=self.fetch_toggles)
79-
self._status = ProviderStatus.READY
80-
self._event_manager.emit_event(ProviderEvent.PROVIDER_READY)
8179
except Exception as e:
8280
self._status = ProviderStatus.ERROR
8381
self._event_manager.emit_event(
@@ -101,19 +99,18 @@ def get_provider_hooks(self) -> list[Hook]:
10199

102100
def shutdown(self) -> None:
103101
"""Shutdown the Unleash client."""
104-
if self.client:
105-
try:
102+
try:
103+
if self.client.is_initialized:
106104
self.client.destroy()
107-
self.client = None
108-
self._status = ProviderStatus.NOT_READY
109-
except Exception as e:
110-
self._status = ProviderStatus.ERROR
111-
self._event_manager.emit_event(
112-
ProviderEvent.PROVIDER_ERROR,
113-
error_message=str(e),
114-
error_code=ErrorCode.GENERAL,
115-
)
116-
raise GeneralError(f"Failed to shutdown Unleash provider: {e}") from e
105+
self._status = ProviderStatus.NOT_READY
106+
except Exception as e:
107+
self._status = ProviderStatus.ERROR
108+
self._event_manager.emit_event(
109+
ProviderEvent.PROVIDER_ERROR,
110+
error_message=str(e),
111+
error_code=ErrorCode.GENERAL,
112+
)
113+
raise GeneralError(f"Failed to shutdown Unleash provider: {e}") from e
117114

118115
def on_context_changed(
119116
self,
@@ -152,7 +149,7 @@ def _unleash_event_callback(self, event: BaseEvent) -> None:
152149
Args:
153150
event: The Unleash event
154151
"""
155-
if isinstance(event, UnleashReadyEvent):
152+
if event.event_type == UnleashEventType.READY:
156153
self._status = ProviderStatus.READY
157154
self._event_manager.handle_unleash_event(event)
158155

providers/openfeature-provider-unleash/src/openfeature/contrib/provider/unleash/flag_evaluation.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class UnleashProvider(Protocol):
1818
"""Protocol defining the interface needed by FlagEvaluator."""
1919

2020
@property
21-
def client(self) -> Optional[UnleashClient]: ...
21+
def client(self) -> UnleashClient: ...
2222

2323
@property
2424
def app_name(self) -> str: ...
@@ -55,9 +55,6 @@ def resolve_boolean_details(
5555
Returns:
5656
FlagResolutionDetails with the resolved boolean value
5757
"""
58-
if not self._provider.client:
59-
raise GeneralError("Provider not initialized. Call initialize() first.")
60-
6158
try:
6259
context = self._provider._build_unleash_context(evaluation_context)
6360
is_enabled = self._provider.client.is_enabled(flag_key, context=context)
@@ -177,9 +174,6 @@ def _resolve_variant_flag(
177174
Returns:
178175
FlagResolutionDetails with the resolved value
179176
"""
180-
if not self._provider.client:
181-
raise GeneralError("Provider not initialized. Call initialize() first.")
182-
183177
try:
184178
context = self._provider._build_unleash_context(evaluation_context)
185179
variant = self._provider.client.get_variant(flag_key, context=context)

providers/openfeature-provider-unleash/tests/test_events.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,12 @@ def on_config_changed(event_details):
5050
ProviderEvent.PROVIDER_CONFIGURATION_CHANGED, on_config_changed
5151
)
5252

53-
# Initialize should emit PROVIDER_READY
5453
provider.initialize()
54+
55+
# Simulate the READY event from UnleashClient
56+
event_callback = mock_unleash_client.call_args[1]["event_callback"]
57+
event_callback(UnleashReadyEvent(UnleashEventType.READY, uuid.uuid4()))
58+
5559
assert len(ready_events) == 1
5660
assert ready_events[0]["provider_name"] == "Unleash Provider"
5761

@@ -112,6 +116,7 @@ def test_unleash_event_callback():
112116

113117
# Create a mock UnleashFetchedEvent with features
114118
mock_event = Mock(spec=UnleashFetchedEvent)
119+
mock_event.event_type = UnleashEventType.FETCHED
115120
mock_event.features = {"flag1": {}, "flag2": {}}
116121

117122
event_callback(mock_event)

providers/openfeature-provider-unleash/tests/test_provider.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import uuid
12
from unittest.mock import Mock, patch
3+
from UnleashClient.events import UnleashEventType, UnleashReadyEvent
24

35
from openfeature.contrib.provider.unleash import UnleashProvider
46
from openfeature.evaluation_context import EvaluationContext
@@ -73,10 +75,13 @@ def test_unleash_provider_initialization():
7375
# Should start as NOT_READY
7476
assert provider.get_status() == ProviderStatus.NOT_READY
7577

76-
# Initialize the provider
7778
provider.initialize()
7879

79-
# Should be READY after initialization
80+
# Simulate the READY event from UnleashClient
81+
event_callback = mock_unleash_client.call_args[1]["event_callback"]
82+
event_callback(UnleashReadyEvent(UnleashEventType.READY, uuid.uuid4()))
83+
84+
# Should be READY after receiving the READY event
8085
assert provider.get_status() == ProviderStatus.READY
8186
assert provider.client is not None
8287

0 commit comments

Comments
 (0)