Skip to content

Commit f484895

Browse files
committed
chore: refactor connector api so that an async variant can be created
1 parent d6c338a commit f484895

File tree

10 files changed

+88
-76
lines changed

10 files changed

+88
-76
lines changed

UnleashClient/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
from UnleashClient.api.sync_api import register_client
1919
from UnleashClient.connectors import (
20-
BaseConnector,
20+
BaseSyncConnector,
2121
BootstrapConnector,
2222
OfflineConnector,
2323
PollingConnector,
@@ -175,7 +175,7 @@ def __init__(
175175
cache=self.cache,
176176
).start()
177177

178-
self.connector: BaseConnector = None
178+
self.connector: BaseSyncConnector = None
179179

180180
self._evaluator = Evaluator(
181181
engine=self.engine,

UnleashClient/connectors/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
from .base_connector import BaseConnector
1+
from .base_sync_connector import BaseSyncConnector
22
from .bootstrap_connector import BootstrapConnector
33
from .offline_connector import OfflineConnector
44
from .polling_connector import PollingConnector
55
from .streaming_connector import StreamingConnector
66

77
__all__ = [
8-
"BaseConnector",
8+
"BaseSyncConnector",
99
"BootstrapConnector",
1010
"OfflineConnector",
1111
"PollingConnector",

UnleashClient/connectors/base_connector.py

Lines changed: 0 additions & 57 deletions
This file was deleted.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from abc import ABC, abstractmethod
2+
from typing import Callable, Optional
3+
from yggdrasil_engine.engine import UnleashEngine
4+
from UnleashClient.cache import BaseCache
5+
6+
7+
class BaseSyncConnector(ABC):
8+
def __init__(
9+
self,
10+
engine: UnleashEngine,
11+
cache: BaseCache,
12+
ready_callback: Optional[Callable] = None,
13+
):
14+
"""
15+
:param engine: Feature evaluation engine instance (UnleashEngine).
16+
:param cache: Should be the cache class variable from UnleashClient
17+
:param ready_callback: Optional function to call when features are successfully loaded.
18+
"""
19+
self.engine = engine
20+
self.cache = cache
21+
self.ready_callback = ready_callback
22+
23+
@abstractmethod
24+
def start(self):
25+
pass
26+
27+
@abstractmethod
28+
def stop(self):
29+
pass
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
from yggdrasil_engine.engine import UnleashEngine
22

33
from UnleashClient.cache import BaseCache
4+
from UnleashClient.connectors.hydration import hydrate_engine
45

5-
from .base_connector import BaseConnector
6+
from .base_sync_connector import BaseSyncConnector
67

78

8-
class BootstrapConnector(BaseConnector):
9+
class BootstrapConnector(BaseSyncConnector):
910
def __init__(
1011
self,
1112
engine: UnleashEngine,
@@ -16,7 +17,7 @@ def __init__(
1617
self.job = None
1718

1819
def start(self):
19-
self.load_features()
20+
hydrate_engine(self.cache, self.engine, None)
2021

2122
def stop(self):
2223
pass
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from typing import Callable, Optional
2+
3+
from yggdrasil_engine.engine import UnleashEngine
4+
5+
from UnleashClient.cache import BaseCache
6+
from UnleashClient.constants import FEATURES_URL
7+
from UnleashClient.utils import LOGGER
8+
9+
10+
def hydrate_engine(
11+
cache: BaseCache, engine: UnleashEngine, ready_callback: Optional[Callable] = None
12+
):
13+
feature_provisioning = cache.get(FEATURES_URL)
14+
if not feature_provisioning:
15+
LOGGER.warning(
16+
"Unleash client does not have cached features. "
17+
"Please make sure client can communicate with Unleash server!"
18+
)
19+
return
20+
21+
try:
22+
warnings = engine.take_state(feature_provisioning)
23+
if ready_callback:
24+
ready_callback()
25+
if warnings:
26+
LOGGER.warning(
27+
"Some features were not able to be parsed correctly, they may not evaluate as expected"
28+
)
29+
LOGGER.warning(warnings)
30+
except Exception as e:
31+
LOGGER.error(f"Error loading features: {e}")
32+
LOGGER.debug(f"Full feature response body from server: {feature_provisioning}")

UnleashClient/connectors/offline_connector.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
from yggdrasil_engine.engine import UnleashEngine
66

77
from UnleashClient.cache import BaseCache
8+
from UnleashClient.connectors.hydration import hydrate_engine
89

9-
from .base_connector import BaseConnector
10+
from .base_sync_connector import BaseSyncConnector
1011

1112

12-
class OfflineConnector(BaseConnector):
13+
class OfflineConnector(BaseSyncConnector):
1314
def __init__(
1415
self,
1516
engine: UnleashEngine,
@@ -29,11 +30,14 @@ def __init__(
2930
self.refresh_jitter = refresh_jitter
3031
self.job = None
3132

33+
def hydrate(self):
34+
hydrate_engine(self.cache, self.engine, self.ready_callback)
35+
3236
def start(self):
33-
self.load_features()
37+
self.hydrate()
3438

3539
self.job = self.scheduler.add_job(
36-
self.load_features,
40+
self.hydrate,
3741
trigger=IntervalTrigger(
3842
seconds=self.refresh_interval, jitter=self.refresh_jitter
3943
),

UnleashClient/connectors/polling_connector.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@
77

88
from UnleashClient.api.sync_api import get_feature_toggles
99
from UnleashClient.cache import BaseCache
10+
from UnleashClient.connectors.hydration import hydrate_engine
1011
from UnleashClient.constants import ETAG, FEATURES_URL
1112
from UnleashClient.events import UnleashEventType, UnleashFetchedEvent
1213
from UnleashClient.utils import LOGGER
1314

14-
from .base_connector import BaseConnector
15+
from .base_sync_connector import BaseSyncConnector
1516

1617

17-
class PollingConnector(BaseConnector):
18+
class PollingConnector(BaseSyncConnector):
1819
def __init__(
1920
self,
2021
engine: UnleashEngine,
@@ -78,7 +79,7 @@ def _fetch_and_load(self):
7879
if etag:
7980
self.cache.set(ETAG, etag)
8081

81-
self.load_features()
82+
hydrate_engine(self.cache, self.engine, self.ready_callback)
8283

8384
if state:
8485
if self.event_callback:

UnleashClient/connectors/streaming_connector.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
from yggdrasil_engine.engine import UnleashEngine
77

88
from UnleashClient.cache import BaseCache
9-
from UnleashClient.connectors.base_connector import BaseConnector
9+
from UnleashClient.connectors.base_sync_connector import BaseSyncConnector
10+
from UnleashClient.connectors.hydration import hydrate_engine
1011
from UnleashClient.constants import APPLICATION_HEADERS, FEATURES_URL, STREAMING_URL
1112
from UnleashClient.utils import LOGGER
1213

1314

14-
class StreamingConnector(BaseConnector):
15+
class StreamingConnector(BaseSyncConnector):
1516
def __init__(
1617
self,
1718
engine: UnleashEngine,
@@ -109,14 +110,14 @@ def _run(self):
109110
LOGGER.debug("Ready callback failed", exc_info=True)
110111
except Exception:
111112
LOGGER.error("Error applying streaming state", exc_info=True)
112-
self.load_features()
113+
hydrate_engine(self.cache, self.engine, self.ready_callback)
113114
else:
114115
LOGGER.debug("Ignoring SSE event type: %s", event.event)
115116

116117
LOGGER.debug("SSE stream ended")
117118
except Exception as exc:
118119
LOGGER.warning("Streaming connection failed: %s", exc)
119-
self.load_features()
120+
hydrate_engine(self.cache, self.engine, self.ready_callback)
120121
finally:
121122
try:
122123
if self._client is not None:

tests/unit_tests/connectors/test_offline_connector.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from apscheduler.schedulers.background import BackgroundScheduler
44
from yggdrasil_engine.engine import UnleashEngine
55

6+
from UnleashClient.connectors.hydration import hydrate_engine
67
from tests.utilities.mocks.mock_features import MOCK_FEATURE_RESPONSE
78
from UnleashClient.connectors import OfflineConnector
89
from UnleashClient.constants import FEATURES_URL
@@ -21,7 +22,7 @@ def test_offline_connector_load_features(cache_empty):
2122
scheduler=scheduler,
2223
)
2324

24-
connector.load_features()
25+
hydrate_engine(connector.cache, connector.engine, None)
2526
assert engine.is_enabled("testFlag", {})
2627

2728

0 commit comments

Comments
 (0)