Skip to content

Commit

Permalink
feat(python): allow for listing known features (#171)
Browse files Browse the repository at this point in the history
  • Loading branch information
sighphyre authored Nov 11, 2024
1 parent 15bfc05 commit 95e0c4a
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 4 deletions.
22 changes: 20 additions & 2 deletions python-engine/tests/test_engine.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from dataclasses import asdict
import json
from yggdrasil_engine.engine import UnleashEngine, Variant
from yggdrasil_engine.engine import UnleashEngine, Variant, FeatureDefinition
import json
import os

Expand Down Expand Up @@ -138,7 +138,6 @@ def test_increments_counts_for_yes_no_and_variants():
assert metrics["toggles"]["testToggle"]["variants"]["disabled"] == 1



def test_metrics_are_flushed_when_get_metrics_is_called():
engine = UnleashEngine()

Expand All @@ -155,6 +154,7 @@ def test_metrics_are_flushed_when_get_metrics_is_called():
metrics = engine.get_metrics()
assert metrics is None


def test_metrics_are_still_incremented_when_toggle_does_not_exist():
engine = UnleashEngine()

Expand All @@ -164,6 +164,7 @@ def test_metrics_are_still_incremented_when_toggle_does_not_exist():

assert metrics["toggles"]["aToggleSoSecretItDoesNotExist"]["yes"] == 1


def test_yields_impression_data():
engine = UnleashEngine()

Expand All @@ -172,3 +173,20 @@ def test_yields_impression_data():
assert engine.should_emit_impression_event("Feature.A")
assert not engine.should_emit_impression_event("Nonexisting")


def test_list_known_toggles():
engine = UnleashEngine()

engine.take_state(CUSTOM_STRATEGY_STATE)
first_toggle = engine.list_known_toggles()[0]

assert len(engine.list_known_toggles()) == 1
assert first_toggle == FeatureDefinition(
name="Feature.A", project="default", type=None
)


def test_list_empty_toggles_yields_empty_list():
engine = UnleashEngine()

assert engine.list_known_toggles() == []
35 changes: 33 additions & 2 deletions python-engine/yggdrasil_engine/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,34 @@ def from_dict(data: dict) -> "Variant":
)


@dataclass
class FeatureDefinition:
name: str
project: str
type: Optional[str]

@staticmethod
def from_dict(data: dict) -> "FeatureDefinition":
return FeatureDefinition(
name=data.get("name", ""),
project=data.get("project", ""),
type=data.get("type"),
)


def load_feature_defs(raw_defs: List[dict]) -> List[FeatureDefinition]:
return [FeatureDefinition.from_dict(defn) for defn in raw_defs]


@dataclass
class Response:
status_code: StatusCode
value: Optional[any]
error_message: Optional[str]

# this only exists to handle feature_enabled/featureEnabled
deserializers: ClassVar[Dict[Type, Callable[[Any], Any]]] = {
Variant: Variant.from_dict,
List[FeatureDefinition]: load_feature_defs,
}

@staticmethod
Expand Down Expand Up @@ -134,6 +153,9 @@ def __init__(self):

self.lib.should_emit_impression_event.restype = ctypes.POINTER(ctypes.c_char)

self.lib.list_known_toggles.argtypes = [ctypes.c_void_p]
self.lib.list_known_toggles.restype = ctypes.POINTER(ctypes.c_char)

self.state = self.lib.new_engine()
self.custom_strategy_handler = CustomStrategyHandler()

Expand Down Expand Up @@ -227,4 +249,13 @@ def should_emit_impression_event(self, toggle_name: str) -> bool:
with self.materialize_pointer(response_ptr, bool) as response:
if response.status_code == StatusCode.ERROR:
raise YggdrasilError(response.error_message)
return response.value
return response.value

def list_known_toggles(self) -> List[FeatureDefinition]:
response_ptr = self.lib.list_known_toggles(self.state)
with self.materialize_pointer(
response_ptr, List[FeatureDefinition]
) as response:
if response.status_code == StatusCode.ERROR:
raise YggdrasilError(response.error_message)
return response.value

0 comments on commit 95e0c4a

Please sign in to comment.