Skip to content

Commit

Permalink
fixup: adding gherkin tests for evaluations, and fxing found issues
Browse files Browse the repository at this point in the history
Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>
  • Loading branch information
aepfli committed Nov 23, 2024
1 parent fb110bb commit 36269b5
Show file tree
Hide file tree
Showing 19 changed files with 419 additions and 161 deletions.
2 changes: 1 addition & 1 deletion providers/openfeature-provider-flagd/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ classifiers = [
keywords = []
dependencies = [
"openfeature-sdk>=0.4.0",
"grpcio>=1.60.0",
"grpcio>=1.68.0",
"protobuf>=4.25.2",
"mmh3>=4.1.0",
"panzi-json-logic>=1.0.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

from ..config import Config
from ..flag_type import FlagType
from . import AbstractResolver
from .protocol import AbstractResolver

T = typing.TypeVar("T")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,6 @@ def resolve_float_details(
evaluation_context: typing.Optional[EvaluationContext] = None,
) -> FlagResolutionDetails[float]:
result = self._resolve(key, default_value, evaluation_context)
if not isinstance(result.value, float):
result.value = float(result.value)
return result

def resolve_integer_details(
Expand All @@ -84,8 +82,6 @@ def resolve_integer_details(
evaluation_context: typing.Optional[EvaluationContext] = None,
) -> FlagResolutionDetails[int]:
result = self._resolve(key, default_value, evaluation_context)
if not isinstance(result.value, int):
result.value = int(result.value)
return result

def resolve_object_details(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
import typing

import grpc
from schemas.protobuf.flagd.sync.v1 import ( # type:ignore[import-not-found]
sync_pb2,
sync_pb2_grpc,
)

from openfeature.evaluation_context import EvaluationContext
from openfeature.event import ProviderEventDetails
from openfeature.exception import ErrorCode, ParseError, ProviderNotReadyError

from ....config import Config
from ....proto.flagd.sync.v1 import sync_pb2, sync_pb2_grpc
from ..connector import FlagStateConnector
from ..flags import FlagStore

Expand All @@ -29,9 +32,9 @@ def __init__(
emit_provider_error: typing.Callable[[ProviderEventDetails], None],
):
self.flag_store = flag_store
channel_factory = grpc.secure_channel if config.tls else grpc.insecure_channel
self.channel = channel_factory(f"{config.host}:{config.port}")
self.stub = sync_pb2_grpc.FlagSyncServiceStub(self.channel)
self.config = config

self.stub, self.channel = self.create_stub()
self.timeout = config.timeout
self.retry_backoff_seconds = config.retry_backoff_seconds
self.selector = config.selector
Expand All @@ -40,6 +43,22 @@ def __init__(

self.connected = False

def create_stub(
self,
) -> typing.Tuple[sync_pb2_grpc.FlagSyncServiceStub, grpc.Channel]:
config = self.config
channel_factory = grpc.secure_channel if config.tls else grpc.insecure_channel
channel = channel_factory(
f"{config.host}:{config.port}",
options=(
("grpc.max_reconnect_backoff_ms", 1000),
("grpc.initial_reconnect_backoff_ms", 1000),
("grpc.keepalive_time_ms", 1000),
),
)
stub = sync_pb2_grpc.FlagSyncServiceStub(channel)
return stub, channel

def initialize(self, context: EvaluationContext) -> None:
self.active = True
self.thread = threading.Thread(
Expand All @@ -60,13 +79,13 @@ def initialize(self, context: EvaluationContext) -> None:

def shutdown(self) -> None:
self.active = False
self.channel.close()

def sync_flags(self) -> None:
request = sync_pb2.SyncFlagsRequest(selector=self.selector) # type:ignore[attr-defined]

retry_delay = self.retry_backoff_seconds
while self.active:
try:
request = sync_pb2.SyncFlagsRequest(selector=self.selector)
logger.debug("Setting up gRPC sync flags connection")
for flag_rsp in self.stub.SyncFlags(request):
flag_str = flag_rsp.flag_configuration
Expand All @@ -89,6 +108,9 @@ def sync_flags(self) -> None:
return

Check warning on line 108 in providers/openfeature-provider-flagd/src/openfeature/contrib/provider/flagd/resolvers/process/connector/grpc_watcher.py

View check run for this annotation

Codecov / codecov/patch

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

Added lines #L107 - L108 were not covered by tests
except grpc.RpcError as e:
logger.error(f"SyncFlags stream error, {e.code()=} {e.details()=}")
if e.code() == grpc.StatusCode.UNAVAILABLE:
self.stub, self.channel = self.create_stub()

except json.JSONDecodeError:
logger.exception(
f"Could not parse JSON flag data from SyncFlags endpoint: {flag_str=}"
Expand All @@ -107,4 +129,4 @@ def sync_flags(self) -> None:
)
logger.info(f"gRPC sync disconnected, reconnecting in {retry_delay}s")
time.sleep(retry_delay)
retry_delay = min(2 * retry_delay, self.MAX_BACK_OFF)
retry_delay = min(2, self.MAX_BACK_OFF)
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ def from_dict(cls, key: str, data: dict) -> "Flag":
data["default_variant"] = data["defaultVariant"]
del data["defaultVariant"]

if "source" in data:
del data["source"]
if "selector" in data:
del data["selector"]
try:
flag = cls(key=key, **data)
return flag
Expand Down
15 changes: 4 additions & 11 deletions providers/openfeature-provider-flagd/tests/e2e/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,22 @@
from tests.e2e.flagd_container import FlagdContainer
from tests.e2e.steps import * # noqa: F403

from openfeature import api
from openfeature.contrib.provider.flagd import FlagdProvider

JsonPrimitive = typing.Union[str, bool, float, int]


@pytest.fixture(autouse=True, scope="package")
def setup(request, port, image, resolver_type):
@pytest.fixture(autouse=True, scope="module")
def setup(request, port, image):
container: DockerContainer = FlagdContainer(
image=image,
port=port,
)
# Setup code
c = container.start()
api.set_provider(
FlagdProvider(
resolver_type=resolver_type,
port=int(container.get_exposed_port(port)),
)
)

def fin():
c.stop()

# Teardown code
request.addfinalizer(fin)

return c.get_exposed_port(port)

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Loading

0 comments on commit 36269b5

Please sign in to comment.