Skip to content

Commit e9cd2ed

Browse files
committed
fix: AbstractProvider dependency everywhere
Signed-off-by: Cole Bailey <[email protected]>
1 parent 45e3739 commit e9cd2ed

File tree

7 files changed

+66
-28
lines changed

7 files changed

+66
-28
lines changed

providers/openfeature-provider-flagd/src/openfeature/contrib/provider/flagd/provider.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import typing
2525

2626
from openfeature.evaluation_context import EvaluationContext
27+
from openfeature.event import ProviderEventDetails
2728
from openfeature.flag_evaluation import FlagResolutionDetails
2829
from openfeature.provider.metadata import Metadata
2930
from openfeature.provider.provider import AbstractProvider
@@ -75,7 +76,12 @@ def setup_resolver(self) -> AbstractResolver:
7576
if self.config.resolver_type == ResolverType.GRPC:
7677
return GrpcResolver(self.config)
7778
elif self.config.resolver_type == ResolverType.IN_PROCESS:
78-
return InProcessResolver(self.config, self)
79+
return InProcessResolver(
80+
self.config,
81+
self.emit_provider_ready,
82+
self.emit_provider_error,
83+
self.emit_provider_configuration_changed,
84+
)
7985
else:
8086
raise ValueError(
8187
f"`resolver_type` parameter invalid: {self.config.resolver_type}"
@@ -92,6 +98,11 @@ def get_metadata(self) -> Metadata:
9298
"""Returns provider metadata"""
9399
return Metadata(name="FlagdProvider")
94100

101+
def flag_store_updated_callback(self, flag_keys: typing.List[str]) -> None:
102+
self.emit_provider_configuration_changed(
103+
ProviderEventDetails(flags_changed=flag_keys)
104+
)
105+
95106
def resolve_boolean_details(
96107
self,
97108
key: str,

providers/openfeature-provider-flagd/src/openfeature/contrib/provider/flagd/resolvers/in_process.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
from json_logic import builtins, jsonLogic # type: ignore[import-untyped]
55

66
from openfeature.evaluation_context import EvaluationContext
7+
from openfeature.event import ProviderEventDetails
78
from openfeature.exception import FlagNotFoundError, ParseError
89
from openfeature.flag_evaluation import FlagResolutionDetails, Reason
9-
from openfeature.provider.provider import AbstractProvider
1010

1111
from ..config import Config
1212
from .process.connector import FlagStateConnector
@@ -27,19 +27,32 @@ class InProcessResolver:
2727
"sem_ver": sem_ver,
2828
}
2929

30-
def __init__(self, config: Config, provider: AbstractProvider):
30+
def __init__(
31+
self,
32+
config: Config,
33+
emit_provider_ready: typing.Callable[[ProviderEventDetails], None],
34+
emit_provider_error: typing.Callable[[ProviderEventDetails], None],
35+
emit_provider_configuration_changed: typing.Callable[
36+
[ProviderEventDetails], None
37+
],
38+
):
3139
self.config = config
32-
self.provider = provider
33-
self.flag_store = FlagStore(provider)
40+
self.flag_store = FlagStore(emit_provider_configuration_changed)
3441
self.connector: FlagStateConnector = (
3542
FileWatcher(
3643
self.config.offline_flag_source_path,
37-
self.provider,
3844
self.flag_store,
45+
emit_provider_ready,
46+
emit_provider_error,
3947
self.config.offline_poll_interval_seconds,
4048
)
4149
if self.config.offline_flag_source_path
42-
else GrpcWatcher(self.config, self.provider, self.flag_store)
50+
else GrpcWatcher(
51+
self.config,
52+
self.flag_store,
53+
emit_provider_ready,
54+
emit_provider_error,
55+
)
4356
)
4457

4558
def initialize(self, evaluation_context: EvaluationContext) -> None:

providers/openfeature-provider-flagd/src/openfeature/contrib/provider/flagd/resolvers/process/connector/file_watcher.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from openfeature.evaluation_context import EvaluationContext
1111
from openfeature.event import ProviderEventDetails
1212
from openfeature.exception import ParseError, ProviderNotReadyError
13-
from openfeature.provider.provider import AbstractProvider
1413

1514
from ..connector import FlagStateConnector
1615
from ..flags import FlagStore
@@ -22,12 +21,14 @@ class FileWatcher(FlagStateConnector):
2221
def __init__(
2322
self,
2423
file_path: str,
25-
provider: AbstractProvider,
2624
flag_store: FlagStore,
25+
emit_provider_ready: typing.Callable[[ProviderEventDetails], None],
26+
emit_provider_error: typing.Callable[[ProviderEventDetails], None],
2727
poll_interval_seconds: float = 1.0,
2828
):
2929
self.file_path = file_path
30-
self.provider = provider
30+
self.emit_provider_ready = emit_provider_ready
31+
self.emit_provider_error = emit_provider_error
3132
self.poll_interval_seconds = poll_interval_seconds
3233

3334
self.last_modified = 0.0
@@ -83,7 +84,7 @@ def _load_data(self, modified_time: typing.Optional[float] = None) -> None:
8384
self.flag_store.update(data)
8485

8586
if self.should_emit_ready_on_success:
86-
self.provider.emit_provider_ready(
87+
self.emit_provider_ready(
8788
ProviderEventDetails(
8889
message="Reloading file contents recovered from error state"
8990
)
@@ -95,4 +96,4 @@ def _load_data(self, modified_time: typing.Optional[float] = None) -> None:
9596
def handle_error(self, error_message: str) -> None:
9697
logger.exception(error_message)
9798
self.should_emit_ready_on_success = True
98-
self.provider.emit_provider_error(ProviderEventDetails(message=error_message))
99+
self.emit_provider_error(ProviderEventDetails(message=error_message))

providers/openfeature-provider-flagd/src/openfeature/contrib/provider/flagd/resolvers/process/connector/grpc_watcher.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
import logging
33
import threading
44
import time
5+
import typing
56

67
import grpc
78

89
from openfeature.evaluation_context import EvaluationContext
910
from openfeature.event import ProviderEventDetails
1011
from openfeature.exception import ErrorCode, ParseError, ProviderNotReadyError
11-
from openfeature.provider.provider import AbstractProvider
1212

1313
from ....config import Config
1414
from ....proto.flagd.sync.v1 import sync_pb2, sync_pb2_grpc
@@ -22,16 +22,21 @@ class GrpcWatcher(FlagStateConnector):
2222
MAX_BACK_OFF = 120
2323

2424
def __init__(
25-
self, config: Config, provider: AbstractProvider, flag_store: FlagStore
25+
self,
26+
config: Config,
27+
flag_store: FlagStore,
28+
emit_provider_ready: typing.Callable[[ProviderEventDetails], None],
29+
emit_provider_error: typing.Callable[[ProviderEventDetails], None],
2630
):
27-
self.provider = provider
2831
self.flag_store = flag_store
2932
channel_factory = grpc.secure_channel if config.tls else grpc.insecure_channel
3033
self.channel = channel_factory(f"{config.host}:{config.port}")
3134
self.stub = sync_pb2_grpc.FlagSyncServiceStub(self.channel)
3235
self.timeout = config.timeout
3336
self.retry_backoff_seconds = config.retry_backoff_seconds
3437
self.selector = config.selector
38+
self.emit_provider_ready = emit_provider_ready
39+
self.emit_provider_error = emit_provider_error
3540

3641
self.connected = False
3742

@@ -71,7 +76,7 @@ def sync_flags(self) -> None:
7176
self.flag_store.update(json.loads(flag_str))
7277

7378
if not self.connected:
74-
self.provider.emit_provider_ready(
79+
self.emit_provider_ready(
7580
ProviderEventDetails(
7681
message="gRPC sync connection established"
7782
)
@@ -94,7 +99,7 @@ def sync_flags(self) -> None:
9499
)
95100

96101
self.connected = False
97-
self.provider.emit_provider_error(
102+
self.emit_provider_error(
98103
ProviderEventDetails(
99104
message=f"gRPC sync disconnected, reconnecting in {retry_delay}s",
100105
error_code=ErrorCode.GENERAL,

providers/openfeature-provider-flagd/src/openfeature/contrib/provider/flagd/resolvers/process/flags.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@
55

66
from openfeature.event import ProviderEventDetails
77
from openfeature.exception import ParseError
8-
from openfeature.provider.provider import AbstractProvider
98

109

1110
class FlagStore:
1211
def __init__(
1312
self,
14-
provider: AbstractProvider,
13+
emit_provider_configuration_changed: typing.Callable[
14+
[ProviderEventDetails], None
15+
],
1516
):
16-
self.provider = provider
17+
self.emit_provider_configuration_changed = emit_provider_configuration_changed
1718
self.flags: typing.Mapping[str, "Flag"] = {}
1819

1920
def get_flag(self, key: str) -> typing.Optional["Flag"]:
@@ -34,7 +35,7 @@ def update(self, flags_data: dict) -> None:
3435
raise ParseError("`flags` key of configuration must be a dictionary")
3536
self.flags = {key: Flag.from_dict(key, data) for key, data in flags.items()}
3637

37-
self.provider.emit_provider_configuration_changed(
38+
self.emit_provider_configuration_changed(
3839
ProviderEventDetails(flags_changed=list(self.flags.keys()))
3940
)
4041

providers/openfeature-provider-flagd/tests/test_file_store.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
FileWatcher,
99
)
1010
from openfeature.contrib.provider.flagd.resolvers.process.flags import Flag, FlagStore
11-
from openfeature.provider.provider import AbstractProvider
1211

1312

1413
def create_client(provider: FlagdProvider):
@@ -24,9 +23,13 @@ def create_client(provider: FlagdProvider):
2423
],
2524
)
2625
def test_file_load(file_name: str):
27-
provider = Mock(spec=AbstractProvider)
28-
flag_store = FlagStore(provider)
29-
file_watcher = FileWatcher(f"tests/flags/{file_name}", provider, flag_store)
26+
emit_provider_configuration_changed = Mock()
27+
emit_provider_ready = Mock()
28+
emit_provider_error = Mock()
29+
flag_store = FlagStore(emit_provider_configuration_changed)
30+
file_watcher = FileWatcher(
31+
f"tests/flags/{file_name}", flag_store, emit_provider_ready, emit_provider_error
32+
)
3033
file_watcher.initialize(None)
3134

3235
flag = flag_store.get_flag("basic-flag")

providers/openfeature-provider-flagd/tests/test_grpc_sync_connector.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,13 @@ def sync(request):
3030
),
3131
)
3232
def test_invalid_payload(flag_configuration: str):
33-
fake_provider = MagicMock()
34-
flag_store = FlagStore(fake_provider)
35-
watcher = GrpcWatcher(Config(timeout=0.2), fake_provider, flag_store)
33+
emit_provider_configuration_changed = MagicMock()
34+
emit_provider_ready = MagicMock()
35+
emit_provider_error = MagicMock()
36+
flag_store = FlagStore(emit_provider_configuration_changed)
37+
watcher = GrpcWatcher(
38+
Config(timeout=0.2), flag_store, emit_provider_ready, emit_provider_error
39+
)
3640

3741
fake_sync_flags = fake_grpc_service(flag_configuration)
3842
with patch.object(watcher.stub, "SyncFlags", fake_sync_flags), pytest.raises(

0 commit comments

Comments
 (0)