diff --git a/splitio/events/__init__.py b/splitio/events/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/splitio/events/events_manager_config.py b/splitio/events/events_manager_config.py new file mode 100644 index 00000000..891d17a5 --- /dev/null +++ b/splitio/events/events_manager_config.py @@ -0,0 +1,124 @@ +"""Events Manager Configuration.""" +from splitio.models.events import SdkEvent, SdkInternalEvent + +class EventsManagerConfig(object): + """Events Manager Configurations class.""" + + def __init__(self): + """ + Construct Events Manager Configuration instance. + """ + self._require_all = self._get_require_all() + self._prerequisites = self._get_prerequisites() + self._require_any = self._get_require_any() + self._suppressed_by = self._get_suppressed_by() + self._execution_limits = self._get_execution_limits() + self._evaluation_order = self._get_sorted_events() + + @property + def require_all(self): + """Return require all dict""" + return self._require_all + + @property + def prerequisites(self): + """Return prerequisites dict""" + return self._prerequisites + + @property + def require_any(self): + """Return require_any dict""" + return self._require_any + + @property + def suppressed_by(self): + """Return suppressed_by dict""" + return self._suppressed_by + + @property + def execution_limits(self): + """Return execution_limits dict""" + return self._execution_limits + + @property + def prerequisites(self): + """Return require all dict""" + return self._prerequisites + + @property + def evaluation_order(self): + """Return evaluation_order dict""" + return self._evaluation_order + + @property + def sorted_events(self): + """Return sorted_events dict""" + return self._sorted_events + + def _get_require_all(self): + """Return require all dict""" + return { + SdkEvent.SDK_READY: {SdkInternalEvent.SDK_READY} + } + + def _get_prerequisites(self): + """Return prerequisites dict""" + return { + SdkEvent.SDK_UPDATE: {SdkEvent.SDK_READY} + } + + def _get_require_any(self): + """Return require_any dict""" + return { + SdkEvent.SDK_UPDATE: {SdkInternalEvent.FLAG_KILLED_NOTIFICATION, SdkInternalEvent.FLAGS_UPDATED, + SdkInternalEvent.RB_SEGMENTS_UPDATED, SdkInternalEvent.SEGMENTS_UPDATED}, + SdkEvent.SDK_READY_TIMED_OUT: {SdkInternalEvent.SDK_TIMED_OUT} + } + + def _get_suppressed_by(self): + """Return suppressed_by dict""" + return { + SdkEvent.SDK_READY_TIMED_OUT: {SdkEvent.SDK_READY} + } + + def _get_execution_limits(self): + """Return execution_limits dict""" + return { + SdkEvent.SDK_READY: 1, + SdkEvent.SDK_READY_TIMED_OUT: -1, + SdkEvent.SDK_UPDATE: -1 + } + + def _get_sorted_events(self): + """Return dorted events set""" + sorted_events = [] + for sdk_event in [SdkEvent.SDK_READY, SdkEvent.SDK_READY_TIMED_OUT, SdkEvent.SDK_UPDATE]: + sorted_events = self._dfs_recursive(sdk_event, sorted_events) + + return sorted_events + + + def _dfs_recursive(self, sdk_event, added): + """Return sorted events set based on the dependency rules""" + if sdk_event in added: + return added + + for dependent_event in self._get_dependencies(sdk_event): + added = self._dfs_recursive(dependent_event, added) + + added.append(sdk_event) + return added + + def _get_dependencies(self, sdk_event): + """Return dependencies set from prerequisites and suppressed events for a given event""" + dependencies = set() + for prerequisites_event_name, prerequisites_event_value in self.prerequisites.items(): + if prerequisites_event_name == sdk_event: + for prereq_event in prerequisites_event_value: + dependencies.add(prereq_event) + + for suppressed_event_name, suppressed_event_value in self.suppressed_by.items(): + if sdk_event in suppressed_event_value: + dependencies.add(suppressed_event_name) + + return dependencies diff --git a/splitio/events/events_metadata.py b/splitio/events/events_metadata.py new file mode 100644 index 00000000..5d6f4961 --- /dev/null +++ b/splitio/events/events_metadata.py @@ -0,0 +1,35 @@ +"""Events Metadata.""" +from enum import Enum + +class SdkEventType(Enum): + """Public event types""" + + FLAG_UPDATE = 'FLAG_UPDATE' + SEGMENT_UPDATE = 'SEGMENT_UPDATE' + +class EventsMetadata(object): + """Events Metadata class.""" + + def __init__(self, type, names): + """ + Construct Events Metadata instance. + """ + self._type = type + self._names = self._sanitize(names) + + def get_type(self): + """Return type""" + return self._type + + def get_names(self): + """Return names""" + return self._names + + def _sanitize(self, names): + """Return sanitized names list with values str""" + santized_data = set() + for name in names: + if isinstance(name, str): + santized_data.add(name) + + return santized_data diff --git a/splitio/models/events.py b/splitio/models/events.py index b924417b..efcd3ef1 100644 --- a/splitio/models/events.py +++ b/splitio/models/events.py @@ -4,7 +4,7 @@ The dto is implemented as a namedtuple for performance matters. """ from collections import namedtuple - +from enum import Enum Event = namedtuple('Event', [ 'key', @@ -19,3 +19,23 @@ 'event', 'size', ]) + +class SdkEvent(Enum): + """Public SDK events""" + + SDK_READY = 'SDK_READY' + SDK_READY_TIMED_OUT = 'SDK_READY_TIMED_OUT' + SDK_UPDATE = 'SDK_UPDATE' + +class SdkInternalEvent(Enum): + """Internal SDK events""" + + SDK_READY = 'SDK_READY' + SDK_TIMED_OUT = 'SDK_TIMED_OUT' + FLAGS_UPDATED = 'FLAGS_UPDATED' + FLAG_KILLED_NOTIFICATION = 'FLAG_KILLED_NOTIFICATION' + SEGMENTS_UPDATED = 'SEGMENTS_UPDATED' + RB_SEGMENTS_UPDATED = 'RB_SEGMENTS_UPDATED' + LARGE_SEGMENTS_UPDATED = 'LARGE_SEGMENTS_UPDATED' + + diff --git a/tests/events/test_events_manager_config.py b/tests/events/test_events_manager_config.py new file mode 100644 index 00000000..5c9748c0 --- /dev/null +++ b/tests/events/test_events_manager_config.py @@ -0,0 +1,43 @@ +"""EventsManagerConfig test module.""" +import pytest + +from splitio.events.events_manager_config import EventsManagerConfig +from splitio.models.events import SdkEvent, SdkInternalEvent + +class EventsManagerConfigTests(object): + """Tests for EventsManagerConfig.""" + + def test_build_instance(self): + config = EventsManagerConfig() + + assert len(config.require_all[SdkEvent.SDK_READY]) == 1 + assert SdkInternalEvent.SDK_READY in config.require_all[SdkEvent.SDK_READY] + + assert SdkEvent.SDK_READY in config.prerequisites[SdkEvent.SDK_UPDATE] + + assert config.execution_limits[SdkEvent.SDK_READY_TIMED_OUT] == -1 + assert config.execution_limits[SdkEvent.SDK_UPDATE] == -1 + assert config.execution_limits[SdkEvent.SDK_READY] == 1 + + assert len(config.require_any[SdkEvent.SDK_READY_TIMED_OUT]) == 1 + assert SdkInternalEvent.SDK_TIMED_OUT in config.require_any[SdkEvent.SDK_READY_TIMED_OUT] + + assert len(config.require_any[SdkEvent.SDK_UPDATE]) == 4 + assert SdkInternalEvent.FLAG_KILLED_NOTIFICATION in config.require_any[SdkEvent.SDK_UPDATE] + assert SdkInternalEvent.FLAGS_UPDATED in config.require_any[SdkEvent.SDK_UPDATE] + assert SdkInternalEvent.RB_SEGMENTS_UPDATED in config.require_any[SdkEvent.SDK_UPDATE] + assert SdkInternalEvent.SEGMENTS_UPDATED in config.require_any[SdkEvent.SDK_UPDATE] + + assert len(config.suppressed_by[SdkEvent.SDK_READY_TIMED_OUT]) == 1 + assert SdkEvent.SDK_READY in config.suppressed_by[SdkEvent.SDK_READY_TIMED_OUT] + + order = 0 + assert len(config.evaluation_order) == 3 + for sdk_event in config.evaluation_order: + order += 1 + if order == 1: + assert sdk_event == SdkEvent.SDK_READY_TIMED_OUT + if order == 2: + assert sdk_event == SdkEvent.SDK_READY + if order == 3: + assert sdk_event == SdkEvent.SDK_UPDATE \ No newline at end of file diff --git a/tests/events/test_events_metadata.py b/tests/events/test_events_metadata.py new file mode 100644 index 00000000..3ce90d0f --- /dev/null +++ b/tests/events/test_events_metadata.py @@ -0,0 +1,21 @@ +"""EventsMetadata test module.""" +import pytest + +from splitio.events.events_metadata import EventsMetadata +from splitio.events.events_metadata import SdkEventType + +class EventsMetadataTests(object): + """Tests for EventsMetadata.""" + + def test_build_instance(self): + metadata = EventsMetadata(SdkEventType.FLAG_UPDATE, { "feature1" }) + assert len(metadata.get_names()) == 1 + assert metadata.get_names().pop() == "feature1" + assert len(metadata.get_names()) == 0 + assert metadata.get_type() == SdkEventType.FLAG_UPDATE + + def test_sanitize_none_input(self): + metadata = EventsMetadata(SdkEventType.FLAG_UPDATE, { "feature1", None, 123, False }) + assert len(metadata.get_names()) == 1 + assert metadata.get_names().pop() == "feature1" + assert len(metadata.get_names()) == 0