From df03579a081ba6b8b3c5af8613816b297177b3da Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 26 May 2026 14:00:10 -0700 Subject: [PATCH] decoupled models from splitio --- splitio/api/auth.py | 8 +- splitio/api/events.py | 4 +- splitio/api/impressions.py | 4 +- splitio/api/segments.py | 2 +- splitio/api/splits.py | 2 +- splitio/api/telemetry.py | 2 +- splitio/client/client.py | 25 +- splitio/client/config.py | 2 +- splitio/client/factory.py | 6 +- splitio/client/input_validator.py | 2 +- splitio/engine/evaluator.py | 16 +- splitio/engine/impressions/impressions.py | 4 +- splitio/engine/impressions/manager.py | 16 +- splitio/engine/impressions/strategies.py | 12 +- splitio/engine/telemetry.py | 2 +- splitio/events/events_manager.py | 2 +- splitio/events/events_manager_config.py | 2 +- splitio/models/__init__.py | 6 - splitio/models/datatypes.py | 66 - splitio/models/events.py | 39 - splitio/models/fallback_config.py | 100 - splitio/models/fallback_treatment.py | 34 - splitio/models/grammar/__init__.py | 0 splitio/models/grammar/condition.py | 136 -- splitio/models/grammar/matchers/__init__.py | 82 - splitio/models/grammar/matchers/base.py | 122 - splitio/models/grammar/matchers/keys.py | 86 - splitio/models/grammar/matchers/misc.py | 99 - splitio/models/grammar/matchers/numeric.py | 256 --- .../models/grammar/matchers/prerequisites.py | 38 - .../grammar/matchers/rule_based_segment.py | 72 - splitio/models/grammar/matchers/semver.py | 260 --- splitio/models/grammar/matchers/sets.py | 208 -- splitio/models/grammar/matchers/string.py | 274 --- .../models/grammar/matchers/utils/__init__.py | 0 .../models/grammar/matchers/utils/utils.py | 168 -- splitio/models/grammar/partitions.py | 55 - splitio/models/impressions.py | 68 - splitio/models/label.py | 16 + splitio/models/notification.py | 227 -- splitio/models/rule_based_segments.py | 195 -- splitio/models/segments.py | 86 - splitio/models/splits.py | 284 +-- splitio/models/telemetry.py | 1955 ----------------- splitio/models/token.py | 83 - splitio/push/manager.py | 6 +- splitio/push/splitsse.py | 6 +- splitio/push/status_tracker.py | 4 +- splitio/push/workers.py | 4 +- splitio/recorder/recorder.py | 16 +- splitio/storage/__init__.py | 6 +- splitio/storage/inmemmory.py | 28 +- splitio/storage/pluggable.py | 39 +- splitio/storage/redis.py | 41 +- splitio/sync/manager.py | 2 +- splitio/sync/segment.py | 2 +- splitio/sync/split.py | 3 +- splitio/util/storage_helper.py | 5 +- tests/api/test_events.py | 2 +- tests/api/test_impressions_api.py | 2 +- tests/api/test_telemetry_api.py | 2 +- tests/api/test_util.py | 2 +- tests/client/test_client.py | 8 +- tests/client/test_config.py | 4 +- tests/client/test_factory.py | 10 +- tests/client/test_input_validator.py | 4 +- tests/client/test_localhost.py | 2 +- .../files/splits_prereq.json | 0 tests/engine/test_evaluator.py | 18 +- tests/engine/test_impressions.py | 4 +- tests/engine/test_splitter.py | 2 +- tests/events/test_events_delivery.py | 2 +- tests/events/test_events_manager.py | 2 +- tests/events/test_events_manager_config.py | 2 +- tests/events/test_events_task.py | 4 +- tests/integration/test_client_e2e.py | 9 +- .../integration/test_pluggable_integration.py | 3 +- tests/integration/test_redis_integration.py | 3 +- tests/integration/test_streaming_e2e.py | 4 +- tests/models/grammar/files/between-semver.csv | 18 - .../models/grammar/files/equal-to-semver.csv | 7 - .../files/invalid-semantic-versions.csv | 28 - tests/models/grammar/files/regex.txt | 145 -- .../grammar/files/valid-semantic-versions.csv | 25 - tests/models/grammar/test_conditions.py | 78 - tests/models/grammar/test_matchers.py | 1169 ---------- tests/models/grammar/test_partitions.py | 24 - tests/models/grammar/test_semver.py | 71 - tests/models/test_fallback.py | 63 - tests/models/test_notification.py | 51 - tests/models/test_rule_based_segments.py | 103 - tests/models/test_splits.py | 6 +- tests/models/test_telemetry_model.py | 662 ------ tests/models/test_token.py | 49 - tests/push/test_manager.py | 4 +- tests/push/test_segment_worker.py | 2 +- tests/push/test_split_worker.py | 2 +- tests/push/test_splitsse.py | 2 +- tests/push/test_status_tracker.py | 2 +- tests/recorder/test_recorder.py | 4 +- tests/storage/test_inmemory_storage.py | 14 +- tests/storage/test_pluggable.py | 11 +- tests/storage/test_redis.py | 10 +- tests/sync/test_events_synchronizer.py | 2 +- tests/sync/test_impressions_synchronizer.py | 2 +- tests/sync/test_manager.py | 2 +- tests/sync/test_segments_synchronizer.py | 4 +- tests/sync/test_splits_synchronizer.py | 2 +- tests/sync/test_synchronizer.py | 2 +- tests/sync/test_telemetry.py | 4 +- tests/tasks/test_events_sync.py | 2 +- tests/tasks/test_impressions_sync.py | 2 +- tests/tasks/test_segment_sync.py | 6 +- tests/util/test_storage_helper.py | 3 +- 114 files changed, 275 insertions(+), 7683 deletions(-) delete mode 100644 splitio/models/__init__.py delete mode 100644 splitio/models/datatypes.py delete mode 100644 splitio/models/events.py delete mode 100644 splitio/models/fallback_config.py delete mode 100644 splitio/models/fallback_treatment.py delete mode 100644 splitio/models/grammar/__init__.py delete mode 100644 splitio/models/grammar/condition.py delete mode 100644 splitio/models/grammar/matchers/__init__.py delete mode 100644 splitio/models/grammar/matchers/base.py delete mode 100644 splitio/models/grammar/matchers/keys.py delete mode 100644 splitio/models/grammar/matchers/misc.py delete mode 100644 splitio/models/grammar/matchers/numeric.py delete mode 100644 splitio/models/grammar/matchers/prerequisites.py delete mode 100644 splitio/models/grammar/matchers/rule_based_segment.py delete mode 100644 splitio/models/grammar/matchers/semver.py delete mode 100644 splitio/models/grammar/matchers/sets.py delete mode 100644 splitio/models/grammar/matchers/string.py delete mode 100644 splitio/models/grammar/matchers/utils/__init__.py delete mode 100644 splitio/models/grammar/matchers/utils/utils.py delete mode 100644 splitio/models/grammar/partitions.py delete mode 100644 splitio/models/impressions.py create mode 100644 splitio/models/label.py delete mode 100644 splitio/models/notification.py delete mode 100644 splitio/models/rule_based_segments.py delete mode 100644 splitio/models/segments.py delete mode 100644 splitio/models/telemetry.py delete mode 100644 splitio/models/token.py rename tests/{models/grammar => engine}/files/splits_prereq.json (100%) delete mode 100644 tests/models/grammar/files/between-semver.csv delete mode 100644 tests/models/grammar/files/equal-to-semver.csv delete mode 100644 tests/models/grammar/files/invalid-semantic-versions.csv delete mode 100644 tests/models/grammar/files/regex.txt delete mode 100644 tests/models/grammar/files/valid-semantic-versions.csv delete mode 100644 tests/models/grammar/test_conditions.py delete mode 100644 tests/models/grammar/test_matchers.py delete mode 100644 tests/models/grammar/test_partitions.py delete mode 100644 tests/models/grammar/test_semver.py delete mode 100644 tests/models/test_fallback.py delete mode 100644 tests/models/test_notification.py delete mode 100644 tests/models/test_rule_based_segments.py delete mode 100644 tests/models/test_telemetry_model.py delete mode 100644 tests/models/test_token.py diff --git a/splitio/api/auth.py b/splitio/api/auth.py index 986ee31a..fe5d4411 100644 --- a/splitio/api/auth.py +++ b/splitio/api/auth.py @@ -8,8 +8,8 @@ from splitio.spec import SPEC_VERSION from splitio.util.time import get_current_epoch_time_ms from splitio.api.client import HttpClientException -from splitio.models.token import from_raw -from splitio.models.telemetry import HTTPExceptionsAndLatencies +from harness_commons.models.token import from_raw +from harness_commons.models.telemetry import HTTPExceptionsAndLatencies _LOGGER = logging.getLogger(__name__) @@ -39,7 +39,7 @@ def authenticate(self): Perform authentication. :return: Json representation of an authentication. - :rtype: splitio.models.token.Token + :rtype: harness_commons.models.token.Token """ try: response = self._client.get( @@ -86,7 +86,7 @@ async def authenticate(self): Perform authentication. :return: Json representation of an authentication. - :rtype: splitio.models.token.Token + :rtype: harness_commons.models.token.Token """ try: response = await self._client.get( diff --git a/splitio/api/events.py b/splitio/api/events.py index 16beeddc..58c686a8 100644 --- a/splitio/api/events.py +++ b/splitio/api/events.py @@ -3,7 +3,7 @@ from splitio.api import APIException, headers_from_metadata from splitio.api.client import HttpClientException -from splitio.models.telemetry import HTTPExceptionsAndLatencies +from harness_commons.models.telemetry import HTTPExceptionsAndLatencies _LOGGER = logging.getLogger(__name__) @@ -18,7 +18,7 @@ def _build_bulk(events): Build event bulk as expected by the API. :param events: Events to be bundled. - :type events: list(splitio.models.events.Event) + :type events: list(harness_commons.models.events.Event) :return: Formatted bulk. :rtype: dict diff --git a/splitio/api/impressions.py b/splitio/api/impressions.py index da85691b..79244d06 100644 --- a/splitio/api/impressions.py +++ b/splitio/api/impressions.py @@ -6,7 +6,7 @@ from splitio.api import APIException, headers_from_metadata from splitio.api.client import HttpClientException from splitio.engine.impressions import ImpressionsMode -from splitio.models.telemetry import HTTPExceptionsAndLatencies +from harness_commons.models.telemetry import HTTPExceptionsAndLatencies _LOGGER = logging.getLogger(__name__) @@ -21,7 +21,7 @@ def _build_bulk(impressions): Build an impression bulk formatted as the API expects it. :param impressions: List of impressions to bundle. - :type impressions: list(splitio.models.impressions.Impression) + :type impressions: list(harness_commons.models.impressions.Impression) :return: Dictionary of lists of impressions. :rtype: list diff --git a/splitio/api/segments.py b/splitio/api/segments.py index aae33ac6..c9b67a4c 100644 --- a/splitio/api/segments.py +++ b/splitio/api/segments.py @@ -6,7 +6,7 @@ from splitio.api import APIException, headers_from_metadata from splitio.api.commons import build_fetch from splitio.api.client import HttpClientException -from splitio.models.telemetry import HTTPExceptionsAndLatencies +from harness_commons.models.telemetry import HTTPExceptionsAndLatencies _LOGGER = logging.getLogger(__name__) diff --git a/splitio/api/splits.py b/splitio/api/splits.py index 771100fc..0be23069 100644 --- a/splitio/api/splits.py +++ b/splitio/api/splits.py @@ -6,7 +6,7 @@ from splitio.api import APIException, headers_from_metadata from splitio.api.commons import build_fetch, FetchOptions from splitio.api.client import HttpClientException -from splitio.models.telemetry import HTTPExceptionsAndLatencies +from harness_commons.models.telemetry import HTTPExceptionsAndLatencies from splitio.util.time import utctime_ms from splitio.spec import SPEC_VERSION from splitio.sync import util diff --git a/splitio/api/telemetry.py b/splitio/api/telemetry.py index 48f2ad2d..6bae4d3d 100644 --- a/splitio/api/telemetry.py +++ b/splitio/api/telemetry.py @@ -3,7 +3,7 @@ from splitio.api import APIException, headers_from_metadata from splitio.api.client import HttpClientException -from splitio.models.telemetry import HTTPExceptionsAndLatencies +from harness_commons.models.telemetry import HTTPExceptionsAndLatencies _LOGGER = logging.getLogger(__name__) diff --git a/splitio/client/client.py b/splitio/client/client.py index 3c61166d..75a60e3c 100644 --- a/splitio/client/client.py +++ b/splitio/client/client.py @@ -7,9 +7,10 @@ from splitio.client import input_validator from splitio.engine.evaluator import Evaluator, CONTROL, EvaluationDataFactory, AsyncEvaluationDataFactory from splitio.engine.splitters import Splitter -from splitio.models.impressions import Impression, Label, ImpressionDecorated -from splitio.models.events import Event, EventWrapper, SdkEvent -from splitio.models.telemetry import get_latency_bucket_index, MethodExceptionsAndLatencies +from harness_commons.models.impressions import Impression, ImpressionDecorated +from splitio.models.label import Label +from harness_commons.models.events import Event, EventWrapper, SdkEvent +from harness_commons.models.telemetry import get_latency_bucket_index, MethodExceptionsAndLatencies from splitio.optional.loaders import asyncio from splitio.util.time import get_current_epoch_time_ms, utctime_ms @@ -174,7 +175,7 @@ def _validate_track(self, key, traffic_type, event_type, value=None, properties= :type properties: dict :return: validation, event created and its properties size. - :rtype: tuple(bool, splitio.models.events.Event, int) + :rtype: tuple(bool, harness_commons.models.events.Event, int) """ if self.destroyed: _LOGGER.error("Client has already been destroyed - no calls possible") @@ -338,7 +339,7 @@ def _get_treatment(self, method, key, feature, attributes=None, evaluation_optio :param attributes: An optional dictionary of attributes :type attributes: dict :param method: The method calling this function - :type method: splitio.models.telemetry.MethodExceptionsAndLatencies + :type method: harness_commons.models.telemetry.MethodExceptionsAndLatencies :param evaluation_options: An optional dictionary of options :type evaluation_options: dict :return: The treatment and config for the key and feature flag @@ -508,7 +509,7 @@ def _get_treatments_by_flag_sets(self, key, flag_sets, method, attributes=None, :param flag_sets: list of flag sets :type flag_sets: list :param method: Treatment by flag set method flavor - :type method: splitio.models.telemetry.MethodExceptionsAndLatencies + :type method: harness_commons.models.telemetry.MethodExceptionsAndLatencies :param attributes: An optional dictionary of attributes :type attributes: dict :param evaluation_options: An optional dictionary of options @@ -637,7 +638,7 @@ def _get_treatments(self, key, features, method, attributes=None, evaluation_opt :param feature_flag_names: Array of feature flag names for which to get the treatments :type feature_flag_names: list(str) :param method: The method calling this function - :type method: splitio.models.telemetry.MethodExceptionsAndLatencies + :type method: harness_commons.models.telemetry.MethodExceptionsAndLatencies :param attributes: An optional dictionary of attributes :type attributes: dict :param evaluation_options: An optional dictionary of options @@ -688,7 +689,7 @@ def _record_stats(self, impressions_decorated, start, operation): Record impressions. :param impressions_decorated: Generated impressions - :type impressions_decorated: list[tuple[splitio.models.impression.ImpressionDecorated, dict]] + :type impressions_decorated: list[tuple[harness_commons.models.impression.ImpressionDecorated, dict]] :param start: timestamp when get_treatment or get_treatments was called :type start: int @@ -845,7 +846,7 @@ async def _get_treatment(self, method, key, feature, attributes=None, evaluation :param attributes: An optional dictionary of attributes :type attributes: dict :param method: The method calling this function - :type method: splitio.models.telemetry.MethodExceptionsAndLatencies + :type method: harness_commons.models.telemetry.MethodExceptionsAndLatencies :param evaluation_options: An optional dictionary of options :type evaluation_options: dict :return: The treatment and config for the key and feature flag @@ -1013,7 +1014,7 @@ async def _get_treatments_by_flag_sets(self, key, flag_sets, method, attributes= :param flag_sets: list of flag sets :type flag_sets: list :param method: Treatment by flag set method flavor - :type method: splitio.models.telemetry.MethodExceptionsAndLatencies + :type method: harness_commons.models.telemetry.MethodExceptionsAndLatencies :param attributes: An optional dictionary of attributes :type attributes: dict :param evaluation_options: An optional dictionary of options @@ -1057,7 +1058,7 @@ async def _get_treatments(self, key, features, method, attributes=None, evaluati :param feature_flag_names: Array of feature flag names for which to get the treatments :type feature_flag_names: list(str) :param method: The method calling this function - :type method: splitio.models.telemetry.MethodExceptionsAndLatencies + :type method: harness_commons.models.telemetry.MethodExceptionsAndLatencies :param attributes: An optional dictionary of attributes :type attributes: dict :param evaluation_options: An optional dictionary of options @@ -1107,7 +1108,7 @@ async def _record_stats(self, impressions_decorated, start, operation): Record impressions for async calls :param impressions_decorated: Generated impressions decorated - :type impressions_decorated: list[tuple[splitio.models.impression.Impression, dict]] + :type impressions_decorated: list[tuple[harness_commons.models.impression.Impression, dict]] :param start: timestamp when get_treatment or get_treatments was called :type start: int diff --git a/splitio/client/config.py b/splitio/client/config.py index 25b1bc31..c125f28b 100644 --- a/splitio/client/config.py +++ b/splitio/client/config.py @@ -5,7 +5,7 @@ from splitio.engine.impressions import ImpressionsMode from splitio.client.input_validator import validate_flag_sets, validate_fallback_treatment, validate_regex_name -from splitio.models.fallback_config import FallbackTreatmentsConfiguration +from harness_commons.models.fallback_config import FallbackTreatmentsConfiguration _LOGGER = logging.getLogger(__name__) DEFAULT_DATA_SAMPLING = 1 diff --git a/splitio/client/factory.py b/splitio/client/factory.py index 10979b85..d14e3d43 100644 --- a/splitio/client/factory.py +++ b/splitio/client/factory.py @@ -23,9 +23,9 @@ from splitio.events.events_manager_config import EventsManagerConfig from splitio.events.events_task import EventsTask, EventsTaskAsync from splitio.events.events_delivery import EventsDelivery -from splitio.models.fallback_config import FallbackTreatmentCalculator -from splitio.models.notification import SdkInternalEventNotification -from splitio.models.events import SdkInternalEvent +from harness_commons.models.fallback_config import FallbackTreatmentCalculator +from harness_commons.models.notification import SdkInternalEventNotification +from harness_commons.models.events import SdkInternalEvent # Storage from splitio.storage.inmemmory import InMemorySplitStorage, InMemorySegmentStorage, \ diff --git a/splitio/client/input_validator.py b/splitio/client/input_validator.py index dfded942..807ec65d 100644 --- a/splitio/client/input_validator.py +++ b/splitio/client/input_validator.py @@ -8,7 +8,7 @@ from splitio.client.key import Key from splitio.client import client from splitio.engine.evaluator import CONTROL -from splitio.models.fallback_treatment import FallbackTreatment +from harness_commons.models.fallback_treatment import FallbackTreatment _LOGGER = logging.getLogger(__name__) diff --git a/splitio/engine/evaluator.py b/splitio/engine/evaluator.py index b47db5c5..c7c3d6be 100644 --- a/splitio/engine/evaluator.py +++ b/splitio/engine/evaluator.py @@ -2,13 +2,13 @@ import logging from collections import namedtuple -from splitio.models.impressions import Label -from splitio.models.grammar.condition import ConditionType -from splitio.models.grammar.matchers.misc import DependencyMatcher -from splitio.models.grammar.matchers.keys import UserDefinedSegmentMatcher -from splitio.models.grammar.matchers import RuleBasedSegmentMatcher -from splitio.models.grammar.matchers.prerequisites import PrerequisitesMatcher -from splitio.models.rule_based_segments import SegmentType +from splitio.models.label import Label +from harness_commons.models.grammar.condition import ConditionType +from harness_commons.models.grammar.matchers.misc import DependencyMatcher +from harness_commons.models.grammar.matchers.keys import UserDefinedSegmentMatcher +from harness_commons.models.grammar.matchers import RuleBasedSegmentMatcher +from harness_commons.models.grammar.matchers.prerequisites import PrerequisitesMatcher +from harness_commons.models.rule_based_segments import SegmentType from splitio.optional.loaders import asyncio CONTROL = 'control' @@ -252,4 +252,4 @@ def update_objects(fetched, fetched_rbs, splits, rb_segments): return features, rbsegments, splits, rb_segments def get_prerequisites(feature): - return [prerequisite.feature_flag_name for prerequisite in feature.prerequisites] + return [prerequisite.definition_name for prerequisite in feature.prerequisites] diff --git a/splitio/engine/impressions/impressions.py b/splitio/engine/impressions/impressions.py index 428fdd13..0059b4e0 100644 --- a/splitio/engine/impressions/impressions.py +++ b/splitio/engine/impressions/impressions.py @@ -33,10 +33,10 @@ def process_impressions(self, impressions_decorated): Impressions are analyzed to see if they've been seen before and counted. :param impressions_decorated: List of impression objects with attributes - :type impressions_decorated: list[tuple[splitio.models.impression.ImpressionDecorated, dict]] + :type impressions_decorated: list[tuple[harness_commons.models.impression.ImpressionDecorated, dict]] :return: processed and deduped impressions. - :rtype: tuple(list[tuple[splitio.models.impression.Impression, dict]], list(int)) + :rtype: tuple(list[tuple[harness_commons.models.impression.Impression, dict]], list(int)) """ for_listener_all = [] for_log_all = [] diff --git a/splitio/engine/impressions/manager.py b/splitio/engine/impressions/manager.py index 56727fd0..4591aa6b 100644 --- a/splitio/engine/impressions/manager.py +++ b/splitio/engine/impressions/manager.py @@ -2,7 +2,7 @@ from collections import defaultdict, namedtuple from splitio.util.time import utctime_ms -from splitio.models.impressions import Impression +from harness_commons.models.impressions import Impression from splitio.engine.hashfns import murmur_128 from splitio.engine.cache.lru import SimpleLruCache from splitio.optional.loaders import asyncio @@ -28,10 +28,10 @@ def truncate_impressions_time(imps, counter = None): Impressions are truncated based on time :param impressions: List of impression objects with attributes - :type impressions: list[tuple[splitio.models.impression.Impression, dict]] + :type impressions: list[tuple[harness_commons.models.impression.Impression, dict]] :returns: truncated list of impressions - :rtype: list[splitio.models.impression.Impression] + :rtype: list[harness_commons.models.impression.Impression] """ this_hour = truncate_time(utctime_ms()) return [imp for imp, _ in imps] if counter is None \ @@ -61,7 +61,7 @@ def _stringify(self, impression): Stringify an impression. :param impression: Impression to stringify using _PATTERN - :type impression: splitio.models.impressions.Impression + :type impression: harness_commons.models.impressions.Impression :returns: a string representation of the impression :rtype: str @@ -77,7 +77,7 @@ def process(self, impression): Hash an impression. :param impression: Impression to hash. - :type impression: splitio.models.impressions.Impression + :type impression: harness_commons.models.impressions.Impression :returns: a hash of the supplied impression's relevant fields. :rtype: int @@ -98,10 +98,10 @@ def test_and_set(self, impression): Examine an impression to determine and set it's previous time accordingly. :param impression: Impression to track - :type impression: splitio.models.impressions.Impression + :type impression: harness_commons.models.impressions.Impression :returns: Impression with populated previous time - :rtype: splitio.models.impressions.Impression + :rtype: harness_commons.models.impressions.Impression """ previous_time = self._cache.test_and_set(self._hasher.process(impression), impression.time) return Impression(impression.matching_key, @@ -130,7 +130,7 @@ def track(self, impressions, inc=1): Register N new impressions for a feature in a specific timeframe. :param impressions: generated impressions - :type impressions: list[splitio.models.impressions.Impression] + :type impressions: list[harness_commons.models.impressions.Impression] :param inc: amount to increment (defaults to 1) :type inc: int diff --git a/splitio/engine/impressions/strategies.py b/splitio/engine/impressions/strategies.py index c2b0c565..0eda56ab 100644 --- a/splitio/engine/impressions/strategies.py +++ b/splitio/engine/impressions/strategies.py @@ -33,10 +33,10 @@ def process_impressions(self, impressions): Impressions are analyzed to see if they've been seen before. :param impressions: List of impression objects with attributes - :type impressions: list[tuple[splitio.models.impression.Impression, dict]] + :type impressions: list[tuple[harness_commons.models.impression.Impression, dict]] :returns: Tuple of to be stored, observed and counted impressions, and unique keys tuple - :rtype: list[tuple[splitio.models.impression.Impression, dict]], list[], list[], list[] + :rtype: list[tuple[harness_commons.models.impression.Impression, dict]], list[], list[], list[] """ imps = [] for imp, attrs in impressions: @@ -59,10 +59,10 @@ def process_impressions(self, impressions): Unique keys tracking are updated. :param impressions: List of impression objects with attributes - :type impressions: list[tuple[splitio.models.impression.Impression, dict]] + :type impressions: list[tuple[harness_commons.models.impression.Impression, dict]] :returns: Tuple of to be stored, observed and counted impressions, and unique keys tuple - :rtype: list[[], dict]], list[splitio.models.impression.Impression], list[splitio.models.impression.Impression], list[(str, str)] + :rtype: list[[], dict]], list[harness_commons.models.impression.Impression], list[harness_commons.models.impression.Impression], list[(str, str)] """ counter_imps = [imp for imp, _ in impressions] unique_keys_tracker = [] @@ -87,10 +87,10 @@ def process_impressions(self, impressions): Impressions are analyzed to see if they've been seen before and counted. :param impressions: List of impression objects with attributes - :type impressions: list[tuple[splitio.models.impression.Impression, dict]] + :type impressions: list[tuple[harness_commons.models.impression.Impression, dict]] :returns: Tuple of to be stored, observed and counted impressions, and unique keys tuple - :rtype: list[tuple[splitio.models.impression.Impression, dict]], list[splitio.models.impression.Impression], list[splitio.models.impression.Impression], list[] + :rtype: list[tuple[harness_commons.models.impression.Impression, dict]], list[harness_commons.models.impression.Impression], list[harness_commons.models.impression.Impression], list[] """ imps = [] for imp, attrs in impressions: diff --git a/splitio/engine/telemetry.py b/splitio/engine/telemetry.py index f3bbba53..e0bdff71 100644 --- a/splitio/engine/telemetry.py +++ b/splitio/engine/telemetry.py @@ -5,7 +5,7 @@ import logging _LOGGER = logging.getLogger(__name__) -from splitio.models.telemetry import CounterConstants, UpdateFromSSE +from harness_commons.models.telemetry import CounterConstants, UpdateFromSSE class TelemetryStorageProducerBase(object): """Telemetry storage producer base class.""" diff --git a/splitio/events/events_manager.py b/splitio/events/events_manager.py index de8206f1..bd1aa289 100644 --- a/splitio/events/events_manager.py +++ b/splitio/events/events_manager.py @@ -5,7 +5,7 @@ from splitio.optional.loaders import asyncio from splitio.events import EventsManagerInterface -from splitio.models.events import SdkEvent +from harness_commons.models.events import SdkEvent _LOGGER = logging.getLogger(__name__) diff --git a/splitio/events/events_manager_config.py b/splitio/events/events_manager_config.py index b987d380..8a7167f4 100644 --- a/splitio/events/events_manager_config.py +++ b/splitio/events/events_manager_config.py @@ -1,5 +1,5 @@ """Events Manager Configuration.""" -from splitio.models.events import SdkEvent, SdkInternalEvent +from harness_commons.models.events import SdkEvent, SdkInternalEvent class EventsManagerConfig(object): """Events Manager Configurations class.""" diff --git a/splitio/models/__init__.py b/splitio/models/__init__.py deleted file mode 100644 index ea86ed44..00000000 --- a/splitio/models/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -class MatcherNotFoundException(Exception): - """Exception to raise when a matcher is not found.""" - - def __init__(self, custom_message): - """Constructor.""" - Exception.__init__(self, custom_message) \ No newline at end of file diff --git a/splitio/models/datatypes.py b/splitio/models/datatypes.py deleted file mode 100644 index 751c2908..00000000 --- a/splitio/models/datatypes.py +++ /dev/null @@ -1,66 +0,0 @@ -"""Datatypes converters for matchers.""" - - -def ts_truncate_seconds(timestamp): - """ - Set seconds to zero in a timestamp. - - :param ts: Timestamp in seconds. - :type ts: int - - :return: Timestamp in seconds, but without counting them (ie: DD-MM-YY HH:MM:00) - :rtype: int - """ - return timestamp - (timestamp % 60) - - -def ts_truncate_time(timestamp): - """ - Set time to zero in a timestamp. - - :param ts: Timestamp in seconds. - :type ts: int - - :return: Timestamp in seconds, without counting time (ie: DD-MM-YYYY 00:00:00) - :rtype: int - """ - return timestamp - (timestamp % 86400) - - -def java_ts_to_secs(java_ts): - """ - Convert java timestamp into unix timestamp. - - :param java_ts: java timestamp in milliseconds. - :type java_ts: int - - :return: Timestamp in seconds. - :rtype: int - """ - return java_ts / 1000 - - -def java_ts_truncate_seconds(java_ts): - """ - Set seconds to zero in a timestamp. - - :param ts: Timestamp in seconds. - :type ts: int - - :return: Timestamp in seconds, but without counting them (ie: DD-MM-YY HH:MM:00) - :rtype: int - """ - return ts_truncate_seconds(java_ts_to_secs(java_ts)) - - -def java_ts_truncate_time(java_ts): - """ - Set time to zero in a timestamp. - - :param ts: Timestamp in seconds. - :type ts: int - - :return: Timestamp in seconds, without counting time (ie: DD-MM-YYYY 00:00:00) - :rtype: int - """ - return ts_truncate_time(java_ts_to_secs(java_ts)) diff --git a/splitio/models/events.py b/splitio/models/events.py deleted file mode 100644 index 2863d235..00000000 --- a/splitio/models/events.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -Event DTO and Storage classes. - -The dto is implemented as a namedtuple for performance matters. -""" -from collections import namedtuple -from enum import Enum - -Event = namedtuple('Event', [ - 'key', - 'traffic_type_name', - 'event_type_id', - 'value', - 'timestamp', - 'properties', -]) - -EventWrapper = namedtuple('EventWrapper', [ - 'event', - 'size', -]) - -class SdkEvent(Enum): - """Public SDK events""" - - SDK_READY = 'SDK_READY' - SDK_UPDATE = 'SDK_UPDATE' - -class SdkInternalEvent(Enum): - """Internal SDK events""" - - SDK_READY = 'SDK_READY' - 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/splitio/models/fallback_config.py b/splitio/models/fallback_config.py deleted file mode 100644 index ca021bf7..00000000 --- a/splitio/models/fallback_config.py +++ /dev/null @@ -1,100 +0,0 @@ -"""Segment module.""" -from splitio.models.fallback_treatment import FallbackTreatment -from splitio.client.client import CONTROL - -class FallbackTreatmentsConfiguration(object): - """FallbackTreatmentsConfiguration object class.""" - - def __init__(self, global_fallback_treatment=None, by_flag_fallback_treatment=None): - """ - Class constructor. - - :param global_fallback_treatment: global FallbackTreatment. - :type global_fallback_treatment: FallbackTreatment - - :param by_flag_fallback_treatment: Dict of flags and their fallback treatment - :type by_flag_fallback_treatment: {str: FallbackTreatment} - """ - self._global_fallback_treatment = self._build_global_fallback(global_fallback_treatment) - self._by_flag_fallback_treatment = self._build_by_flag_fallback(by_flag_fallback_treatment) - - @property - def global_fallback_treatment(self): - """Return global fallback treatment.""" - return self._global_fallback_treatment - - @global_fallback_treatment.setter - def global_fallback_treatment(self, new_value): - """Set global fallback treatment.""" - self._global_fallback_treatment = new_value - - @property - def by_flag_fallback_treatment(self): - """Return by flag fallback treatment.""" - return self._by_flag_fallback_treatment - - @by_flag_fallback_treatment.setter - def by_flag_fallback_treatment(self, new_value): - """Set global fallback treatment.""" - self.by_flag_fallback_treatment = new_value - - def _build_global_fallback(self, global_fallback_treatment): - if isinstance(global_fallback_treatment, str): - return FallbackTreatment(global_fallback_treatment) - - return global_fallback_treatment - - def _build_by_flag_fallback(self, by_flag_fallback_treatment): - if not isinstance(by_flag_fallback_treatment, dict): - return by_flag_fallback_treatment - - parsed_by_flag_fallback = {} - for key, value in by_flag_fallback_treatment.items(): - if isinstance(value, str): - parsed_by_flag_fallback[key] = FallbackTreatment(value) - else: - parsed_by_flag_fallback[key] = value - - return parsed_by_flag_fallback - -class FallbackTreatmentCalculator(object): - """FallbackTreatmentCalculator object class.""" - - def __init__(self, fallback_treatment_configuration): - """ - Class constructor. - - :param fallback_treatment_configuration: fallback treatment configuration - :type fallback_treatment_configuration: FallbackTreatmentsConfiguration - """ - self._label_prefix = "fallback - " - self._fallback_treatments_configuration = fallback_treatment_configuration - - @property - def fallback_treatments_configuration(self): - """Return fallback treatment configuration.""" - return self._fallback_treatments_configuration - - def resolve(self, flag_name, label): - if self._fallback_treatments_configuration != None: - if self._fallback_treatments_configuration.by_flag_fallback_treatment != None \ - and self._fallback_treatments_configuration.by_flag_fallback_treatment.get(flag_name) != None: - return self._copy_with_label(self._fallback_treatments_configuration.by_flag_fallback_treatment.get(flag_name), \ - self._resolve_label(label)) - - if self._fallback_treatments_configuration.global_fallback_treatment != None: - return self._copy_with_label(self._fallback_treatments_configuration.global_fallback_treatment, \ - self._resolve_label(label)) - - return FallbackTreatment(CONTROL, None, label) - - def _resolve_label(self, label): - if label == None: - return None - - return self._label_prefix + label - - def _copy_with_label(self, fallback_treatment, label): - return FallbackTreatment(fallback_treatment.treatment, fallback_treatment.config, label) - - \ No newline at end of file diff --git a/splitio/models/fallback_treatment.py b/splitio/models/fallback_treatment.py deleted file mode 100644 index 794cbb63..00000000 --- a/splitio/models/fallback_treatment.py +++ /dev/null @@ -1,34 +0,0 @@ -"""Segment module.""" -import json - -class FallbackTreatment(object): - """FallbackTreatment object class.""" - - def __init__(self, treatment, config=None, label=None): - """ - Class constructor. - - :param treatment: treatment. - :type treatment: str - - :param config: config. - :type config: json - """ - self._treatment = treatment - self._config = config - self._label = label - - @property - def treatment(self): - """Return treatment.""" - return self._treatment - - @property - def config(self): - """Return config.""" - return self._config - - @property - def label(self): - """Return label prefix.""" - return self._label \ No newline at end of file diff --git a/splitio/models/grammar/__init__.py b/splitio/models/grammar/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/splitio/models/grammar/condition.py b/splitio/models/grammar/condition.py deleted file mode 100644 index 79fdb928..00000000 --- a/splitio/models/grammar/condition.py +++ /dev/null @@ -1,136 +0,0 @@ -"""Split conditions module.""" - -from enum import Enum - -from splitio.models import MatcherNotFoundException -from splitio.models.grammar import matchers -from splitio.models.grammar import partitions - -_MATCHER_COMBINERS = { - 'AND': lambda ms, k, a, c: all(m.evaluate(k, a, c) for m in ms) -} - - -class ConditionType(Enum): - """Feature Flag possible condition types.""" - - WHITELIST = 'WHITELIST' - ROLLOUT = 'ROLLOUT' - - -class Condition(object): - """Condition object class.""" - - def __init__( # pylint: disable=too-many-arguments - self, - matcher_list, - combiner, parts, label, - condition_type=ConditionType.WHITELIST - ): - """ - Class constructor. - - :param matcher: A combining matcher - :type matcher: CombiningMatcher - :param parts: A list of partitions - :type parts: list - """ - self._matchers = matcher_list - self._combiner = combiner - self._partitions = tuple(parts) - self._label = label - self._condition_type = condition_type - - @property - def matchers(self): - """Return the list of matchers associated to the condition.""" - return self._matchers - - @property - def partitions(self): - """Return the list of partitions associated with the condition.""" - return self._partitions - - @property - def label(self): - """Return the label of this condition.""" - return self._label - - @property - def condition_type(self): - """Return the condition type.""" - return self._condition_type - - def matches(self, key, attributes=None, context=None): - """ - Check whether the condition matches against user submitted input. - - :param key: User key - :type key: splitio.client.key.Key - :param attributes: User custom attributes. - :type attributes: dict - :param context: Evaluation context - :type context: dict - """ - return self._combiner(self._matchers, key, attributes, context) - - def get_segment_names(self): - """ - Fetch segment names for all IN_SEGMENT matchers. - - :return: List of segment names - :rtype: list(str) - """ - return [ - matcher._segment_name for matcher in self.matchers # pylint: disable=protected-access - if isinstance(matcher, matchers.UserDefinedSegmentMatcher) - ] - - def __str__(self): - """Return the string representation of the condition.""" - return '{matcher} then split {parts}'.format( - matcher=self._matchers, parts=','.join( - '{size}:{treatment}'.format(size=partition.size, - treatment=partition.treatment) - for partition in self._partitions)) - - def to_json(self): - """Return the JSON representation of this condition.""" - return { - 'conditionType': self._condition_type.name, - 'label': self._label, - 'matcherGroup': { - 'combiner': next( - (k, v) for k, v in _MATCHER_COMBINERS.items() if v == self._combiner - )[0], - 'matchers': [m.to_json() for m in self.matchers] - }, - 'partitions': [p.to_json() for p in self.partitions] - } - - -def from_raw(raw_condition): - """ - Parse a condition from a JSON portion of splitChanges. - - :param raw_condition: JSON object extracted from a feature flag's conditions array. - :type raw_condition: dict - - :return: A condition object. - :rtype: Condition - """ - parsed_partitions = [] - if raw_condition.get("partitions") is not None: - parsed_partitions = [ - partitions.from_raw(raw_partition) - for raw_partition in raw_condition['partitions'] - ] - - matcher_objects = [matchers.from_raw(x) for x in raw_condition['matcherGroup']['matchers']] - - combiner = _MATCHER_COMBINERS[raw_condition['matcherGroup']['combiner']] - label = raw_condition.get('label') - - condition_type = ConditionType(raw_condition.get('conditionType', ConditionType.WHITELIST)) - - return Condition(matcher_objects, combiner, parsed_partitions, label, condition_type) diff --git a/splitio/models/grammar/matchers/__init__.py b/splitio/models/grammar/matchers/__init__.py deleted file mode 100644 index def75626..00000000 --- a/splitio/models/grammar/matchers/__init__.py +++ /dev/null @@ -1,82 +0,0 @@ -"""Matchers entrypoint module.""" -from splitio.models import MatcherNotFoundException -from splitio.models.grammar.matchers.keys import AllKeysMatcher, UserDefinedSegmentMatcher -from splitio.models.grammar.matchers.numeric import BetweenMatcher, EqualToMatcher, \ - GreaterThanOrEqualMatcher, LessThanOrEqualMatcher -from splitio.models.grammar.matchers.sets import ContainsAllOfSetMatcher, \ - ContainsAnyOfSetMatcher, EqualToSetMatcher, PartOfSetMatcher -from splitio.models.grammar.matchers.string import ContainsStringMatcher, \ - EndsWithMatcher, RegexMatcher, StartsWithMatcher, WhitelistMatcher -from splitio.models.grammar.matchers.misc import BooleanMatcher, DependencyMatcher -from splitio.models.grammar.matchers.semver import EqualToSemverMatcher, GreaterThanOrEqualToSemverMatcher, LessThanOrEqualToSemverMatcher, \ - BetweenSemverMatcher, InListSemverMatcher -from splitio.models.grammar.matchers.rule_based_segment import RuleBasedSegmentMatcher - - -MATCHER_TYPE_ALL_KEYS = 'ALL_KEYS' -MATCHER_TYPE_IN_SEGMENT = 'IN_SEGMENT' -MATCHER_TYPE_WHITELIST = 'WHITELIST' -MATCHER_TYPE_EQUAL_TO = 'EQUAL_TO' -MATCHER_TYPE_GREATER_THAN_OR_EQUAL_TO = 'GREATER_THAN_OR_EQUAL_TO' -MATCHER_TYPE_LESS_THAN_OR_EQUAL_TO = 'LESS_THAN_OR_EQUAL_TO' -MATCHER_TYPE_BETWEEN = 'BETWEEN' -MATCHER_TYPE_EQUAL_TO_SET = 'EQUAL_TO_SET' -MATCHER_TYPE_PART_OF_SET = 'PART_OF_SET' -MATCHER_TYPE_CONTAINS_ALL_OF_SET = 'CONTAINS_ALL_OF_SET' -MATCHER_TYPE_CONTAINS_ANY_OF_SET = 'CONTAINS_ANY_OF_SET' -MATCHER_TYPE_STARTS_WITH = 'STARTS_WITH' -MATCHER_TYPE_ENDS_WITH = 'ENDS_WITH' -MATCHER_TYPE_CONTAINS_STRING = 'CONTAINS_STRING' -MATCHER_TYPE_IN_SPLIT_TREATMENT = 'IN_SPLIT_TREATMENT' -MATCHER_TYPE_EQUAL_TO_BOOLEAN = 'EQUAL_TO_BOOLEAN' -MATCHER_TYPE_MATCHES_STRING = 'MATCHES_STRING' -MATCHER_TYPE_EQUAL_TO_SEMVER = 'EQUAL_TO_SEMVER' -MATCHER_GREATER_THAN_OR_EQUAL_TO_SEMVER = 'GREATER_THAN_OR_EQUAL_TO_SEMVER' -MATCHER_LESS_THAN_OR_EQUAL_TO_SEMVER = 'LESS_THAN_OR_EQUAL_TO_SEMVER' -MATCHER_BETWEEN_SEMVER = 'BETWEEN_SEMVER' -MATCHER_INLIST_SEMVER = 'IN_LIST_SEMVER' -MATCHER_IN_RULE_BASED_SEGMENT = 'IN_RULE_BASED_SEGMENT' - - -_MATCHER_BUILDERS = { - MATCHER_TYPE_ALL_KEYS: AllKeysMatcher, - MATCHER_TYPE_IN_SEGMENT: UserDefinedSegmentMatcher, - MATCHER_TYPE_WHITELIST: WhitelistMatcher, - MATCHER_TYPE_EQUAL_TO: EqualToMatcher, - MATCHER_TYPE_GREATER_THAN_OR_EQUAL_TO: GreaterThanOrEqualMatcher, - MATCHER_TYPE_LESS_THAN_OR_EQUAL_TO: LessThanOrEqualMatcher, - MATCHER_TYPE_BETWEEN: BetweenMatcher, - MATCHER_TYPE_EQUAL_TO_SET: EqualToSetMatcher, - MATCHER_TYPE_PART_OF_SET: PartOfSetMatcher, - MATCHER_TYPE_CONTAINS_ALL_OF_SET: ContainsAllOfSetMatcher, - MATCHER_TYPE_CONTAINS_ANY_OF_SET: ContainsAnyOfSetMatcher, - MATCHER_TYPE_STARTS_WITH: StartsWithMatcher, - MATCHER_TYPE_ENDS_WITH: EndsWithMatcher, - MATCHER_TYPE_CONTAINS_STRING: ContainsStringMatcher, - MATCHER_TYPE_IN_SPLIT_TREATMENT: DependencyMatcher, - MATCHER_TYPE_EQUAL_TO_BOOLEAN: BooleanMatcher, - MATCHER_TYPE_MATCHES_STRING: RegexMatcher, - MATCHER_TYPE_EQUAL_TO_SEMVER: EqualToSemverMatcher, - MATCHER_GREATER_THAN_OR_EQUAL_TO_SEMVER: GreaterThanOrEqualToSemverMatcher, - MATCHER_LESS_THAN_OR_EQUAL_TO_SEMVER: LessThanOrEqualToSemverMatcher, - MATCHER_BETWEEN_SEMVER: BetweenSemverMatcher, - MATCHER_INLIST_SEMVER: InListSemverMatcher, - MATCHER_IN_RULE_BASED_SEGMENT: RuleBasedSegmentMatcher -} - -def from_raw(raw_matcher): - """ - Parse a condition from a JSON portion of splitChanges. - - :param raw_matcher: JSON object extracted from a condition's matcher array. - :type raw_matcher: dict - - :return: A concrete Matcher object. - :rtype: Matcher - """ - matcher_type = raw_matcher['matcherType'] - try: - builder = _MATCHER_BUILDERS[matcher_type] - except KeyError: - raise MatcherNotFoundException('Invalid matcher type %s' % matcher_type) - return builder(raw_matcher) diff --git a/splitio/models/grammar/matchers/base.py b/splitio/models/grammar/matchers/base.py deleted file mode 100644 index 57d0feb5..00000000 --- a/splitio/models/grammar/matchers/base.py +++ /dev/null @@ -1,122 +0,0 @@ -"""Abstract matcher module.""" -import abc - -from splitio.client.key import Key - - -class Matcher(object, metaclass=abc.ABCMeta): - """Matcher abstract class.""" - - def __init__(self, raw_matcher): - """ - Initialize generic data and call matcher-specific parser. - - :param raw_matcher: raw matcher as read from splitChanges response. - :type raw_matcher: dict - - :returns: A concrete matcher object. - :rtype: Matcher - """ - self._negate = raw_matcher['negate'] - self._matcher_type = raw_matcher['matcherType'] - key_selector = raw_matcher.get('keySelector') - if key_selector is not None and 'attribute' in key_selector: - self._attribute_name = raw_matcher['keySelector']['attribute'] - else: - self._attribute_name = None - self._build(raw_matcher) - - def _get_matcher_input(self, key, attributes=None): - """ - Examine split, attributes & key, and return the appropriate matching input. - - :param key: User-submitted key - :type key: str | Key - :param attributes: User-submitted attributes - :type attributes: dict - - :returns: data to use when matching - :rtype: str | set | int | bool - """ - if self._attribute_name is not None: - if attributes is not None and attributes.get(self._attribute_name) is not None: - return attributes[self._attribute_name] - - return None - - if isinstance(key, Key): - return key.matching_key - - return key - - @abc.abstractmethod - def _build(self, raw_matcher): - """ - Build the final matcher according to matcher specific data. - - :param raw_matcher: raw matcher as read from splitChanges response. - :type raw_matcher: dict - """ - pass - - @abc.abstractmethod - def _match(self, key, attributes=None, context=None): - """ - Evaluate user input against matcher and return whether the match is successful. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - - :returns: Wheter the match is successful. - :rtype: bool - """ - pass - - def evaluate(self, key, attributes=None, context=None): - """ - Perform the actual evaluation taking into account possible matcher negation. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - """ - return self._negate ^ self._match(key, attributes, context) - - @abc.abstractmethod - def _add_matcher_specific_properties_to_json(self): - """ - Add matcher specific properties to base dict before returning it. - - :return: Dictionary with matcher specific prooperties. - :rtype: dict - """ - pass - - def to_json(self): - """ - Reconstruct the original JSON representation of the matcher. - - :return: JSON representation of a matcher. - :rtype: dict - """ - base = { - "keySelector": {'attribute': self._attribute_name} if self._attribute_name else None, - "matcherType": self._matcher_type, - "negate": self._negate, - "userDefinedSegmentMatcherData": None, - "whitelistMatcherData": None, - "unaryNumericMatcherData": None, - "betweenMatcherData": None, - "dependencyMatcherData": None, - "booleanMatcherData": None, - "stringMatcherData": None, - } - base.update(self._add_matcher_specific_properties_to_json()) - return base diff --git a/splitio/models/grammar/matchers/keys.py b/splitio/models/grammar/matchers/keys.py deleted file mode 100644 index 0d719310..00000000 --- a/splitio/models/grammar/matchers/keys.py +++ /dev/null @@ -1,86 +0,0 @@ -"""Keys matchers module.""" -from splitio.models.grammar.matchers.base import Matcher - - -class AllKeysMatcher(Matcher): - """A matcher that always returns True.""" - - def _build(self, raw_matcher): - """ - Build an AllKeysMatcher. - - :param raw_matcher: raw matcher as fetched from splitChanges response. - :type raw_matcher: dict - """ - pass - - def _match(self, key, attributes=None, context=None): - """ - Evaluate user input against a matcher and return whether the match is successful. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - - :returns: Wheter the match is successful. - :rtype: bool - """ - return key is not None - - def __str__(self): - """Return string Representation.""" - return 'in segment all' - - def _add_matcher_specific_properties_to_json(self): - """Add matcher specific properties to base dict before returning it.""" - return {} - - -class UserDefinedSegmentMatcher(Matcher): - """Matcher that returns true when the submitted key belongs to a segment.""" - - def _build(self, raw_matcher): - """ - Build an UserDefinedSegmentMatcher. - - :param raw_matcher: raw matcher as fetched from splitChanges response. - :type raw_matcher: dict - """ - self._segment_name = raw_matcher['userDefinedSegmentMatcherData']['segmentName'] - - def _match(self, key, attributes=None, context=None): - """ - Evaluate user input against a matcher and return whether the match is successful. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - - :returns: Wheter the match is successful. - :rtype: bool - """ - matching_data = self._get_matcher_input(key, attributes) - if matching_data is None: - return False - - return context['ec'].segment_memberships[self._segment_name] - - def _add_matcher_specific_properties_to_json(self): - """Return UserDefinedSegment specific properties.""" - return { - 'userDefinedSegmentMatcherData': { - 'segmentName': self._segment_name - } - } - - def __str__(self): - """Return string Representation.""" - return 'in segment {segment_name}'.format( - segment_name=self._segment_name - ) diff --git a/splitio/models/grammar/matchers/misc.py b/splitio/models/grammar/matchers/misc.py deleted file mode 100644 index 1f52c1fa..00000000 --- a/splitio/models/grammar/matchers/misc.py +++ /dev/null @@ -1,99 +0,0 @@ -"""Miscelaneous matchers that don't fall into other categories.""" -import json - -from splitio.models.grammar.matchers.base import Matcher - - -class DependencyMatcher(Matcher): - """Matcher that returns true if the user's key secondary evaluation result matches.""" - - def _build(self, raw_matcher): - """ - Build an DependencyMatcher. - - :param raw_matcher: raw matcher as fetched from splitChanges response. - :type raw_matcher: dict - """ - self._split_name = raw_matcher['dependencyMatcherData']['split'] - self._treatments = raw_matcher['dependencyMatcherData']['treatments'] - - def _match(self, key, attributes=None, context=None): - """ - Evaluate user input against a matcher and return whether the match is successful. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - - :returns: Wheter the match is successful. - :rtype: bool - """ - evaluator = context.get('evaluator') - assert evaluator is not None - - bucketing_key = context.get('bucketing_key') - result = evaluator.eval_with_context(key, bucketing_key, self._split_name, attributes, context['ec']) - return result['treatment'] in self._treatments - - def _add_matcher_specific_properties_to_json(self): - """Return Dependency specific properties.""" - return { - 'dependencyMatcherData': { - 'split': self._split_name, - 'treatments': self._treatments - } - } - - -class BooleanMatcher(Matcher): - """Matcher that returns true if the user submited value is similar to the stored boolean.""" - - def _build(self, raw_matcher): - """ - Build an BooleanMatcher. - - :param raw_matcher: raw matcher as fetched from splitChanges response. - :type raw_matcher: dict - """ - self._data = raw_matcher['booleanMatcherData'] - - def _match(self, key, attributes=None, context=None): - """ - Evaluate user input against a matcher and return whether the match is successful. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - - :returns: Wheter the match is successful. - :rtype: bool - """ - matching_data = self._get_matcher_input(key, attributes) - if matching_data is None: - return False - - if isinstance(matching_data, bool): - decoded = matching_data - elif isinstance(matching_data, str): - try: - decoded = json.loads(matching_data.lower()) - if not isinstance(decoded, bool): - return False - - except ValueError: - return False - - else: - return False - - return decoded == self._data - - def _add_matcher_specific_properties_to_json(self): - """Return Boolean specific properties.""" - return {'booleanMatcherData': self._data} diff --git a/splitio/models/grammar/matchers/numeric.py b/splitio/models/grammar/matchers/numeric.py deleted file mode 100644 index c39fabd7..00000000 --- a/splitio/models/grammar/matchers/numeric.py +++ /dev/null @@ -1,256 +0,0 @@ -"""Numeric & Date based matchers.""" -import numbers -import logging - -from splitio.models.grammar.matchers.base import Matcher -from splitio.models import datatypes - - -_LOGGER = logging.getLogger(__name__) - - -class Sanitizer(object): # pylint: disable=too-few-public-methods - """Numeric input sanitizer.""" - - @classmethod - def ensure_int(cls, data): - """ - Do a best effort attempt to conver input to a int. - - :param input: user supplied input. - :type input: mixed. - """ - if data is None: # Failed to fetch attribute. no need to convert. - return None - - # For some reason bool is considered an integral type. We want to avoid True - # to be converted to 1, and False to 0 on numeric matchers since it can be - # misleading. - if isinstance(data, numbers.Integral) and not isinstance(data, bool): - return data - - if not isinstance(data, str): - _LOGGER.error('Cannot convert %s to int. Failing.', type(data)) - return None - - _LOGGER.warning( - 'Supplied attribute is of type %s and should have been an int. ', - type(data) - ) - - try: - return int(data) - except ValueError: - _LOGGER.error('Cannot convert %s to int. Failing.', type(data)) - return None - - -class ZeroSecondDataMatcher(object): # pylint: disable=too-few-public-methods - """Mixin to use in matchers that when dealing with datetimes, truncate seconds.""" - - data_parsers = { - 'NUMBER': lambda x: x, - 'DATETIME': datatypes.java_ts_truncate_seconds - } - - input_parsers = { - 'NUMBER': lambda x: x, - 'DATETIME': datatypes.ts_truncate_seconds - } - - -class ZeroTimeDataMatcher(object): # pylint: disable=no-init,too-few-public-methods - """Mixin to use in matchers that when dealing with datetimes, truncate time.""" - - input_parsers = { - 'NUMBER': lambda x: x, - 'DATETIME': datatypes.ts_truncate_time - } - - data_parsers = { - 'NUMBER': lambda x: x, - 'DATETIME': datatypes.java_ts_truncate_time - } - - -class BetweenMatcher(Matcher, ZeroSecondDataMatcher): - """Matcher that returns true if user input is within a specified range.""" - - def _build(self, raw_matcher): - """ - Build InBetweenMatcher. - - :param raw_matcher: raw matcher as fetched from splitChanges response. - :type raw_matcher: dict - """ - self._data_type = raw_matcher['betweenMatcherData']['dataType'] - self._original_lower = raw_matcher['betweenMatcherData']['start'] - self._original_upper = raw_matcher['betweenMatcherData']['end'] - self._lower = self.data_parsers[self._data_type](self._original_lower) - self._upper = self.data_parsers[self._data_type](self._original_upper) - - def _match(self, key, attributes=None, context=None): - """ - Evaluate user input against matcher and return whether the match is successful. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - - :returns: Wheter the match is successful. - :rtype: bool - """ - matching_data = Sanitizer.ensure_int(self._get_matcher_input(key, attributes)) - if matching_data is None: - return False - - return self._lower <= self.input_parsers[self._data_type](matching_data) <= self._upper - - def __str__(self): - """Return string Representation.""" - return 'between {start} and {end}'.format(start=self._lower, end=self._upper) - - def _add_matcher_specific_properties_to_json(self): - """Return BetweenMatcher specific properties.""" - return { - 'betweenMatcherData': { - 'dataType': self._data_type, - 'start': self._original_lower, - 'end': self._original_upper - } - } - - -class EqualToMatcher(Matcher, ZeroTimeDataMatcher): - """Return true if the provided input is equal to the value stored in the matcher.""" - - def _build(self, raw_matcher): - """ - Build EqualToMatcher. - - :param raw_matcher: raw matcher as fetched from splitChanges response. - :type raw_matcher: dict - """ - self._data_type = raw_matcher['unaryNumericMatcherData']['dataType'] - self._original_value = raw_matcher['unaryNumericMatcherData']['value'] - self._value = self.data_parsers[self._data_type](self._original_value) - - def _match(self, key, attributes=None, context=None): - """ - Evaluate user input against a matcher and return whether the match is successful. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - - :returns: Wheter the match is successful. - :rtype: bool - """ - matching_data = Sanitizer.ensure_int(self._get_matcher_input(key, attributes)) - if matching_data is None: - return False - - return self.input_parsers[self._data_type](matching_data) == self._value - - def _add_matcher_specific_properties_to_json(self): - """Return EqualTo specific properties.""" - return { - 'unaryNumericMatcherData': { - 'dataType': self._data_type, - 'value': self._original_value, - } - } - - -class GreaterThanOrEqualMatcher(Matcher, ZeroSecondDataMatcher): - """Return true if the provided input is >= the value stored in the matcher.""" - - def _build(self, raw_matcher): - """ - Build GreaterThanOrEqualMatcher. - - :param raw_matcher: raw matcher as fetched from splitChanges response. - :type raw_matcher: dict - """ - self._data_type = raw_matcher['unaryNumericMatcherData']['dataType'] - self._original_value = raw_matcher['unaryNumericMatcherData']['value'] - self._value = self.data_parsers[self._data_type](self._original_value) - - def _match(self, key, attributes=None, context=None): - """ - Evaluate user input against a matcher and return whether the match is successful. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - - :returns: Wheter the match is successful. - :rtype: bool - """ - matching_data = Sanitizer.ensure_int(self._get_matcher_input(key, attributes)) - if matching_data is None: - return False - - return self.input_parsers[self._data_type](matching_data) >= self._value - - def _add_matcher_specific_properties_to_json(self): - """Return GreaterThan specific properties.""" - return { - 'unaryNumericMatcherData': { - 'dataType': self._data_type, - 'value': self._original_value, - } - } - - -class LessThanOrEqualMatcher(Matcher, ZeroSecondDataMatcher): - """Return true if the provided input is <= the value stored in the matcher.""" - - def _build(self, raw_matcher): - """ - Build LessThanOrEqualMatcher. - - :param raw_matcher: raw matcher as fetched from splitChanges response. - :type raw_matcher: dict - """ - self._data_type = raw_matcher['unaryNumericMatcherData']['dataType'] - self._original_value = raw_matcher['unaryNumericMatcherData']['value'] - self._value = self.data_parsers[self._data_type](self._original_value) - - def _match(self, key, attributes=None, context=None): - """ - Evaluate user input against a matcher and return whether the match is successful. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - - :returns: Wheter the match is successful. - :rtype: bool - """ - matching_data = Sanitizer.ensure_int(self._get_matcher_input(key, attributes)) - if matching_data is None: - return False - - return self.input_parsers[self._data_type](matching_data) <= self._value - - def _add_matcher_specific_properties_to_json(self): - """Return LessThan specific properties.""" - return { - 'unaryNumericMatcherData': { - 'dataType': self._data_type, - 'value': self._original_value, - } - } diff --git a/splitio/models/grammar/matchers/prerequisites.py b/splitio/models/grammar/matchers/prerequisites.py deleted file mode 100644 index 799df5c4..00000000 --- a/splitio/models/grammar/matchers/prerequisites.py +++ /dev/null @@ -1,38 +0,0 @@ -"""Prerequisites matcher classes.""" - -class PrerequisitesMatcher(object): - - def __init__(self, prerequisites): - """ - Build a PrerequisitesMatcher. - - :param prerequisites: prerequisites - :type raw_matcher: List of Prerequisites - """ - self._prerequisites = prerequisites - - def match(self, key, attributes=None, context=None): - """ - Evaluate user input against a matcher and return whether the match is successful. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - - :returns: Wheter the match is successful. - :rtype: bool - """ - if self._prerequisites == None: - return True - - evaluator = context.get('evaluator') - bucketing_key = context.get('bucketing_key') - for prerequisite in self._prerequisites: - result = evaluator.eval_with_context(key, bucketing_key, prerequisite.feature_flag_name, attributes, context['ec']) - if result['treatment'] not in prerequisite.treatments: - return False - - return True \ No newline at end of file diff --git a/splitio/models/grammar/matchers/rule_based_segment.py b/splitio/models/grammar/matchers/rule_based_segment.py deleted file mode 100644 index 6e4c8023..00000000 --- a/splitio/models/grammar/matchers/rule_based_segment.py +++ /dev/null @@ -1,72 +0,0 @@ -"""Rule based segment matcher classes.""" -from splitio.models.grammar.matchers.base import Matcher -from splitio.models.rule_based_segments import SegmentType - -class RuleBasedSegmentMatcher(Matcher): - - def _build(self, raw_matcher): - """ - Build an RuleBasedSegmentMatcher. - - :param raw_matcher: raw matcher as fetched from splitChanges response. - :type raw_matcher: dict - """ - self._rbs_segment_name = raw_matcher['userDefinedSegmentMatcherData']['segmentName'] - - def _match(self, key, attributes=None, context=None): - """ - Evaluate user input against a matcher and return whether the match is successful. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - - :returns: Wheter the match is successful. - :rtype: bool - """ - if self._rbs_segment_name == None: - return False - - rb_segment = context['ec'].rbs_segments.get(self._rbs_segment_name) - - if key in rb_segment.excluded.get_excluded_keys(): - return False - - if self._match_dep_rb_segments(rb_segment.excluded.get_excluded_segments(), key, attributes, context): - return False - - return self._match_conditions(rb_segment.conditions, key, attributes, context) - - def _add_matcher_specific_properties_to_json(self): - """Return UserDefinedSegment specific properties.""" - return { - 'userDefinedSegmentMatcherData': { - 'segmentName': self._rbs_segment_name - } - } - - def _match_conditions(self, rbs_segment_conditions, key, attributes, context): - for parsed_condition in rbs_segment_conditions: - if parsed_condition.matches(key, attributes, context): - return True - - return False - - def _match_dep_rb_segments(self, excluded_rb_segments, key, attributes, context): - for excluded_rb_segment in excluded_rb_segments: - if excluded_rb_segment.type == SegmentType.STANDARD: - if context['ec'].segment_memberships[excluded_rb_segment.name]: - return True - else: - excluded_segment = context['ec'].rbs_segments.get(excluded_rb_segment.name) - if key in excluded_segment.excluded.get_excluded_keys(): - return False - - if self._match_dep_rb_segments(excluded_segment.excluded.get_excluded_segments(), key, attributes, context) \ - or self._match_conditions(excluded_segment.conditions, key, attributes, context): - return True - - return False diff --git a/splitio/models/grammar/matchers/semver.py b/splitio/models/grammar/matchers/semver.py deleted file mode 100644 index 46ccf01d..00000000 --- a/splitio/models/grammar/matchers/semver.py +++ /dev/null @@ -1,260 +0,0 @@ -"""Semver matcher classes.""" -import logging - -from splitio.models.grammar.matchers.base import Matcher -from splitio.models.grammar.matchers.string import Sanitizer -from splitio.models.grammar.matchers.utils.utils import build_semver_or_none - - -_LOGGER = logging.getLogger(__name__) - - -class EqualToSemverMatcher(Matcher): - """A matcher for Semver equal to.""" - - def _build(self, raw_matcher): - """ - Build an EqualToSemverMatcher. - - :param raw_matcher: raw matcher as fetched from splitChanges response. - :type raw_matcher: dict - """ - self._data = raw_matcher.get('stringMatcherData') - self._semver = build_semver_or_none(raw_matcher.get('stringMatcherData')) - - def _match(self, key, attributes=None, context=None): - """ - Evaluate user input against a matcher and return whether the match is successful. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - - :returns: Wheter the match is successful. - :rtype: bool - """ - if self._semver is None: - _LOGGER.error("stringMatcherData is required for EQUAL_TO_SEMVER matcher type") - return False - - matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes)) - if matching_data is None: - return False - - matching_semver = build_semver_or_none(matching_data) - if matching_semver is None: - return False - - return self._semver.version == matching_semver.version - - def __str__(self): - """Return string Representation.""" - return f'equal semver {self._data}' - - def _add_matcher_specific_properties_to_json(self): - """Add matcher specific properties to base dict before returning it.""" - return {'matcherType': 'EQUAL_TO_SEMVER', 'stringMatcherData': self._data} - -class GreaterThanOrEqualToSemverMatcher(Matcher): - """A matcher for Semver greater than or equal to.""" - - def _build(self, raw_matcher): - """ - Build a GreaterThanOrEqualToSemverMatcher. - - :param raw_matcher: raw matcher as fetched from splitChanges response. - :type raw_matcher: dict - """ - self._data = raw_matcher.get('stringMatcherData') - self._semver = build_semver_or_none(raw_matcher.get('stringMatcherData')) - - def _match(self, key, attributes=None, context=None): - """ - Evaluate user input against a matcher and return whether the match is successful. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - - :returns: Wheter the match is successful. - :rtype: bool - """ - if self._semver is None: - _LOGGER.error("stringMatcherData is required for GREATER_THAN_OR_EQUAL_TO_SEMVER matcher type") - return False - - matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes)) - if matching_data is None: - return False - - matching_semver = build_semver_or_none(matching_data) - if matching_semver is None: - return False - - return matching_semver.compare(self._semver) in [0, 1] - - def __str__(self): - """Return string Representation.""" - return f'greater than or equal to semver {self._data}' - - def _add_matcher_specific_properties_to_json(self): - """Add matcher specific properties to base dict before returning it.""" - return {'matcherType': 'GREATER_THAN_OR_EQUAL_TO_SEMVER', 'stringMatcherData': self._data} - - -class LessThanOrEqualToSemverMatcher(Matcher): - """A matcher for Semver less than or equal to.""" - - def _build(self, raw_matcher): - """ - Build a LessThanOrEqualToSemverMatcher. - - :param raw_matcher: raw matcher as fetched from splitChanges response. - :type raw_matcher: dict - """ - self._data = raw_matcher.get('stringMatcherData') - self._semver = build_semver_or_none(raw_matcher.get('stringMatcherData')) - - def _match(self, key, attributes=None, context=None): - """ - Evaluate user input against a matcher and return whether the match is successful. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - - :returns: Wheter the match is successful. - :rtype: bool - """ - if self._semver is None: - _LOGGER.error("stringMatcherData is required for LESS_THAN_OR_EQUAL_TO_SEMVER matcher type") - return False - - matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes)) - if matching_data is None: - return False - - matching_semver = build_semver_or_none(matching_data) - if matching_semver is None: - return False - - return matching_semver.compare(self._semver) in [0, -1] - - def __str__(self): - """Return string Representation.""" - return f'less than or equal to semver {self._data}' - - def _add_matcher_specific_properties_to_json(self): - """Add matcher specific properties to base dict before returning it.""" - return {'matcherType': 'LESS_THAN_OR_EQUAL_TO_SEMVER', 'stringMatcherData': self._data} - - -class BetweenSemverMatcher(Matcher): - """A matcher for Semver between.""" - - def _build(self, raw_matcher): - """ - Build a BetweenSemverMatcher. - - :param raw_matcher: raw matcher as fetched from splitChanges response. - :type raw_matcher: dict - """ - self._data = raw_matcher.get('betweenStringMatcherData') - self._semver_start = build_semver_or_none(self._data['start']) - self._semver_end = build_semver_or_none(self._data['end']) - - def _match(self, key, attributes=None, context=None): - """ - Evaluate user input against a matcher and return whether the match is successful. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - - :returns: Wheter the match is successful. - :rtype: bool - """ - if self._semver_start is None or self._semver_end is None: - _LOGGER.error("betweenStringMatcherData is required for BETWEEN_SEMVER matcher type") - return False - - matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes)) - if matching_data is None: - return False - - matching_semver = build_semver_or_none(matching_data) - if matching_semver is None: - return False - - return (self._semver_start.compare(matching_semver) in [0, -1]) and (self._semver_end.compare(matching_semver) in [0, 1]) - - def __str__(self): - """Return string Representation.""" - return 'between semver {start} and {end}'.format(start=self._data.get('start'), end=self._data.get('end')) - - def _add_matcher_specific_properties_to_json(self): - """Add matcher specific properties to base dict before returning it.""" - return {'matcherType': 'BETWEEN_SEMVER', 'betweenStringMatcherData': self._data} - - -class InListSemverMatcher(Matcher): - """A matcher for Semver in list.""" - - def _build(self, raw_matcher): - """ - Build a InListSemverMatcher. - - :param raw_matcher: raw matcher as fetched from splitChanges response. - :type raw_matcher: dict - """ - self._data = raw_matcher['whitelistMatcherData']['whitelist'] - semver_list = [build_semver_or_none(item) for item in self._data if item] - self._semver_list = frozenset([item.version for item in semver_list if item]) - - def _match(self, key, attributes=None, context=None): - """ - Evaluate user input against a matcher and return whether the match is successful. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - - :returns: Wheter the match is successful. - :rtype: bool - """ - if self._semver_list is None: - _LOGGER.error("whitelistMatcherData is required for IN_LIST_SEMVER matcher type") - return False - - matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes)) - if matching_data is None: - return False - - matching_semver = build_semver_or_none(matching_data) - if matching_semver is None: - return False - - return matching_semver.version in self._semver_list - - def __str__(self): - """Return string Representation.""" - return 'in list semver {data}'.format(data=self._data) - - def _add_matcher_specific_properties_to_json(self): - """Add matcher specific properties to base dict before returning it.""" - return {'matcherType': 'IN_LIST_SEMVER', 'whitelistMatcherData': {'whitelist': self._data}} diff --git a/splitio/models/grammar/matchers/sets.py b/splitio/models/grammar/matchers/sets.py deleted file mode 100644 index f46970b4..00000000 --- a/splitio/models/grammar/matchers/sets.py +++ /dev/null @@ -1,208 +0,0 @@ -"""Set based matchers module.""" -from splitio.models.grammar.matchers.base import Matcher - - -class ContainsAllOfSetMatcher(Matcher): - """Matcher that returns true if the user data is a subset of the matcher's data.""" - - def _build(self, raw_matcher): - """ - Build an ContainsAllOfSetMatcher. - - :param raw_matcher: raw matcher as fetched from splitChanges response. - :type raw_matcher: dict - """ - self._whitelist = frozenset(raw_matcher['whitelistMatcherData']['whitelist']) - - def _match(self, key, attributes=None, context=None): - """ - Evaluate user input against a matcher and return whether the match is successful. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - - :returns: Wheter the match is successful. - :rtype: bool - """ - matching_data = self._get_matcher_input(key, attributes) - if matching_data is None: - return False - - try: - setkey = set(matching_data) - return self._whitelist.issubset(setkey) - - except TypeError: - return False - - def _add_matcher_specific_properties_to_json(self): - """Return ContainsAllOfSet specific properties.""" - return { - 'whitelistMatcherData': { - 'whitelist': list(list(self._whitelist)) - } - } - - def __str__(self): - """Return string Representation.""" - return 'contains all of the following set: [{whitelist}]'.format( - whitelist=','.join('"{}"'.format(item) for item in self._whitelist) - ) - - -class ContainsAnyOfSetMatcher(Matcher): - """Matcher that returns true if the intersection of both sets is not empty.""" - - def _build(self, raw_matcher): - """ - Build an ContainsAnyOfSetMatcher. - - :param raw_matcher: raw matcher as fetched from splitChanges response. - :type raw_matcher: dict - """ - self._whitelist = frozenset(raw_matcher['whitelistMatcherData']['whitelist']) - - def _match(self, key, attributes=None, context=None): - """ - Evaluate user input against a matcher and return whether the match is successful. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - - :returns: Wheter the match is successful. - :rtype: bool - """ - matching_data = self._get_matcher_input(key, attributes) - if matching_data is None: - return False - - try: - return len(self._whitelist.intersection(set(matching_data))) != 0 - - except TypeError: - return False - - def _add_matcher_specific_properties_to_json(self): - """Return ContainsAnyOfSet specific properties.""" - return { - 'whitelistMatcherData': { - 'whitelist': list(self._whitelist) - } - } - - def __str__(self): - """Return string Representation.""" - return 'contains on of the following se: [{whitelist}]'.format( - whitelist=','.join('"{}"'.format(item) for item in self._whitelist) - ) - - -class EqualToSetMatcher(Matcher): - """Matcher that returns true if the set provided by the user is equal to the matcher's one.""" - - def _build(self, raw_matcher): - """ - Build an EqualToSetMatcher. - - :param raw_matcher: raw matcher as fetched from splitChanges response. - :type raw_matcher: dict - """ - self._whitelist = frozenset(raw_matcher['whitelistMatcherData']['whitelist']) - - def _match(self, key, attributes=None, context=None): - """ - Evaluate user input against a matcher and return whether the match is successful. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - - :returns: Wheter the match is successful. - :rtype: bool - """ - matching_data = self._get_matcher_input(key, attributes) - if matching_data is None: - return False - - try: - return self._whitelist == set(matching_data) - - except TypeError: - return False - - def _add_matcher_specific_properties_to_json(self): - """Return EqualToSet specific properties.""" - return { - 'whitelistMatcherData': { - 'whitelist': list(self._whitelist) - } - } - - def __str__(self): - """Return string Representation.""" - return 'equals the following set: [{whitelist}]'.format( - whitelist=','.join('"{}"'.format(item) for item in self._whitelist) - ) - - -class PartOfSetMatcher(Matcher): - """a.""" - - def _build(self, raw_matcher): - """ - Build an PartOfSetMatcher. - - :param raw_matcher: raw matcher as fetched from splitChanges response. - :type raw_matcher: dict - """ - self._whitelist = frozenset(raw_matcher['whitelistMatcherData']['whitelist']) - - def _match(self, key, attributes=None, context=None): - """ - Evaluate user input against a matcher and return whether the match is successful. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - - :returns: Wheter the match is successful. - :rtype: bool - """ - matching_data = self._get_matcher_input(key, attributes) - if matching_data is None: - return False - - try: - setkey = set(matching_data) - return len(setkey) > 0 and setkey.issubset(set(self._whitelist)) - - except TypeError: - return False - - def _add_matcher_specific_properties_to_json(self): - """Return PartOfSet specific properties.""" - return { - 'whitelistMatcherData': { - 'whitelist': list(self._whitelist) - } - } - - def __str__(self): - """Return string Representation.""" - return 'is a subset of the following set: [{whitelist}]'.format( - whitelist=','.join('"{}"'.format(item) for item in self._whitelist) - ) diff --git a/splitio/models/grammar/matchers/string.py b/splitio/models/grammar/matchers/string.py deleted file mode 100644 index 1a820b21..00000000 --- a/splitio/models/grammar/matchers/string.py +++ /dev/null @@ -1,274 +0,0 @@ -"""String matchers module.""" -import logging -import json -import re - -from splitio.models.grammar.matchers.base import Matcher - - -_LOGGER = logging.getLogger(__name__) - - -class Sanitizer(object): # pylint: disable=too-few-public-methods - """Numeric input sanitizer.""" - - @classmethod - def ensure_string(cls, data): - """ - Do a best effort attempt to conver input to a string. - - :param input: user supplied input. - :type input: mixed. - - :return: String or None - :rtype: string - """ - if data is None: # Failed to fetch attribute. no need to convert. - return None - - if isinstance(data, str): - return data - - _LOGGER.warning( - 'Supplied attribute is of type %s and should have been a string. ', - type(data) - ) - try: - return json.dumps(data) - - except TypeError: - return None - - -class WhitelistMatcher(Matcher): - """Matcher that returns true if the user key is within a whitelist.""" - - def _build(self, raw_matcher): - """ - Build an WhitelistMatcher. - - :param raw_matcher: raw matcher as fetched from splitChanges response. - :type raw_matcher: dict - """ - self._whitelist = frozenset(raw_matcher['whitelistMatcherData']['whitelist']) - - def _match(self, key, attributes=None, context=None): - """ - Evaluate user input against a matcher and return whether the match is successful. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - - :returns: Wheter the match is successful. - :rtype: bool - """ - matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes)) - if matching_data is None: - return False - - return matching_data in self._whitelist - - def _add_matcher_specific_properties_to_json(self): - """Return Whitelist specific properties.""" - return { - 'whitelistMatcherData': { - 'whitelist': list(self._whitelist) - } - } - - def __str__(self): - """Return string Representation.""" - return 'in whitelist [{whitelist}]'.format( - whitelist=','.join('"{}"'.format(item) for item in self._whitelist) - ) - - -class StartsWithMatcher(Matcher): - """Matcher that returns true if the key is a prefix of the stored value.""" - - def _build(self, raw_matcher): - """ - Build an StartsWithMatcher. - - :param raw_matcher: raw matcher as fetched from splitChanges response. - :type raw_matcher: dict - """ - self._whitelist = frozenset(raw_matcher['whitelistMatcherData']['whitelist']) - - def _match(self, key, attributes=None, context=None): - """ - Evaluate user input against a matcher and return whether the match is successful. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - - :returns: Wheter the match is successful. - :rtype: bool - """ - matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes)) - if matching_data is None: - return False - - return (isinstance(key, str) and - any(matching_data.startswith(s) for s in self._whitelist)) - - def _add_matcher_specific_properties_to_json(self): - """Return StartsWith specific properties.""" - return { - 'whitelistMatcherData': { - 'whitelist': list(self._whitelist) - } - } - - def __str__(self): - """Return string Representation.""" - return 'has one of the following prefixes [{whitelist}]'.format( - whitelist=','.join('"{}"'.format(item) for item in self._whitelist) - ) - - -class EndsWithMatcher(Matcher): - """Matcher that returns true if the key ends with the suffix stored in matcher data.""" - - def _build(self, raw_matcher): - """ - Build an EndsWithMatcher. - - :param raw_matcher: raw matcher as fetched from splitChanges response. - :type raw_matcher: dict - """ - self._whitelist = frozenset(raw_matcher['whitelistMatcherData']['whitelist']) - - def _match(self, key, attributes=None, context=None): - """ - Evaluate user input against a matcher and return whether the match is successful. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - - :returns: Wheter the match is successful. - :rtype: bool - """ - matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes)) - if matching_data is None: - return False - - return (isinstance(key, str) and - any(matching_data.endswith(s) for s in self._whitelist)) - - def _add_matcher_specific_properties_to_json(self): - """Return EndsWith specific properties.""" - return { - 'whitelistMatcherData': { - 'whitelist': list(self._whitelist) - } - } - - def __str__(self): - """Return string Representation.""" - return 'has one of the following suffixes [{whitelist}]'.format( - whitelist=','.join('"{}"'.format(item) for item in self._whitelist) - ) - - -class ContainsStringMatcher(Matcher): - """Matcher that returns true if the input key is part of the string in matcher data.""" - - def _build(self, raw_matcher): - """ - Build a ContainsStringMatcher. - - :param raw_matcher: raw matcher as fetched from splitChanges response. - :type raw_matcher: dict - """ - self._whitelist = frozenset(raw_matcher['whitelistMatcherData']['whitelist']) - - def _match(self, key, attributes=None, context=None): - """ - Evaluate user input against a matcher and return whether the match is successful. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - - :returns: Wheter the match is successful. - :rtype: bool - """ - matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes)) - if matching_data is None: - return False - - return (isinstance(matching_data, str) and - any(s in matching_data for s in self._whitelist)) - - def _add_matcher_specific_properties_to_json(self): - """Return ContainsString specific properties.""" - return { - 'whitelistMatcherData': { - 'whitelist': list(self._whitelist) - } - } - - def __str__(self): - """Return string Representation.""" - return 'contains one of the following string: [{whitelist}]'.format( - whitelist=','.join('"{}"'.format(item) for item in self._whitelist) - ) - - -class RegexMatcher(Matcher): - """Matcher that returns true if the user input matches the regex stored in the matcher.""" - - def _build(self, raw_matcher): - """ - Build a RegexMatcher. - - :param raw_matcher: raw matcher as fetched from splitChanges response. - :type raw_matcher: dict - """ - self._data = raw_matcher['stringMatcherData'] - self._regex = re.compile(self._data) - - def _match(self, key, attributes=None, context=None): - """ - Evaluate user input against a matcher and return whether the match is successful. - - :param key: User key. - :type key: str. - :param attributes: Custom user attributes. - :type attributes: dict. - :param context: Evaluation context - :type context: dict - - :returns: Wheter the match is successful. - :rtype: bool - """ - matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes)) - if matching_data is None: - return False - - try: - matches = re.search(self._regex, matching_data) - return matches is not None - - except TypeError: - return False - - def _add_matcher_specific_properties_to_json(self): - """Return Regex specific properties.""" - return {'stringMatcherData': self._data} diff --git a/splitio/models/grammar/matchers/utils/__init__.py b/splitio/models/grammar/matchers/utils/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/splitio/models/grammar/matchers/utils/utils.py b/splitio/models/grammar/matchers/utils/utils.py deleted file mode 100644 index d0b2e727..00000000 --- a/splitio/models/grammar/matchers/utils/utils.py +++ /dev/null @@ -1,168 +0,0 @@ -"""Utils module.""" - -import logging - -_LOGGER = logging.getLogger(__name__) - -M_DELIMITER = "+" -P_DELIMITER = "-" -V_DELIMITER = "." - - -def compare(var1, var2): - """ - Compare 2 variables and return int as follows: - 0: if var1 == var2 - 1: if var1 > var2 - -1: if var1 < var2 - - :param var1: any object accept ==, < or > operators - :type var1: str/int - :param var2: any object accept ==, < or > operators - :type var2: str/int - - :returns: integer based on comparison - :rtype: int - """ - if var1 == var2: - return 0 - if var1 > var2: - return 1 - return -1 - - -def build_semver_or_none(version): - try: - return Semver(version) - except (RuntimeError, ValueError): - _LOGGER.error("Invalid semver version: %s", version) - return None - - -class Semver(object): - """Semver class.""" - - def __init__(self, version): - """ - Class Initializer - - :param version: raw version as read from splitChanges response. - :type version: str - """ - self._major = 0 - self._minor = 0 - self._patch = 0 - self._pre_release = [] - self._is_stable = False - self._version = "" - self._metadata = "" - self._parse(version) - - def _parse(self, version): - """ - Parse the string in self.version to update the other internal variables - """ - without_metadata = self._extract_metadata(version) - index = without_metadata.find(P_DELIMITER) - if index == -1: - self._is_stable = True - else: - pre_release_data = without_metadata[index+1:] - if pre_release_data == "": - raise RuntimeError("Pre-release is empty despite delimiter exists: " + version) - - without_metadata = without_metadata[:index] - for pre_digit in pre_release_data.split(V_DELIMITER): - if pre_digit.isnumeric(): - pre_digit = str(int(pre_digit)) - self._pre_release.append(pre_digit) - - self._set_components(without_metadata) - - def _extract_metadata(self, version): - """ - Check if there is any metadata characters in self.version. - - :returns: The semver string without the metadata - :rtype: str - """ - index = version.find(M_DELIMITER) - if index == -1: - return version - - self._metadata = version[index+1:] - if self._metadata == "": - raise RuntimeError("Metadata is empty despite delimiter exists: " + version) - - return version[:index] - - def _set_components(self, version): - """ - Set the major, minor and patch internal variables based on string passed. - - :param version: raw version containing major.minor.patch numbers. - :type version: str - """ - - parts = version.split(V_DELIMITER) - if len(parts) != 3: - raise RuntimeError("Unable to convert to Semver, incorrect format: " + version) - try: - self._major, self._minor, self._patch = int(parts[0]), int(parts[1]), int(parts[2]) - self._version = f"{self._major}{V_DELIMITER}{self._minor}{V_DELIMITER}{self._patch}" - self._version += f"{P_DELIMITER + V_DELIMITER.join(self._pre_release) if len(self._pre_release) > 0 else ''}" - self._version += f"{M_DELIMITER + self._metadata if self._metadata else ''}" - except Exception: - raise RuntimeError("Unable to convert to Semver, incorrect format: " + version) - - @property - def version(self): - return self._version - - def compare(self, to_compare): - """ - Compare the current Semver object to a given Semver object, return: - 0: if self == passed - 1: if self > passed - -1: if self < passed - - :param to_compare: a Semver object - :type to_compare: splitio.models.grammar.matchers.semver.Semver - - :returns: integer based on comparison - :rtype: int - """ - if self.version == to_compare.version: - return 0 - - # Compare major, minor, and patch versions numerically - result = compare(self._major, to_compare._major) - if result != 0: - return result - - result = compare(self._minor, to_compare._minor) - if result != 0: - return result - - result = compare(self._patch, to_compare._patch) - if result != 0: - return result - - if not self._is_stable and to_compare._is_stable: - return -1 - elif self._is_stable and not to_compare._is_stable: - return 1 - - # Compare pre-release versions lexically - min_length = min(len(self._pre_release), len(to_compare._pre_release)) - for i in range(min_length): - if self._pre_release[i] == to_compare._pre_release[i]: - continue - - if self._pre_release[i].isnumeric() and to_compare._pre_release[i].isnumeric(): - return compare(int(self._pre_release[i]), int(to_compare._pre_release[i])) - - return compare(self._pre_release[i], to_compare._pre_release[i]) - - # Compare lengths of pre-release versions - return compare(len(self._pre_release), len(to_compare._pre_release)) diff --git a/splitio/models/grammar/partitions.py b/splitio/models/grammar/partitions.py deleted file mode 100644 index 51f84ac6..00000000 --- a/splitio/models/grammar/partitions.py +++ /dev/null @@ -1,55 +0,0 @@ -"""Split partition module.""" - - -class Partition(object): - """Partition object class.""" - - def __init__(self, treatment, size): - """ - Class constructor. - - :param treatment: The treatment for the partition - :type treatment: str - :param size: A number between 0 a 100 - :type size: float - """ - if size < 0 or size > 100: - raise ValueError('size MUST BE between 0 and 100') - - self._treatment = treatment - self._size = size - - @property - def treatment(self): - """Return the treatment associated with this partition.""" - return self._treatment - - @property - def size(self): - """Return the percentage owned by this partition.""" - return self._size - - def to_json(self): - """Return a JSON representation of a partition.""" - return { - 'treatment': self._treatment, - 'size': self._size - } - - def __str__(self): - """Return string representation of a partition.""" - return '{size}%:{treatment}'.format(size=self._size, - treatment=self._treatment) - - -def from_raw(raw_partition): - """ - Build a partition object from a splitChanges partition portion. - - :param raw_partition: JSON snippet of a partition. - :type raw_partition: dict - - :return: New partition object. - :rtype: Partition - """ - return Partition(raw_partition['treatment'], raw_partition['size']) diff --git a/splitio/models/impressions.py b/splitio/models/impressions.py deleted file mode 100644 index 0c6d50f7..00000000 --- a/splitio/models/impressions.py +++ /dev/null @@ -1,68 +0,0 @@ -"""Impressions model module.""" -from collections import namedtuple - - -Impression = namedtuple( - 'Impression', - [ - 'matching_key', - 'feature_name', - 'treatment', - 'label', - 'change_number', - 'bucketing_key', - 'time', - 'previous_time', - 'properties' - ] -) - -ImpressionDecorated = namedtuple( - 'ImpressionDecorated', - [ - 'Impression', - 'disabled' - ] -) - -# pre-python3.7 hack to make previous_time optional -Impression.__new__.__defaults__ = (None,) - - -class Label(object): # pylint: disable=too-few-public-methods - """Impressions labels.""" - - # Condition: Split Was Killed - # Treatment: Default treatment - # Label: killed - KILLED = 'killed' - - # Condition: No condition matched - # Treatment: Default Treatment - # Label: no condition matched - NO_CONDITION_MATCHED = 'default rule' - - # Condition: Split definition was not found - # Treatment: control - # Label: split not found - SPLIT_NOT_FOUND = 'definition not found' - - # Condition: Traffic allocation failed - # Treatment: Default Treatment - # Label: not in split - NOT_IN_SPLIT = 'not in split' - - # Condition: There was an exception - # Treatment: control - # Label: exception - EXCEPTION = 'exception' - - # Condition: Evaluation requested while client not ready - # Treatment: control - # Label: not ready - NOT_READY = 'not ready' - - # Condition: Prerequisites not met - # Treatment: Default treatment - # Label: prerequisites not met - PREREQUISITES_NOT_MET = "prerequisites not met" diff --git a/splitio/models/label.py b/splitio/models/label.py new file mode 100644 index 00000000..cb8ab398 --- /dev/null +++ b/splitio/models/label.py @@ -0,0 +1,16 @@ +"""Impressions model module.""" +from collections import namedtuple +from harness_commons.models.impressions import Label as CommonsLabel + +class Label(CommonsLabel): # pylint: disable=too-few-public-methods + """Impressions labels.""" + + # Condition: Definition definition was not found + # Treatment: control + # Label: Definition not found + SPLIT_NOT_FOUND = 'split not found' + + # Condition: Traffic allocation failed + # Treatment: Default Treatment + # Label: not in Definition + NOT_IN_SPLIT = 'not in split' \ No newline at end of file diff --git a/splitio/models/notification.py b/splitio/models/notification.py deleted file mode 100644 index 60b629e1..00000000 --- a/splitio/models/notification.py +++ /dev/null @@ -1,227 +0,0 @@ -"""Notification Module""" - -import json - -from enum import Enum - - -class Type(Enum): - """Notification Type.""" - - SPLIT_UPDATE = 'SPLIT_UPDATE' - SPLIT_KILL = 'SPLIT_KILL' - SEGMENT_UPDATE = 'SEGMENT_UPDATE' - CONTROL = 'CONTROL' - - -class Control(Enum): - """Control Type.""" - - STREAMING_PAUSED = 'STREAMING_PAUSED' - STREAMING_RESUMED = 'STREAMING_RESUMED' - STREAMING_DISABLED = 'STREAMING_DISABLED' - - -class ControlNotification(object): # pylint: disable=too-many-instance-attributes - """ControlNotification model object.""" - - def __init__(self, channel, notification_type, control_type): - """ - Class constructor. - - :param channel: Channel of incoming notification - :type channel: str - :param notification_type: Type of incoming notification - :type notification_type: str - :param control_type: Control type of incoming CONTROL notification. - :type control_type: str - - """ - self._channel = channel - self._notification_type = Type(notification_type) - self._control_type = Control(control_type) - - @property - def channel(self): - return self._channel - - @property - def control_type(self): - return self._control_type - - @property - def notification_type(self): - return self._notification_type - - -class SegmentChangeNotification(object): # pylint: disable=too-many-instance-attributes - """SegmentChangeNotification model object.""" - - def __init__(self, channel, notification_type, change_number, segment_name): - """ - Class constructor. - - :param channel: Channel of incoming notification - :type channel: str - :param notification_type: Type of incoming notification - :type notification_type: str - :param change_number: ChangeNumber of incoming notification. - :type change_number: int - :param segment_name: Segment Name of incoming notification. - :type segment_name: str - - """ - self._channel = channel - self._notification_type = Type(notification_type) - self._change_number = change_number - self._segment_name = segment_name - - @property - def channel(self): - return self._channel - - @property - def change_number(self): - return self._change_number - - @property - def notification_type(self): - return self._notification_type - - @property - def segment_name(self): - return self._segment_name - - -class SplitChangeNotification(object): # pylint: disable=too-many-instance-attributes - """SplitChangeNotification model object.""" - - def __init__(self, channel, notification_type, change_number): - """ - Class constructor. - - :param channel: Channel of incoming notification - :type channel: str - :param notification_type: Type of incoming notification - :type notification_type: str - :param change_number: ChangeNumber of incoming notification. - :type change_number: int - - """ - self._channel = channel - self._notification_type = Type(notification_type) - self._change_number = change_number - - @property - def channel(self): - return self._channel - - @property - def change_number(self): - return self._change_number - - @property - def notification_type(self): - return self._notification_type - - -class SplitKillNotification(object): # pylint: disable=too-many-instance-attributes - """SplitKillNotification model object.""" - - def __init__(self, channel, notification_type, change_number, default_treatment, split_name): - """ - Class constructor. - - :param channel: Channel of incoming notification - :type channel: str - :param notification_type: Type of incoming notification - :type notification_type: str - :param change_number: ChangeNumber of incoming notification. - :type change_number: int - :param default_treatment: Default treatment of incoming SPLIT_KILL notification. - :type default_treatment: str - :param split_name: Split Name of incoming SPLIT or SPLIT_KILL notification. - :type split_name: str - - """ - self._channel = channel - self._notification_type = Type(notification_type) - self._change_number = change_number - self._default_treatment = default_treatment - self._split_name = split_name - - @property - def channel(self): - return self._channel - - @property - def change_number(self): - return self._change_number - - @property - def default_treatment(self): - return self._default_treatment - - @property - def notification_type(self): - return self._notification_type - - @property - def split_name(self): - return self._split_name - -class SdkInternalEventNotification(object): # pylint: disable=too-many-instance-attributes - """SdkInternalEventNotification model object.""" - - def __init__(self, internal_event, metadata): - """ - Class constructor. - - :param internal_event: internal event object - :type channel: SdkInternalEvent - :param metadata: metadata associated with event - :type change_number: EventsMetadata - - """ - self._internal_event = internal_event - self._metadata = metadata - - @property - def internal_event(self): - return self._internal_event - - @property - def metadata(self): - return self._metadata - -_NOTIFICATION_MAPPERS = { - Type.SPLIT_UPDATE: lambda c, d: SplitChangeNotification(c, Type.SPLIT_UPDATE, d['changeNumber']), - Type.SPLIT_KILL: lambda c, d: SplitKillNotification(c, Type.SPLIT_KILL, d['changeNumber'], d['defaultTreatment'], d['splitName']), - Type.SEGMENT_UPDATE: lambda c, d: SegmentChangeNotification(c, Type.SEGMENT_UPDATE, d['changeNumber'], d['segmentName']), - Type.CONTROL: lambda c, d: ControlNotification(c, Type.CONTROL, d['controlType']) -} - - -def wrap_notification(raw_data, channel): - """ - Parse notification from raw notification payload - - :param raw_data: data - :type raw_data: str - :param channel: Channel of incoming notification - :type channel: str - """ - try: - if channel is None: - raise ValueError("channel cannot be None.") - raw_data = json.loads(raw_data) - notification_type = Type(raw_data['type']) - mapper = _NOTIFICATION_MAPPERS[notification_type] - return mapper(channel, raw_data) - - except ValueError: - raise ValueError("Wrong notification type received.") - except KeyError: - raise KeyError("Could not parse notification.") - except TypeError: - raise TypeError("Wrong JSON format.") diff --git a/splitio/models/rule_based_segments.py b/splitio/models/rule_based_segments.py deleted file mode 100644 index f7bf3f4d..00000000 --- a/splitio/models/rule_based_segments.py +++ /dev/null @@ -1,195 +0,0 @@ -"""RuleBasedSegment module.""" - -from enum import Enum -import logging - -from splitio.models import MatcherNotFoundException -from splitio.models.splits import _DEFAULT_CONDITIONS_TEMPLATE -from splitio.models.grammar import condition -from splitio.models.splits import Status - -_LOGGER = logging.getLogger(__name__) - -class SegmentType(Enum): - """Segment type.""" - - STANDARD = "standard" - RULE_BASED = "rule-based" - -class RuleBasedSegment(object): - """RuleBasedSegment object class.""" - - def __init__(self, name, traffic_type_name, change_number, status, conditions, excluded): - """ - Class constructor. - - :param name: Segment name. - :type name: str - :param traffic_type_name: traffic type name. - :type traffic_type_name: str - :param change_number: change number. - :type change_number: str - :param status: status. - :type status: str - :param conditions: List of conditions belonging to the segment. - :type conditions: List - :param excluded: excluded objects. - :type excluded: Excluded - """ - self._name = name - self._traffic_type_name = traffic_type_name - self._change_number = change_number - self._conditions = conditions - self._excluded = excluded - try: - self._status = Status(status) - except ValueError: - self._status = Status.ARCHIVED - - @property - def name(self): - """Return segment name.""" - return self._name - - @property - def traffic_type_name(self): - """Return traffic type name.""" - return self._traffic_type_name - - @property - def change_number(self): - """Return change number.""" - return self._change_number - - @property - def status(self): - """Return status.""" - return self._status - - @property - def conditions(self): - """Return conditions.""" - return self._conditions - - @property - def excluded(self): - """Return excluded.""" - return self._excluded - - def to_json(self): - """Return a JSON representation of this rule based segment.""" - return { - 'changeNumber': self.change_number, - 'trafficTypeName': self.traffic_type_name, - 'name': self.name, - 'status': self.status.value, - 'conditions': [c.to_json() for c in self.conditions], - 'excluded': self.excluded.to_json() - } - - def get_condition_segment_names(self): - segments = set() - for condition in self._conditions: - for matcher in condition.matchers: - if matcher._matcher_type == 'IN_SEGMENT': - segments.add(matcher.to_json()['userDefinedSegmentMatcherData']['segmentName']) - return segments - -def from_raw(raw_rule_based_segment): - """ - Parse a Rule based segment from a JSON portion of splitChanges. - - :param raw_rule_based_segment: JSON object extracted from a splitChange's response - :type raw_rule_based_segment: dict - - :return: A parsed RuleBasedSegment object capable of performing evaluations. - :rtype: RuleBasedSegment - """ - try: - conditions = [condition.from_raw(c) for c in raw_rule_based_segment['conditions']] - except MatcherNotFoundException as e: - _LOGGER.error(str(e)) - _LOGGER.debug("Using default conditions template for feature flag: %s", raw_rule_based_segment['name']) - conditions = [condition.from_raw(_DEFAULT_CONDITIONS_TEMPLATE)] - - if raw_rule_based_segment.get('excluded') == None: - raw_rule_based_segment['excluded'] = {'keys': [], 'segments': []} - - if raw_rule_based_segment['excluded'].get('keys') == None: - raw_rule_based_segment['excluded']['keys'] = [] - - if raw_rule_based_segment['excluded'].get('segments') == None: - raw_rule_based_segment['excluded']['segments'] = [] - - return RuleBasedSegment( - raw_rule_based_segment['name'], - raw_rule_based_segment['trafficTypeName'], - raw_rule_based_segment['changeNumber'], - raw_rule_based_segment['status'], - conditions, - Excluded(raw_rule_based_segment['excluded']['keys'], raw_rule_based_segment['excluded']['segments']) - ) - -class Excluded(object): - - def __init__(self, keys, segments): - """ - Class constructor. - - :param keys: List of excluded keys in a rule based segment. - :type keys: List - :param segments: List of excluded segments in a rule based segment. - :type segments: List - """ - self._keys = keys - self._segments = [ExcludedSegment(segment['name'], segment['type']) for segment in segments] - - def get_excluded_keys(self): - """Return excluded keys.""" - return self._keys - - def get_excluded_segments(self): - """Return excluded segments""" - return self._segments - - def get_excluded_standard_segments(self): - """Return excluded segments""" - to_return = [] - for segment in self._segments: - if segment.type == SegmentType.STANDARD: - to_return.append(segment.name) - return to_return - - def to_json(self): - """Return a JSON representation of this object.""" - return { - 'keys': self._keys, - 'segments': self._segments - } - -class ExcludedSegment(object): - - def __init__(self, name, type): - """ - Class constructor. - - :param name: rule based segment name - :type name: str - :param type: segment type - :type type: str - """ - self._name = name - try: - self._type = SegmentType(type) - except ValueError: - self._type = SegmentType.STANDARD - - @property - def name(self): - """Return name.""" - return self._name - - @property - def type(self): - """Return type.""" - return self._type diff --git a/splitio/models/segments.py b/splitio/models/segments.py deleted file mode 100644 index 3d7ab5c9..00000000 --- a/splitio/models/segments.py +++ /dev/null @@ -1,86 +0,0 @@ -"""Segment module.""" - - -class Segment(object): - """Segment object class.""" - - def __init__(self, name, keys, change_number): - """ - Class constructor. - - :param name: Segment name. - :type name: str - - :param keys: List of keys belonging to the segment. - :type keys: List - """ - self._name = name - self._keys = set(keys) - self._change_number = change_number - - @property - def name(self): - """Return segment name.""" - return self._name - - def contains(self, key): - """ - Return whether the supplied key belongs to the segment. - - :param key: User key. - :type key: str - - :return: True if the user is in the segment. False otherwise. - :rtype: bool - """ - return key in self._keys - - def update(self, to_add, to_remove): - """ - Add supplied keys to the segment. - - :param to_add: List of keys to add. - :type to_add: list - :param to_remove: List of keys to remove. - :type to_remove: list - """ - self._keys = self._keys.union(set(to_add)).difference(to_remove) - - @property - def keys(self): - """ - Return the segment keys. - - :return: A set of the segment keys - :rtype: set - """ - return self._keys - - @property - def change_number(self): - """Return segment change number.""" - return self._change_number - - @change_number.setter - def change_number(self, new_value): - """ - Set new change number. - - :param new_value: New change number. - :type new_value: int - """ - self._change_number = new_value - - -def from_raw(raw_segment): - """ - Parse a new segment from a raw segment_changes response. - - :param raw_segment: Segment parsed from segment changes response. - :type raw_segment: dict - - :return: New segment model object - :rtype: splitio.models.segment.Segment - """ - keys = set(raw_segment['added']).difference(raw_segment['removed']) - return Segment(raw_segment['name'], keys, raw_segment['till']) diff --git a/splitio/models/splits.py b/splitio/models/splits.py index 47e69284..aea28b5f 100644 --- a/splitio/models/splits.py +++ b/splitio/models/splits.py @@ -3,8 +3,9 @@ from collections import namedtuple import logging -from splitio.models import MatcherNotFoundException -from splitio.models.grammar import condition +from harness_commons.models import MatcherNotFoundException, _DEFAULT_CONDITIONS_TEMPLATE, Status, HashAlgorithm, Prerequisites +from harness_commons.models.grammar import condition +from harness_commons.models.definitions import Definition, Prerequisites, Status, from_raw_prerequisites _LOGGER = logging.getLogger(__name__) @@ -13,71 +14,7 @@ ['name', 'traffic_type', 'killed', 'treatments', 'change_number', 'configs', 'default_treatment', 'sets', 'impressions_disabled', 'prerequisites'] ) -_DEFAULT_CONDITIONS_TEMPLATE = { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": None, - "matcherType": "ALL_KEYS", - "negate": False, - "userDefinedSegmentMatcherData": None, - "whitelistMatcherData": None, - "unaryNumericMatcherData": None, - "betweenMatcherData": None, - "dependencyMatcherData": None, - "booleanMatcherData": None, - "stringMatcherData": None - }] - }, - "partitions": [ - { - "treatment": "control", - "size": 100 - } - ], - "label": "targeting rule type unsupported by sdk" -} - -class Prerequisites(object): - """Prerequisites.""" - def __init__(self, feature_flag_name, treatments): - self._feature_flag_name = feature_flag_name - self._treatments = treatments - - @property - def feature_flag_name(self): - """Return featur eflag name.""" - return self._feature_flag_name - - @property - def treatments(self): - """Return treatments.""" - return self._treatments - - def to_json(self): - to_return = [] - for feature_flag_name in self._feature_flag_name: - to_return.append({"n": feature_flag_name, "ts": [treatment for treatment in self._treatments]}) - - return to_return - -class Status(Enum): - """Split status.""" - - ACTIVE = "ACTIVE" - ARCHIVED = "ARCHIVED" - - -class HashAlgorithm(Enum): - """Hash algorithm names.""" - - LEGACY = 1 - MURMUR = 2 - - -class Split(object): # pylint: disable=too-many-instance-attributes +class Split(Definition): # pylint: disable=too-many-instance-attributes """Split model object.""" def __init__( # pylint: disable=too-many-arguments @@ -124,139 +61,9 @@ def __init__( # pylint: disable=too-many-arguments :pram prerequisites: prerequisites :type prerequisites: List of Preqreuisites """ - self._name = name - self._seed = seed - self._killed = killed - self._default_treatment = default_treatment - self._traffic_type_name = traffic_type_name - try: - self._status = Status(status) - except ValueError: - self._status = Status.ARCHIVED - - self._change_number = change_number - self._conditions = conditions if conditions is not None else [] - - if traffic_allocation is None: - self._traffic_allocation = 100 - elif traffic_allocation >= 0 and traffic_allocation <= 100: - self._traffic_allocation = traffic_allocation - else: - self._traffic_allocation = 100 - - self._traffic_allocation_seed = traffic_allocation_seed - try: - self._algo = HashAlgorithm(algo) - except ValueError: - self._algo = HashAlgorithm.LEGACY - - self._configurations = configurations - self._sets = set(sets) if sets is not None else set() - self._impressions_disabled = impressions_disabled if impressions_disabled is not None else False - self._prerequisites = prerequisites if prerequisites is not None else [] - - @property - def name(self): - """Return name.""" - return self._name - - @property - def seed(self): - """Return seed.""" - return self._seed - - @property - def algo(self): - """Return hash algorithm.""" - return self._algo - - @property - def killed(self): - """Return whether the split has been killed.""" - return self._killed - - @property - def default_treatment(self): - """Return the default treatment.""" - return self._default_treatment - - @property - def traffic_type_name(self): - """Return the traffic type of the split.""" - return self._traffic_type_name - - @property - def status(self): - """Return the status of the split.""" - return self._status - - @property - def change_number(self): - """Return the change number of the split.""" - return self._change_number - - @property - def conditions(self): - """Return the condition list of the split.""" - return self._conditions - - @property - def traffic_allocation(self): - """Return the traffic allocation percentage of the split.""" - return self._traffic_allocation - - @property - def traffic_allocation_seed(self): - """Return the traffic allocation seed of the split.""" - return self._traffic_allocation_seed - - @property - def sets(self): - """Return the flag sets of the split.""" - return self._sets - - @property - def impressions_disabled(self): - """Return impressions_disabled of the split.""" - return self._impressions_disabled - - @property - def prerequisites(self): - """Return prerequisites of the split.""" - return self._prerequisites - - def get_configurations_for(self, treatment): - """Return the mapping of treatments to configurations.""" - return self._configurations.get(treatment) if self._configurations else None - - def get_segment_names(self): - """ - Return a list of segment names referenced in all matchers from this split. - - :return: List of segment names. - :rtype: list(string) - """ - return [name for cond in self.conditions for name in cond.get_segment_names()] - - def to_json(self): - """Return a JSON representation of this split.""" - return { - 'changeNumber': self.change_number, - 'trafficTypeName': self.traffic_type_name, - 'name': self.name, - 'trafficAllocation': self.traffic_allocation, - 'trafficAllocationSeed': self.traffic_allocation_seed, - 'seed': self.seed, - 'status': self.status.value, - 'killed': self.killed, - 'defaultTreatment': self.default_treatment, - 'algo': self.algo.value, - 'conditions': [c.to_json() for c in self.conditions], - 'configurations': self._configurations, - 'sets': list(self._sets), - 'impressionsDisabled': self._impressions_disabled, - 'prerequisites': [prerequisite.to_json() for prerequisite in self._prerequisites] - } + Definition.__init__(self, name, seed, killed, default_treatment, traffic_type_name, status, + change_number, conditions, algo, traffic_allocation, traffic_allocation_seed, configurations, + sets, impressions_disabled, prerequisites) def to_split_view(self): """ @@ -278,67 +85,36 @@ def to_split_view(self): self._prerequisites ) - def local_kill(self, default_treatment, change_number): - """ - Perform split kill. - - :param default_treatment: name of the default treatment to return - :type default_treatment: str - :param change_number: change_number - :type change_number: int - """ - self._default_treatment = default_treatment - self._change_number = change_number - self._killed = True - - def __str__(self): - """Return string representation.""" - return 'name: {name}, seed: {seed}, killed: {killed}, ' \ - 'default treatment: {default_treatment}, ' \ - 'conditions: {conditions}'.format( - name=self._name, seed=self._seed, killed=self._killed, - default_treatment=self._default_treatment, - conditions=','.join(map(str, self._conditions)) - ) - - -def from_raw(raw_split): +def from_raw(raw_definition): """ - Parse a split from a JSON portion of splitChanges. + Parse a definition from a JSON portion of definitionChanges. - :param raw_split: JSON object extracted from a splitChange's split array (splitChanges response) - :type raw_split: dict + :param raw_definition: JSON object extracted from a definitionChange's definition array (definitionChanges response) + :type raw_definition: dict - :return: A parsed Split object capable of performing evaluations. - :rtype: Split + :return: A parsed definition object capable of performing evaluations. + :rtype: definition """ try: - conditions = [condition.from_raw(c) for c in raw_split['conditions']] + conditions = [condition.from_raw(c) for c in raw_definition['conditions']] except MatcherNotFoundException as e: _LOGGER.error(str(e)) - _LOGGER.debug("Using default conditions template for feature flag: %s", raw_split['name']) + _LOGGER.debug("Using default conditions template for feature flag: %s", raw_definition['name']) conditions = [condition.from_raw(_DEFAULT_CONDITIONS_TEMPLATE)] return Split( - raw_split['name'], - raw_split['seed'], - raw_split['killed'], - raw_split['defaultTreatment'], - raw_split['trafficTypeName'], - raw_split['status'], - raw_split['changeNumber'], + raw_definition['name'], + raw_definition['seed'], + raw_definition['killed'], + raw_definition['defaultTreatment'], + raw_definition['trafficTypeName'], + raw_definition['status'], + raw_definition['changeNumber'], conditions, - raw_split.get('algo'), - traffic_allocation=raw_split.get('trafficAllocation'), - traffic_allocation_seed=raw_split.get('trafficAllocationSeed'), - configurations=raw_split.get('configurations'), - sets=set(raw_split.get('sets')) if raw_split.get('sets') is not None else [], - impressions_disabled=raw_split.get('impressionsDisabled') if raw_split.get('impressionsDisabled') is not None else False, - prerequisites=from_raw_prerequisites(raw_split.get('prerequisites')) if raw_split.get('prerequisites') is not None else [] - ) - -def from_raw_prerequisites(raw_prerequisites): - to_return = [] - for prerequisite in raw_prerequisites: - to_return.append(Prerequisites(prerequisite['n'], prerequisite['ts'])) - - return to_return \ No newline at end of file + raw_definition.get('algo'), + traffic_allocation=raw_definition.get('trafficAllocation'), + traffic_allocation_seed=raw_definition.get('trafficAllocationSeed'), + configurations=raw_definition.get('configurations'), + sets=set(raw_definition.get('sets')) if raw_definition.get('sets') is not None else [], + impressions_disabled=raw_definition.get('impressionsDisabled') if raw_definition.get('impressionsDisabled') is not None else False, + prerequisites=from_raw_prerequisites(raw_definition.get('prerequisites')) if raw_definition.get('prerequisites') is not None else [] + ) \ No newline at end of file diff --git a/splitio/models/telemetry.py b/splitio/models/telemetry.py deleted file mode 100644 index c9715da4..00000000 --- a/splitio/models/telemetry.py +++ /dev/null @@ -1,1955 +0,0 @@ -"""SDK Telemetry helpers.""" -from bisect import bisect_left -import threading -import os -from enum import Enum -import abc - -from splitio.engine.impressions import ImpressionsMode -from splitio.optional.loaders import asyncio - -BUCKETS = ( - 1000, 1500, 2250, 3375, 5063, - 7594, 11391, 17086, 25629, 38443, - 57665, 86498, 129746, 194620, 291929, - 437894, 656841, 985261, 1477892, 2216838, - 3325257, 4987885, 7481828 -) - -MAX_LATENCY = 7481828 -MAX_LATENCY_BUCKET_COUNT = 23 -MAX_STREAMING_EVENTS = 20 -MAX_TAGS = 10 - -class CounterConstants(Enum): - """Impressions and events counters constants""" - IMPRESSIONS_QUEUED = 'impressionsQueued' - IMPRESSIONS_DEDUPED = 'impressionsDeduped' - IMPRESSIONS_DROPPED = 'impressionsDropped' - EVENTS_QUEUED = 'eventsQueued' - EVENTS_DROPPED = 'eventsDropped' - -class _ConfigParams(Enum): - """Config parameters constants""" - SPLITS_REFRESH_RATE = 'featuresRefreshRate' - SEGMENTS_REFRESH_RATE = 'segmentsRefreshRate' - IMPRESSIONS_REFRESH_RATE = 'impressionsRefreshRate' - EVENTS_REFRESH_RATE = 'eventsPushRate' - TELEMETRY_REFRESH_RATE = 'metricsRefreshRate' - OPERATION_MODE = 'operationMode' - STORAGE_TYPE = 'storageType' - STREAMING_ENABLED = 'streamingEnabled' - IMPRESSIONS_QUEUE_SIZE = 'impressionsQueueSize' - EVENTS_QUEUE_SIZE = 'eventsQueueSize' - IMPRESSIONS_MODE = 'impressionsMode' - IMPRESSIONS_LISTENER = 'impressionListener' - -class _ExtraConfig(Enum): - """Extra config constants""" - ACTIVE_FACTORY_COUNT = 'activeFactoryCount' - REDUNDANT_FACTORY_COUNT = 'redundantFactoryCount' - BLOCK_UNTIL_READY_TIMEOUT = 'blockUntilReadyTimeout' - NOT_READY = 'notReady' - TIME_UNTIL_READY = 'timeUntilReady' - REFRESH_RATE = 'refreshRate' - HTTP_PROXY = 'httpProxy' - HTTPS_PROXY_ENV = 'HTTPS_PROXY' - -class _ApiURLs(Enum): - """Api URL constants""" - SDK_URL = 'sdk_url' - EVENTS_URL = 'events_url' - AUTH_URL = 'auth_url' - STREAMING_URL = 'streaming_url' - TELEMETRY_URL = 'telemetry_url' - URL_OVERRIDE = 'urlOverride' - -class HTTPExceptionsAndLatencies(Enum): - """Sync exceptions and latencies constants""" - HTTP_ERRORS = 'httpErrors' - HTTP_LATENCIES = 'httpLatencies' - SPLIT = 'split' - SEGMENT = 'segment' - IMPRESSION = 'impression' - IMPRESSION_COUNT = 'impressionCount' - EVENT = 'event' - TELEMETRY = 'telemetry' - TOKEN = 'token' - -class MethodExceptionsAndLatencies(Enum): - """Method exceptions and latencies constants""" - METHOD_LATENCIES = 'methodLatencies' - METHOD_EXCEPTIONS = 'methodExceptions' - TREATMENT = 'treatment' - TREATMENTS = 'treatments' - TREATMENT_WITH_CONFIG = 'treatment_with_config' - TREATMENTS_WITH_CONFIG = 'treatments_with_config' - TREATMENTS_BY_FLAG_SET = 'treatments_by_flag_set' - TREATMENTS_BY_FLAG_SETS = 'treatments_by_flag_sets' - TREATMENTS_WITH_CONFIG_BY_FLAG_SET = 'treatments_with_config_by_flag_set' - TREATMENTS_WITH_CONFIG_BY_FLAG_SETS = 'treatments_with_config_by_flag_sets' - TRACK = 'track' - -class _LastSynchronizationConstants(Enum): - """Last sync constants""" - LAST_SYNCHRONIZATIONS = 'lastSynchronizations' - -class SSEStreamingStatus(Enum): - """SSE streaming status enums""" - ENABLED = 0 - DISABLED = 1 - PAUSED = 2 - -class SSEConnectionError(Enum): - """SSE Connection Error enums""" - REQUESTED = 0 - NON_REQUESTED = 1 - -class SSESyncMode(Enum): - """SSE sync mode enums""" - STREAMING = 0 - POLLING = 1 - -class _StreamingEventsConstant(Enum): - """Storage types constant""" - STREAMING_EVENTS = 'streamingEvents' - -class StreamingEventTypes(Enum): - """Streaming event types constants""" - CONNECTION_ESTABLISHED = 0 - OCCUPANCY_PRI = 10 - OCCUPANCY_SEC = 20 - STREAMING_STATUS = 30 - SSE_CONNECTION_ERROR = 40 - TOKEN_REFRESH = 50 - ABLY_ERROR = 60 - SYNC_MODE_UPDATE = 70 - -class StorageType(Enum): - """Storage types constants""" - MEMORY = 'memory' - REDIS = 'redis' - PLUGGABLE = 'pluggable' - -class OperationMode(Enum): - """Storage modes constants""" - STANDALONE = 'standalone' - CONSUMER = 'consumer' - PARTIAL_CONSUMER = 'partial_consumer' - -class UpdateFromSSE(Enum): - """Update from sse constants""" - SPLIT_UPDATE = 'sp' - RBS_UPDATE = 'rbs' - -def get_latency_bucket_index(micros): - """ - Find the bucket index for a measured latency. - - :param micros: Measured latency in microseconds - :type micros: int - :return: Bucket index for the given latency - :rtype: int - """ - if micros > MAX_LATENCY: - return len(BUCKETS) - 1 - - return bisect_left(BUCKETS, micros) - -class MethodLatenciesBase(object, metaclass=abc.ABCMeta): - """ - Method Latency base class - - """ - def _reset_all(self): - """Reset variables""" - self._treatment = [0] * MAX_LATENCY_BUCKET_COUNT - self._treatments = [0] * MAX_LATENCY_BUCKET_COUNT - self._treatment_with_config = [0] * MAX_LATENCY_BUCKET_COUNT - self._treatments_with_config = [0] * MAX_LATENCY_BUCKET_COUNT - self._treatments_by_flag_set = [0] * MAX_LATENCY_BUCKET_COUNT - self._treatments_by_flag_sets = [0] * MAX_LATENCY_BUCKET_COUNT - self._treatments_with_config_by_flag_set = [0] * MAX_LATENCY_BUCKET_COUNT - self._treatments_with_config_by_flag_sets = [0] * MAX_LATENCY_BUCKET_COUNT - self._track = [0] * MAX_LATENCY_BUCKET_COUNT - - @abc.abstractmethod - def add_latency(self, method, latency): - """ - Add Latency method - """ - - @abc.abstractmethod - def pop_all(self): - """ - Pop all latencies - """ - -class MethodLatencies(MethodLatenciesBase): - """ - Method Latency class - - """ - def __init__(self): - """Constructor""" - self._lock = threading.RLock() - with self._lock: - self._reset_all() - - def add_latency(self, method, latency): - """ - Add Latency method - - :param method: passed method name - :type method: str - :param latency: amount of latency in microseconds - :type latency: int - """ - latency_bucket = get_latency_bucket_index(latency) - with self._lock: - if method == MethodExceptionsAndLatencies.TREATMENT: - self._treatment[latency_bucket] += 1 - elif method == MethodExceptionsAndLatencies.TREATMENTS: - self._treatments[latency_bucket] += 1 - elif method == MethodExceptionsAndLatencies.TREATMENT_WITH_CONFIG: - self._treatment_with_config[latency_bucket] += 1 - elif method == MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG: - self._treatments_with_config[latency_bucket] += 1 - elif method == MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SET: - self._treatments_by_flag_set[latency_bucket] += 1 - elif method == MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SETS: - self._treatments_by_flag_sets[latency_bucket] += 1 - elif method == MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SET: - self._treatments_with_config_by_flag_set[latency_bucket] += 1 - elif method == MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SETS: - self._treatments_with_config_by_flag_sets[latency_bucket] += 1 - elif method == MethodExceptionsAndLatencies.TRACK: - self._track[latency_bucket] += 1 - else: - return - - def pop_all(self): - """ - Pop all latencies - - :return: Dictonary of latencies - :rtype: dict - """ - with self._lock: - latencies = {MethodExceptionsAndLatencies.METHOD_LATENCIES.value: { - MethodExceptionsAndLatencies.TREATMENT.value: self._treatment, - MethodExceptionsAndLatencies.TREATMENTS.value: self._treatments, - MethodExceptionsAndLatencies.TREATMENT_WITH_CONFIG.value: self._treatment_with_config, - MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG.value: self._treatments_with_config, - MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SET.value: self._treatments_by_flag_set, - MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SETS.value: self._treatments_by_flag_sets, - MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SET.value: self._treatments_with_config_by_flag_set, - MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SETS.value: self._treatments_with_config_by_flag_sets, - MethodExceptionsAndLatencies.TRACK.value: self._track} - } - self._reset_all() - return latencies - - -class MethodLatenciesAsync(MethodLatenciesBase): - """ - Method async Latency class - - """ - @classmethod - async def create(cls): - """Constructor""" - self = cls() - self._lock = asyncio.Lock() - async with self._lock: - self._reset_all() - return self - - async def add_latency(self, method, latency): - """ - Add Latency method - - :param method: passed method name - :type method: str - :param latency: amount of latency in microseconds - :type latency: int - """ - latency_bucket = get_latency_bucket_index(latency) - async with self._lock: - if method == MethodExceptionsAndLatencies.TREATMENT: - self._treatment[latency_bucket] += 1 - elif method == MethodExceptionsAndLatencies.TREATMENTS: - self._treatments[latency_bucket] += 1 - elif method == MethodExceptionsAndLatencies.TREATMENT_WITH_CONFIG: - self._treatment_with_config[latency_bucket] += 1 - elif method == MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG: - self._treatments_with_config[latency_bucket] += 1 - elif method == MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SET: - self._treatments_by_flag_set[latency_bucket] += 1 - elif method == MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SETS: - self._treatments_by_flag_sets[latency_bucket] += 1 - elif method == MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SET: - self._treatments_with_config_by_flag_set[latency_bucket] += 1 - elif method == MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SETS: - self._treatments_with_config_by_flag_sets[latency_bucket] += 1 - elif method == MethodExceptionsAndLatencies.TRACK: - self._track[latency_bucket] += 1 - else: - return - - async def pop_all(self): - """ - Pop all latencies - - :return: Dictonary of latencies - :rtype: dict - """ - async with self._lock: - latencies = {MethodExceptionsAndLatencies.METHOD_LATENCIES.value: { - MethodExceptionsAndLatencies.TREATMENT.value: self._treatment, - MethodExceptionsAndLatencies.TREATMENTS.value: self._treatments, - MethodExceptionsAndLatencies.TREATMENT_WITH_CONFIG.value: self._treatment_with_config, - MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG.value: self._treatments_with_config, - MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SET.value: self._treatments_by_flag_set, - MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SETS.value: self._treatments_by_flag_sets, - MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SET.value: self._treatments_with_config_by_flag_set, - MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SETS.value: self._treatments_with_config_by_flag_sets, - MethodExceptionsAndLatencies.TRACK.value: self._track} - } - self._reset_all() - return latencies - - -class HTTPLatenciesBase(object, metaclass=abc.ABCMeta): - """ - HTTP Latency class - - """ - def _reset_all(self): - """Reset variables""" - self._split = [0] * MAX_LATENCY_BUCKET_COUNT - self._segment = [0] * MAX_LATENCY_BUCKET_COUNT - self._impression = [0] * MAX_LATENCY_BUCKET_COUNT - self._impression_count = [0] * MAX_LATENCY_BUCKET_COUNT - self._event = [0] * MAX_LATENCY_BUCKET_COUNT - self._telemetry = [0] * MAX_LATENCY_BUCKET_COUNT - self._token = [0] * MAX_LATENCY_BUCKET_COUNT - - @abc.abstractmethod - def add_latency(self, resource, latency): - """ - Add Latency method - """ - - @abc.abstractmethod - def pop_all(self): - """ - Pop all latencies - """ - - -class HTTPLatencies(HTTPLatenciesBase): - """ - HTTP Latency class - - """ - def __init__(self): - """Constructor""" - self._lock = threading.RLock() - with self._lock: - self._reset_all() - - def add_latency(self, resource, latency): - """ - Add Latency method - - :param resource: passed resource name - :type resource: str - :param latency: amount of latency in microseconds - :type latency: int - """ - latency_bucket = get_latency_bucket_index(latency) - with self._lock: - if resource == HTTPExceptionsAndLatencies.SPLIT: - self._split[latency_bucket] += 1 - elif resource == HTTPExceptionsAndLatencies.SEGMENT: - self._segment[latency_bucket] += 1 - elif resource == HTTPExceptionsAndLatencies.IMPRESSION: - self._impression[latency_bucket] += 1 - elif resource == HTTPExceptionsAndLatencies.IMPRESSION_COUNT: - self._impression_count[latency_bucket] += 1 - elif resource == HTTPExceptionsAndLatencies.EVENT: - self._event[latency_bucket] += 1 - elif resource == HTTPExceptionsAndLatencies.TELEMETRY: - self._telemetry[latency_bucket] += 1 - elif resource == HTTPExceptionsAndLatencies.TOKEN: - self._token[latency_bucket] += 1 - else: - return - - def pop_all(self): - """ - Pop all latencies - - :return: Dictonary of latencies - :rtype: dict - """ - with self._lock: - latencies = {HTTPExceptionsAndLatencies.HTTP_LATENCIES.value: {HTTPExceptionsAndLatencies.SPLIT.value: self._split, HTTPExceptionsAndLatencies.SEGMENT.value: self._segment, HTTPExceptionsAndLatencies.IMPRESSION.value: self._impression, - HTTPExceptionsAndLatencies.IMPRESSION_COUNT.value: self._impression_count, HTTPExceptionsAndLatencies.EVENT.value: self._event, - HTTPExceptionsAndLatencies.TELEMETRY.value: self._telemetry, HTTPExceptionsAndLatencies.TOKEN.value: self._token} - } - self._reset_all() - return latencies - - -class HTTPLatenciesAsync(HTTPLatenciesBase): - """ - HTTP Latency async class - - """ - @classmethod - async def create(cls): - """Constructor""" - self = cls() - self._lock = asyncio.Lock() - async with self._lock: - self._reset_all() - return self - - async def add_latency(self, resource, latency): - """ - Add Latency method - - :param resource: passed resource name - :type resource: str - :param latency: amount of latency in microseconds - :type latency: int - """ - latency_bucket = get_latency_bucket_index(latency) - async with self._lock: - if resource == HTTPExceptionsAndLatencies.SPLIT: - self._split[latency_bucket] += 1 - elif resource == HTTPExceptionsAndLatencies.SEGMENT: - self._segment[latency_bucket] += 1 - elif resource == HTTPExceptionsAndLatencies.IMPRESSION: - self._impression[latency_bucket] += 1 - elif resource == HTTPExceptionsAndLatencies.IMPRESSION_COUNT: - self._impression_count[latency_bucket] += 1 - elif resource == HTTPExceptionsAndLatencies.EVENT: - self._event[latency_bucket] += 1 - elif resource == HTTPExceptionsAndLatencies.TELEMETRY: - self._telemetry[latency_bucket] += 1 - elif resource == HTTPExceptionsAndLatencies.TOKEN: - self._token[latency_bucket] += 1 - else: - return - - async def pop_all(self): - """ - Pop all latencies - - :return: Dictonary of latencies - :rtype: dict - """ - async with self._lock: - latencies = {HTTPExceptionsAndLatencies.HTTP_LATENCIES.value: {HTTPExceptionsAndLatencies.SPLIT.value: self._split, HTTPExceptionsAndLatencies.SEGMENT.value: self._segment, HTTPExceptionsAndLatencies.IMPRESSION.value: self._impression, - HTTPExceptionsAndLatencies.IMPRESSION_COUNT.value: self._impression_count, HTTPExceptionsAndLatencies.EVENT.value: self._event, - HTTPExceptionsAndLatencies.TELEMETRY.value: self._telemetry, HTTPExceptionsAndLatencies.TOKEN.value: self._token} - } - self._reset_all() - return latencies - - -class MethodExceptionsBase(object, metaclass=abc.ABCMeta): - """ - Method exceptions base class - - """ - def _reset_all(self): - """Reset variables""" - self._treatment = 0 - self._treatments = 0 - self._treatment_with_config = 0 - self._treatments_with_config = 0 - self._treatments_by_flag_set = 0 - self._treatments_by_flag_sets = 0 - self._treatments_with_config_by_flag_set = 0 - self._treatments_with_config_by_flag_sets = 0 - self._track = 0 - - @abc.abstractmethod - def add_exception(self, method): - """ - Add exceptions method - """ - - @abc.abstractmethod - def pop_all(self): - """ - Pop all exceptions - """ - - -class MethodExceptions(MethodExceptionsBase): - """ - Method exceptions class - - """ - def __init__(self): - """Constructor""" - self._lock = threading.RLock() - with self._lock: - self._reset_all() - - def add_exception(self, method): - """ - Add exceptions method - - :param method: passed method name - :type method: str - """ - with self._lock: - if method == MethodExceptionsAndLatencies.TREATMENT: - self._treatment += 1 - elif method == MethodExceptionsAndLatencies.TREATMENTS: - self._treatments += 1 - elif method == MethodExceptionsAndLatencies.TREATMENT_WITH_CONFIG: - self._treatment_with_config += 1 - elif method == MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG: - self._treatments_with_config += 1 - elif method == MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SET: - self._treatments_by_flag_set += 1 - elif method == MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SETS: - self._treatments_by_flag_sets += 1 - elif method == MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SET: - self._treatments_with_config_by_flag_set += 1 - elif method == MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SETS: - self._treatments_with_config_by_flag_sets += 1 - elif method == MethodExceptionsAndLatencies.TRACK: - self._track += 1 - else: - return - - def pop_all(self): - """ - Pop all exceptions - - :return: Dictonary of exceptions - :rtype: dict - """ - with self._lock: - exceptions = { - MethodExceptionsAndLatencies.METHOD_EXCEPTIONS.value: { - MethodExceptionsAndLatencies.TREATMENT.value: self._treatment, - MethodExceptionsAndLatencies.TREATMENTS.value: self._treatments, - MethodExceptionsAndLatencies.TREATMENT_WITH_CONFIG.value: self._treatment_with_config, - MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG.value: self._treatments_with_config, - MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SET.value: self._treatments_by_flag_set, - MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SETS.value: self._treatments_by_flag_sets, - MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SET.value: self._treatments_with_config_by_flag_set, - MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SETS.value: self._treatments_with_config_by_flag_sets, - MethodExceptionsAndLatencies.TRACK.value: self._track} - } - self._reset_all() - return exceptions - - -class MethodExceptionsAsync(MethodExceptionsBase): - """ - Method async exceptions class - - """ - @classmethod - async def create(cls): - """Constructor""" - self = cls() - self._lock = asyncio.Lock() - async with self._lock: - self._reset_all() - return self - - async def add_exception(self, method): - """ - Add exceptions method - - :param method: passed method name - :type method: str - """ - async with self._lock: - if method == MethodExceptionsAndLatencies.TREATMENT: - self._treatment += 1 - elif method == MethodExceptionsAndLatencies.TREATMENTS: - self._treatments += 1 - elif method == MethodExceptionsAndLatencies.TREATMENT_WITH_CONFIG: - self._treatment_with_config += 1 - elif method == MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG: - self._treatments_with_config += 1 - elif method == MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SET: - self._treatments_by_flag_set += 1 - elif method == MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SETS: - self._treatments_by_flag_sets += 1 - elif method == MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SET: - self._treatments_with_config_by_flag_set += 1 - elif method == MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SETS: - self._treatments_with_config_by_flag_sets += 1 - elif method == MethodExceptionsAndLatencies.TRACK: - self._track += 1 - else: - return - - async def pop_all(self): - """ - Pop all exceptions - - :return: Dictonary of exceptions - :rtype: dict - """ - async with self._lock: - exceptions = { - MethodExceptionsAndLatencies.METHOD_EXCEPTIONS.value: { - MethodExceptionsAndLatencies.TREATMENT.value: self._treatment, - MethodExceptionsAndLatencies.TREATMENTS.value: self._treatments, - MethodExceptionsAndLatencies.TREATMENT_WITH_CONFIG.value: self._treatment_with_config, - MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG.value: self._treatments_with_config, - MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SET.value: self._treatments_by_flag_set, - MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SETS.value: self._treatments_by_flag_sets, - MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SET.value: self._treatments_with_config_by_flag_set, - MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SETS.value: self._treatments_with_config_by_flag_sets, - MethodExceptionsAndLatencies.TRACK.value: self._track} - } - self._reset_all() - return exceptions - - -class LastSynchronizationBase(object, metaclass=abc.ABCMeta): - """ - Last Synchronization info base class - - """ - def _reset_all(self): - """Reset variables""" - self._split = 0 - self._segment = 0 - self._impression = 0 - self._impression_count = 0 - self._event = 0 - self._telemetry = 0 - self._token = 0 - - @abc.abstractmethod - def add_latency(self, resource, sync_time): - """ - Add Latency method - """ - - @abc.abstractmethod - def get_all(self): - """ - get all exceptions - """ - -class LastSynchronization(LastSynchronizationBase): - """ - Last Synchronization info class - - """ - def __init__(self): - """Constructor""" - self._lock = threading.RLock() - with self._lock: - self._reset_all() - - def add_latency(self, resource, sync_time): - """ - Add Latency method - - :param resource: passed resource name - :type resource: str - :param sync_time: amount of last sync time - :type sync_time: int - """ - with self._lock: - if resource == HTTPExceptionsAndLatencies.SPLIT: - self._split = sync_time - elif resource == HTTPExceptionsAndLatencies.SEGMENT: - self._segment = sync_time - elif resource == HTTPExceptionsAndLatencies.IMPRESSION: - self._impression = sync_time - elif resource == HTTPExceptionsAndLatencies.IMPRESSION_COUNT: - self._impression_count = sync_time - elif resource == HTTPExceptionsAndLatencies.EVENT: - self._event = sync_time - elif resource == HTTPExceptionsAndLatencies.TELEMETRY: - self._telemetry = sync_time - elif resource == HTTPExceptionsAndLatencies.TOKEN: - self._token = sync_time - else: - return - - def get_all(self): - """ - get all exceptions - - :return: Dictonary of latencies - :rtype: dict - """ - with self._lock: - return { - _LastSynchronizationConstants.LAST_SYNCHRONIZATIONS.value: { - HTTPExceptionsAndLatencies.SPLIT.value: self._split, - HTTPExceptionsAndLatencies.SEGMENT.value: self._segment, - HTTPExceptionsAndLatencies.IMPRESSION.value: self._impression, - HTTPExceptionsAndLatencies.IMPRESSION_COUNT.value: self._impression_count, - HTTPExceptionsAndLatencies.EVENT.value: self._event, - HTTPExceptionsAndLatencies.TELEMETRY.value: self._telemetry, - HTTPExceptionsAndLatencies.TOKEN.value: self._token} - } - -class LastSynchronizationAsync(LastSynchronizationBase): - """ - Last Synchronization async info class - - """ - @classmethod - async def create(cls): - """Constructor""" - self = cls() - self._lock = asyncio.Lock() - async with self._lock: - self._reset_all() - return self - - async def add_latency(self, resource, sync_time): - """ - Add Latency method - - :param resource: passed resource name - :type resource: str - :param sync_time: amount of last sync time - :type sync_time: int - """ - async with self._lock: - if resource == HTTPExceptionsAndLatencies.SPLIT: - self._split = sync_time - elif resource == HTTPExceptionsAndLatencies.SEGMENT: - self._segment = sync_time - elif resource == HTTPExceptionsAndLatencies.IMPRESSION: - self._impression = sync_time - elif resource == HTTPExceptionsAndLatencies.IMPRESSION_COUNT: - self._impression_count = sync_time - elif resource == HTTPExceptionsAndLatencies.EVENT: - self._event = sync_time - elif resource == HTTPExceptionsAndLatencies.TELEMETRY: - self._telemetry = sync_time - elif resource == HTTPExceptionsAndLatencies.TOKEN: - self._token = sync_time - else: - return - - async def get_all(self): - """ - get all exceptions - - :return: Dictonary of latencies - :rtype: dict - """ - async with self._lock: - return { - _LastSynchronizationConstants.LAST_SYNCHRONIZATIONS.value: { - HTTPExceptionsAndLatencies.SPLIT.value: self._split, - HTTPExceptionsAndLatencies.SEGMENT.value: self._segment, - HTTPExceptionsAndLatencies.IMPRESSION.value: self._impression, - HTTPExceptionsAndLatencies.IMPRESSION_COUNT.value: self._impression_count, - HTTPExceptionsAndLatencies.EVENT.value: self._event, - HTTPExceptionsAndLatencies.TELEMETRY.value: self._telemetry, - HTTPExceptionsAndLatencies.TOKEN.value: self._token} - } - - -class HTTPErrorsBase(object, metaclass=abc.ABCMeta): - """ - Http errors base class - - """ - def _reset_all(self): - """Reset variables""" - self._split = {} - self._segment = {} - self._impression = {} - self._impression_count = {} - self._event = {} - self._telemetry = {} - self._token = {} - - @abc.abstractmethod - def add_error(self, resource, status): - """ - Add Latency method - """ - - @abc.abstractmethod - def pop_all(self): - """ - Pop all errors - """ - - -class HTTPErrors(HTTPErrorsBase): - """ - Http errors class - - """ - def __init__(self): - """Constructor""" - self._lock = threading.RLock() - with self._lock: - self._reset_all() - - def add_error(self, resource, status): - """ - Add Latency method - - :param resource: passed resource name - :type resource: str - :param status: http error code - :type status: str - """ - status = str(status) - with self._lock: - if resource == HTTPExceptionsAndLatencies.SPLIT: - if status not in self._split: - self._split[status] = 0 - self._split[status] += 1 - elif resource == HTTPExceptionsAndLatencies.SEGMENT: - if status not in self._segment: - self._segment[status] = 0 - self._segment[status] += 1 - elif resource == HTTPExceptionsAndLatencies.IMPRESSION: - if status not in self._impression: - self._impression[status] = 0 - self._impression[status] += 1 - elif resource == HTTPExceptionsAndLatencies.IMPRESSION_COUNT: - if status not in self._impression_count: - self._impression_count[status] = 0 - self._impression_count[status] += 1 - elif resource == HTTPExceptionsAndLatencies.EVENT: - if status not in self._event: - self._event[status] = 0 - self._event[status] += 1 - elif resource == HTTPExceptionsAndLatencies.TELEMETRY: - if status not in self._telemetry: - self._telemetry[status] = 0 - self._telemetry[status] += 1 - elif resource == HTTPExceptionsAndLatencies.TOKEN: - if status not in self._token: - self._token[status] = 0 - self._token[status] += 1 - else: - return - - def pop_all(self): - """ - Pop all errors - - :return: Dictonary of exceptions - :rtype: dict - """ - with self._lock: - http_errors = { - HTTPExceptionsAndLatencies.HTTP_ERRORS.value: { - HTTPExceptionsAndLatencies.SPLIT.value: self._split, - HTTPExceptionsAndLatencies.SEGMENT.value: self._segment, - HTTPExceptionsAndLatencies.IMPRESSION.value: self._impression, - HTTPExceptionsAndLatencies.IMPRESSION_COUNT.value: self._impression_count, HTTPExceptionsAndLatencies.EVENT.value: self._event, - HTTPExceptionsAndLatencies.TELEMETRY.value: self._telemetry, HTTPExceptionsAndLatencies.TOKEN.value: self._token - } - } - self._reset_all() - return http_errors - - -class HTTPErrorsAsync(HTTPErrorsBase): - """ - Http error async class - - """ - @classmethod - async def create(cls): - """Constructor""" - self = cls() - self._lock = asyncio.Lock() - async with self._lock: - self._reset_all() - return self - - async def add_error(self, resource, status): - """ - Add Latency method - - :param resource: passed resource name - :type resource: str - :param status: http error code - :type status: str - """ - status = str(status) - async with self._lock: - if resource == HTTPExceptionsAndLatencies.SPLIT: - if status not in self._split: - self._split[status] = 0 - self._split[status] += 1 - elif resource == HTTPExceptionsAndLatencies.SEGMENT: - if status not in self._segment: - self._segment[status] = 0 - self._segment[status] += 1 - elif resource == HTTPExceptionsAndLatencies.IMPRESSION: - if status not in self._impression: - self._impression[status] = 0 - self._impression[status] += 1 - elif resource == HTTPExceptionsAndLatencies.IMPRESSION_COUNT: - if status not in self._impression_count: - self._impression_count[status] = 0 - self._impression_count[status] += 1 - elif resource == HTTPExceptionsAndLatencies.EVENT: - if status not in self._event: - self._event[status] = 0 - self._event[status] += 1 - elif resource == HTTPExceptionsAndLatencies.TELEMETRY: - if status not in self._telemetry: - self._telemetry[status] = 0 - self._telemetry[status] += 1 - elif resource == HTTPExceptionsAndLatencies.TOKEN: - if status not in self._token: - self._token[status] = 0 - self._token[status] += 1 - else: - return - - async def pop_all(self): - """ - Pop all errors - - :return: Dictonary of exceptions - :rtype: dict - """ - async with self._lock: - http_errors = { - HTTPExceptionsAndLatencies.HTTP_ERRORS.value: { - HTTPExceptionsAndLatencies.SPLIT.value: self._split, - HTTPExceptionsAndLatencies.SEGMENT.value: self._segment, - HTTPExceptionsAndLatencies.IMPRESSION.value: self._impression, - HTTPExceptionsAndLatencies.IMPRESSION_COUNT.value: self._impression_count, HTTPExceptionsAndLatencies.EVENT.value: self._event, - HTTPExceptionsAndLatencies.TELEMETRY.value: self._telemetry, HTTPExceptionsAndLatencies.TOKEN.value: self._token - } - } - self._reset_all() - return http_errors - - -class TelemetryCountersBase(object, metaclass=abc.ABCMeta): - """ - Counters base class - - """ - def _reset_all(self): - """Reset variables""" - self._impressions_queued = 0 - self._impressions_deduped = 0 - self._impressions_dropped = 0 - self._events_queued = 0 - self._events_dropped = 0 - self._auth_rejections = 0 - self._token_refreshes = 0 - self._session_length = 0 - self._update_from_sse = {} - - @abc.abstractmethod - def record_impressions_value(self, resource, value): - """ - Append to the resource value - """ - - @abc.abstractmethod - def record_events_value(self, resource, value): - """ - Append to the resource value - """ - - @abc.abstractmethod - def record_auth_rejections(self): - """ - Increament the auth rejection resource by one. - """ - - @abc.abstractmethod - def record_token_refreshes(self): - """ - Increament the token refreshes resource by one. - """ - - @abc.abstractmethod - def record_session_length(self, session): - """ - Set the session length value - """ - - @abc.abstractmethod - def get_counter_stats(self, resource): - """ - Get resource counter value - """ - - @abc.abstractmethod - def get_session_length(self): - """ - Get session length - """ - - @abc.abstractmethod - def pop_auth_rejections(self): - """ - Pop auth rejections - """ - - @abc.abstractmethod - def pop_token_refreshes(self): - """ - Pop token refreshes - """ - - -class TelemetryCounters(TelemetryCountersBase): - """ - Counters class - - """ - def __init__(self): - """Constructor""" - self._lock = threading.RLock() - with self._lock: - self._reset_all() - - def record_impressions_value(self, resource, value): - """ - Append to the resource value - - :param resource: passed resource name - :type resource: str - :param value: value to be appended - :type value: int - """ - with self._lock: - if resource == CounterConstants.IMPRESSIONS_QUEUED: - self._impressions_queued += value - elif resource == CounterConstants.IMPRESSIONS_DEDUPED: - self._impressions_deduped += value - elif resource == CounterConstants.IMPRESSIONS_DROPPED: - self._impressions_dropped += value - else: - return - - def record_events_value(self, resource, value): - """ - Append to the resource value - - :param resource: passed resource name - :type resource: str - :param value: value to be appended - :type value: int - """ - with self._lock: - if resource == CounterConstants.EVENTS_QUEUED: - self._events_queued += value - elif resource == CounterConstants.EVENTS_DROPPED: - self._events_dropped += value - else: - return - - def record_update_from_sse(self, event): - """ - Increment the update from sse resource by one. - """ - with self._lock: - if event.value not in self._update_from_sse: - self._update_from_sse[event.value] = 0 - self._update_from_sse[event.value] += 1 - - def record_auth_rejections(self): - """ - Increment the auth rejection resource by one. - - """ - with self._lock: - self._auth_rejections += 1 - - def record_token_refreshes(self): - """ - Increment the token refreshes resource by one. - - """ - with self._lock: - self._token_refreshes += 1 - - def pop_update_from_sse(self, event): - """ - Pop update from sse - :return: update from sse value - :rtype: int - """ - with self._lock: - if self._update_from_sse.get(event.value) is None: - return 0 - - update_from_sse = self._update_from_sse[event.value] - self._update_from_sse[event.value] = 0 - return update_from_sse - - def record_session_length(self, session): - """ - Set the session length value - - :param session: value to be set - :type session: int - """ - with self._lock: - self._session_length = session - - def get_counter_stats(self, resource): - """ - Get resource counter value - - :param resource: passed resource name - :type resource: str - - :return: resource value - :rtype: int - """ - - with self._lock: - if resource == CounterConstants.IMPRESSIONS_QUEUED: - return self._impressions_queued - - elif resource == CounterConstants.IMPRESSIONS_DEDUPED: - return self._impressions_deduped - - elif resource == CounterConstants.IMPRESSIONS_DROPPED: - return self._impressions_dropped - - elif resource == CounterConstants.EVENTS_QUEUED: - return self._events_queued - - elif resource == CounterConstants.EVENTS_DROPPED: - return self._events_dropped - - else: - return 0 - - def get_session_length(self): - """ - Get session length - - :return: session length value - :rtype: int - """ - with self._lock: - return self._session_length - - def pop_auth_rejections(self): - """ - Pop auth rejections - - :return: auth rejections value - :rtype: int - """ - with self._lock: - auth_rejections = self._auth_rejections - self._auth_rejections = 0 - return auth_rejections - - def pop_token_refreshes(self): - """ - Pop token refreshes - - :return: token refreshes value - :rtype: int - """ - with self._lock: - token_refreshes = self._token_refreshes - self._token_refreshes = 0 - return token_refreshes - -class TelemetryCountersAsync(TelemetryCountersBase): - """ - Counters async class - - """ - @classmethod - async def create(cls): - """Constructor""" - self = cls() - self._lock = asyncio.Lock() - async with self._lock: - self._reset_all() - return self - - async def record_impressions_value(self, resource, value): - """ - Append to the resource value - - :param resource: passed resource name - :type resource: str - :param value: value to be appended - :type value: int - """ - async with self._lock: - if resource == CounterConstants.IMPRESSIONS_QUEUED: - self._impressions_queued += value - elif resource == CounterConstants.IMPRESSIONS_DEDUPED: - self._impressions_deduped += value - elif resource == CounterConstants.IMPRESSIONS_DROPPED: - self._impressions_dropped += value - else: - return - - async def record_events_value(self, resource, value): - """ - Append to the resource value - - :param resource: passed resource name - :type resource: str - :param value: value to be appended - :type value: int - """ - async with self._lock: - if resource == CounterConstants.EVENTS_QUEUED: - self._events_queued += value - elif resource == CounterConstants.EVENTS_DROPPED: - self._events_dropped += value - else: - return - - async def record_update_from_sse(self, event): - """ - Increment the update from sse resource by one. - """ - async with self._lock: - if event.value not in self._update_from_sse: - self._update_from_sse[event.value] = 0 - self._update_from_sse[event.value] += 1 - - async def record_auth_rejections(self): - """ - Increment the auth rejection resource by one. - - """ - async with self._lock: - self._auth_rejections += 1 - - async def record_token_refreshes(self): - """ - Increment the token refreshes resource by one. - - """ - async with self._lock: - self._token_refreshes += 1 - - async def pop_update_from_sse(self, event): - """ - Pop update from sse - :return: update from sse value - :rtype: int - """ - async with self._lock: - if self._update_from_sse.get(event.value) is None: - return 0 - - update_from_sse = self._update_from_sse[event.value] - self._update_from_sse[event.value] = 0 - return update_from_sse - - async def record_session_length(self, session): - """ - Set the session length value - - :param session: value to be set - :type session: int - """ - async with self._lock: - self._session_length = session - - async def get_counter_stats(self, resource): - """ - Get resource counter value - - :param resource: passed resource name - :type resource: str - - :return: resource value - :rtype: int - """ - async with self._lock: - if resource == CounterConstants.IMPRESSIONS_QUEUED: - return self._impressions_queued - - elif resource == CounterConstants.IMPRESSIONS_DEDUPED: - return self._impressions_deduped - - elif resource == CounterConstants.IMPRESSIONS_DROPPED: - return self._impressions_dropped - - elif resource == CounterConstants.EVENTS_QUEUED: - return self._events_queued - - elif resource == CounterConstants.EVENTS_DROPPED: - return self._events_dropped - - else: - return 0 - - async def get_session_length(self): - """ - Get session length - - :return: session length value - :rtype: int - """ - async with self._lock: - return self._session_length - - async def pop_auth_rejections(self): - """ - Pop auth rejections - - :return: auth rejections value - :rtype: int - """ - async with self._lock: - auth_rejections = self._auth_rejections - self._auth_rejections = 0 - return auth_rejections - - async def pop_token_refreshes(self): - """ - Pop token refreshes - - :return: token refreshes value - :rtype: int - """ - async with self._lock: - token_refreshes = self._token_refreshes - self._token_refreshes = 0 - return token_refreshes - - -class StreamingEvent(object): - """ - Streaming event class - - """ - def __init__(self, streaming_event): - """ - Constructor - - :param streaming_event: Streaming event tuple: ('type', 'data', 'time') - :type streaming_event: dict - """ - self._type = streaming_event[0].value - self._data = streaming_event[1] - self._time = streaming_event[2] - - @property - def type(self): - """ - Get streaming event type - - :return: streaming event type - :rtype: str - """ - return self._type - - @property - def data(self): - """ - Get streaming event data - - :return: streaming event data - :rtype: str - """ - return self._data - - @property - def time(self): - """ - Get streaming event time - - :return: streaming event time - :rtype: int - """ - return self._time - -class StreamingEventsAsync(object): - """ - Streaming events async class - - """ - @classmethod - async def create(cls): - """Constructor""" - self = cls() - self._lock = asyncio.Lock() - async with self._lock: - self._streaming_events = [] - return self - - async def record_streaming_event(self, streaming_event): - """ - Record new streaming event - - :param streaming_event: Streaming event dict: - {'type': string, 'data': string, 'time': string} - :type streaming_event: dict - """ - if not StreamingEvent(streaming_event): - return - async with self._lock: - if len(self._streaming_events) < MAX_STREAMING_EVENTS: - self._streaming_events.append(StreamingEvent(streaming_event)) - - async def pop_streaming_events(self): - """ - Get and reset streaming events - - :return: streaming events dict - :rtype: dict - """ - async with self._lock: - streaming_events = self._streaming_events - self._streaming_events = [] - return {_StreamingEventsConstant.STREAMING_EVENTS.value: [ - {'e': streaming_event.type, 'd': streaming_event.data, - 't': streaming_event.time} for streaming_event in streaming_events]} - -class StreamingEvents(object): - """ - Streaming events class - - """ - def __init__(self): - """Constructor""" - self._lock = threading.RLock() - with self._lock: - self._streaming_events = [] - - def record_streaming_event(self, streaming_event): - """ - Record new streaming event - - :param streaming_event: Streaming event dict: - {'type': string, 'data': string, 'time': string} - :type streaming_event: dict - """ - if not StreamingEvent(streaming_event): - return - with self._lock: - if len(self._streaming_events) < MAX_STREAMING_EVENTS: - self._streaming_events.append(StreamingEvent(streaming_event)) - - def pop_streaming_events(self): - """ - Get and reset streaming events - - :return: streaming events dict - :rtype: dict - """ - - with self._lock: - streaming_events = self._streaming_events - self._streaming_events = [] - return {_StreamingEventsConstant.STREAMING_EVENTS.value: [ - {'e': streaming_event.type, 'd': streaming_event.data, - 't': streaming_event.time} for streaming_event in streaming_events]} - - -class TelemetryConfigBase(object, metaclass=abc.ABCMeta): - """ - Telemetry init config base class - - """ - def _reset_all(self): - """Reset variables""" - self._block_until_ready_timeout = 0 - self._not_ready = 0 - self._time_until_ready = 0 - self._operation_mode = None - self._storage_type = None - self._streaming_enabled = None - self._refresh_rate = { - _ConfigParams.SPLITS_REFRESH_RATE.value: 0, - _ConfigParams.SEGMENTS_REFRESH_RATE.value: 0, - _ConfigParams.IMPRESSIONS_REFRESH_RATE.value: 0, - _ConfigParams.EVENTS_REFRESH_RATE.value: 0, - _ConfigParams.TELEMETRY_REFRESH_RATE.value: 0} - self._url_override = { - _ApiURLs.SDK_URL.value: False, - _ApiURLs.EVENTS_URL.value: False, - _ApiURLs.AUTH_URL.value: False, - _ApiURLs.STREAMING_URL.value: False, - _ApiURLs.TELEMETRY_URL.value: False} - self._impressions_queue_size = 0 - self._events_queue_size = 0 - self._impressions_mode = None - self._impression_listener = False - self._http_proxy = None - self._active_factory_count = 0 - self._redundant_factory_count = 0 - self._flag_sets = 0 - self._flag_sets_invalid = 0 - - @abc.abstractmethod - def record_config(self, config, extra_config, total_flag_sets, invalid_flag_sets): - """ - Record configurations. - """ - - @abc.abstractmethod - def record_active_and_redundant_factories(self, active_factory_count, redundant_factory_count): - """ - Record active and redundant factories counts - """ - - @abc.abstractmethod - def record_ready_time(self, ready_time): - """ - Record ready time. - """ - - @abc.abstractmethod - def record_bur_time_out(self): - """ - Record block until ready timeout count - """ - - @abc.abstractmethod - def record_not_ready_usage(self): - """ - record non-ready usage count - """ - - @abc.abstractmethod - def get_bur_time_outs(self): - """ - Get block until ready timeout. - """ - - @abc.abstractmethod - def get_non_ready_usage(self): - """ - Get non-ready usage. - """ - - @abc.abstractmethod - def get_stats(self): - """ - Get config stats. - """ - - def _get_operation_mode(self, op_mode): - """ - Get formatted operation mode - - :param op_mode: config operation mode - :type config: str - - :return: operation mode - :rtype: int - """ - if op_mode == OperationMode.STANDALONE.value: - return 0 - - elif op_mode == OperationMode.CONSUMER.value: - return 1 - - else: - return 2 - - def _get_storage_type(self, op_mode, st_type): - """ - Get storage type from operation mode - - :param op_mode: config operation mode - :type config: str - - :return: storage type - :rtype: str - """ - if op_mode == OperationMode.STANDALONE.value: - return StorageType.MEMORY.value - - elif st_type == StorageType.REDIS.value: - return StorageType.REDIS.value - - else: - return StorageType.PLUGGABLE.value - - def _get_refresh_rates(self, config): - """ - Get refresh rates within config dict - - :param config: config dict - :type config: dict - - :return: refresh rates - :rtype: RefreshRates object - """ - return { - _ConfigParams.SPLITS_REFRESH_RATE.value: config[_ConfigParams.SPLITS_REFRESH_RATE.value], - _ConfigParams.SEGMENTS_REFRESH_RATE.value: config[_ConfigParams.SEGMENTS_REFRESH_RATE.value], - _ConfigParams.IMPRESSIONS_REFRESH_RATE.value: config[_ConfigParams.IMPRESSIONS_REFRESH_RATE.value], - _ConfigParams.EVENTS_REFRESH_RATE.value: config[_ConfigParams.EVENTS_REFRESH_RATE.value], - _ConfigParams.TELEMETRY_REFRESH_RATE.value: config[_ConfigParams.TELEMETRY_REFRESH_RATE.value] - } - - def _get_url_overrides(self, config): - """ - Get URL override within the config dict. - - :param config: config dict - :type config: dict - - :return: URL overrides dict - :rtype: URLOverrides object - """ - return { - _ApiURLs.SDK_URL.value: True if _ApiURLs.SDK_URL.value in config else False, - _ApiURLs.EVENTS_URL.value: True if _ApiURLs.EVENTS_URL.value in config else False, - _ApiURLs.AUTH_URL.value: True if _ApiURLs.AUTH_URL.value in config else False, - _ApiURLs.STREAMING_URL.value: True if _ApiURLs.STREAMING_URL.value in config else False, - _ApiURLs.TELEMETRY_URL.value: True if _ApiURLs.TELEMETRY_URL.value in config else False - } - - def _get_impressions_mode(self, imp_mode): - """ - Get impressions mode from operation mode - - :param op_mode: config operation mode - :type config: str - - :return: impressions mode - :rtype: int - """ - if imp_mode == ImpressionsMode.DEBUG.value: - return 1 - - elif imp_mode == ImpressionsMode.OPTIMIZED.value: - return 0 - - else: - return 2 - - def _check_if_proxy_detected(self): - """ - Return boolean flag if network https proxy is detected - - :return: https network proxy flag - :rtype: boolean - """ - for x in os.environ: - if x.upper() == _ExtraConfig.HTTPS_PROXY_ENV.value: - return True - - return False - - -class TelemetryConfig(TelemetryConfigBase): - """ - Telemetry init config class - - """ - def __init__(self): - """Constructor""" - self._lock = threading.RLock() - with self._lock: - self._reset_all() - - def record_config(self, config, extra_config, total_flag_sets, invalid_flag_sets): - """ - Record configurations. - - :param config: config dict: { - 'operationMode': int, 'storageType': string, 'streamingEnabled': boolean, - 'refreshRate' : { - 'featuresRefreshRate': int, - 'segmentsRefreshRate': int, - 'impressionsRefreshRate': int, - 'eventsPushRate': int, - 'metricsRefreshRate': int - } - 'urlOverride' : { - 'sdk_url': boolean, 'events_url': boolean, 'auth_url': boolean, - 'streaming_url': boolean, 'telemetry_url': boolean, } - }, - 'impressionsQueueSize': int, 'eventsQueueSize': int, 'impressionsMode': string, - 'impressionsListener': boolean, 'activeFactoryCount': int, 'redundantFactoryCount': int - } - :type config: dict - """ - with self._lock: - self._operation_mode = self._get_operation_mode(config[_ConfigParams.OPERATION_MODE.value]) - self._storage_type = self._get_storage_type(config[_ConfigParams.OPERATION_MODE.value], config[_ConfigParams.STORAGE_TYPE.value]) - self._streaming_enabled = config[_ConfigParams.STREAMING_ENABLED.value] - self._refresh_rate = self._get_refresh_rates(config) - self._url_override = self._get_url_overrides(extra_config) - self._impressions_queue_size = config[_ConfigParams.IMPRESSIONS_QUEUE_SIZE.value] - self._events_queue_size = config[_ConfigParams.EVENTS_QUEUE_SIZE.value] - self._impressions_mode = self._get_impressions_mode(config[_ConfigParams.IMPRESSIONS_MODE.value]) - self._impression_listener = True if config[_ConfigParams.IMPRESSIONS_LISTENER.value] is not None else False - self._http_proxy = self._check_if_proxy_detected() - self._flag_sets = total_flag_sets - self._flag_sets_invalid = invalid_flag_sets - - def record_active_and_redundant_factories(self, active_factory_count, redundant_factory_count): - """ - Record active and redundant factories counts - - :param active_factory_count: active factories count - :type active_factory_count: int - - :param redundant_factory_count: redundant factories count - :type redundant_factory_count: int - """ - with self._lock: - self._active_factory_count = active_factory_count - self._redundant_factory_count = redundant_factory_count - - def record_ready_time(self, ready_time): - """ - Record ready time. - - :param ready_time: SDK ready time - :type ready_time: int - """ - with self._lock: - self._time_until_ready = ready_time - - def record_bur_time_out(self): - """ - Record block until ready timeout count - - """ - with self._lock: - self._block_until_ready_timeout += 1 - - def record_not_ready_usage(self): - """ - record non-ready usage count - - """ - with self._lock: - self._not_ready += 1 - - def get_bur_time_outs(self): - """ - Get block until ready timeout. - - :return: block until ready timeouts count - :rtype: int - """ - with self._lock: - return self._block_until_ready_timeout - - def get_non_ready_usage(self): - """ - Get non-ready usage. - - :return: non-ready usage count - :rtype: int - """ - with self._lock: - return self._not_ready - - def get_stats(self): - """ - Get config stats. - - :return: dict of all config stats. - :rtype: dict - """ - with self._lock: - return { - 'bT': self._block_until_ready_timeout, - 'nR': self._not_ready, - 'tR': self._time_until_ready, - 'oM': self._operation_mode, - 'sT': self._storage_type, - 'sE': self._streaming_enabled, - 'rR': { - 'sp': self._refresh_rate[_ConfigParams.SPLITS_REFRESH_RATE.value], - 'se': self._refresh_rate[_ConfigParams.SEGMENTS_REFRESH_RATE.value], - 'im': self._refresh_rate[_ConfigParams.IMPRESSIONS_REFRESH_RATE.value], - 'ev': self._refresh_rate[_ConfigParams.EVENTS_REFRESH_RATE.value], - 'te': self._refresh_rate[_ConfigParams.TELEMETRY_REFRESH_RATE.value]}, - 'uO': { - 's': self._url_override[_ApiURLs.SDK_URL.value], - 'e': self._url_override[_ApiURLs.EVENTS_URL.value], - 'a': self._url_override[_ApiURLs.AUTH_URL.value], - 'st': self._url_override[_ApiURLs.STREAMING_URL.value], - 't': self._url_override[_ApiURLs.TELEMETRY_URL.value]}, - 'iQ': self._impressions_queue_size, - 'eQ': self._events_queue_size, - 'iM': self._impressions_mode, - 'iL': self._impression_listener, - 'hp': self._http_proxy, - 'aF': self._active_factory_count, - 'rF': self._redundant_factory_count, - 'fsT': self._flag_sets, - 'fsI': self._flag_sets_invalid - } - - -class TelemetryConfigAsync(TelemetryConfigBase): - """ - Telemetry init config async class - - """ - @classmethod - async def create(cls): - """Constructor""" - self = cls() - self._lock = asyncio.Lock() - async with self._lock: - self._reset_all() - return self - - async def record_config(self, config, extra_config, total_flag_sets, invalid_flag_sets): - """ - Record configurations. - - :param config: config dict: { - 'operationMode': int, 'storageType': string, 'streamingEnabled': boolean, - 'refreshRate' : { - 'featuresRefreshRate': int, - 'segmentsRefreshRate': int, - 'impressionsRefreshRate': int, - 'eventsPushRate': int, - 'metricsRefreshRate': int - } - 'urlOverride' : { - 'sdk_url': boolean, 'events_url': boolean, 'auth_url': boolean, - 'streaming_url': boolean, 'telemetry_url': boolean, } - }, - 'impressionsQueueSize': int, 'eventsQueueSize': int, 'impressionsMode': string, - 'impressionsListener': boolean, 'activeFactoryCount': int, 'redundantFactoryCount': int - } - :type config: dict - """ - async with self._lock: - self._operation_mode = self._get_operation_mode(config[_ConfigParams.OPERATION_MODE.value]) - self._storage_type = self._get_storage_type(config[_ConfigParams.OPERATION_MODE.value], config[_ConfigParams.STORAGE_TYPE.value]) - self._streaming_enabled = config[_ConfigParams.STREAMING_ENABLED.value] - self._refresh_rate = self._get_refresh_rates(config) - self._url_override = self._get_url_overrides(extra_config) - self._impressions_queue_size = config[_ConfigParams.IMPRESSIONS_QUEUE_SIZE.value] - self._events_queue_size = config[_ConfigParams.EVENTS_QUEUE_SIZE.value] - self._impressions_mode = self._get_impressions_mode(config[_ConfigParams.IMPRESSIONS_MODE.value]) - self._impression_listener = True if config[_ConfigParams.IMPRESSIONS_LISTENER.value] is not None else False - self._http_proxy = self._check_if_proxy_detected() - self._flag_sets = total_flag_sets - self._flag_sets_invalid = invalid_flag_sets - - async def record_active_and_redundant_factories(self, active_factory_count, redundant_factory_count): - """ - Record active and redundant factories counts - - :param active_factory_count: active factories count - :type active_factory_count: int - - :param redundant_factory_count: redundant factories count - :type redundant_factory_count: int - """ - async with self._lock: - self._active_factory_count = active_factory_count - self._redundant_factory_count = redundant_factory_count - - async def record_ready_time(self, ready_time): - """ - Record ready time. - - :param ready_time: SDK ready time - :type ready_time: int - """ - async with self._lock: - self._time_until_ready = ready_time - - async def record_bur_time_out(self): - """ - Record block until ready timeout count - - """ - async with self._lock: - self._block_until_ready_timeout += 1 - - async def record_not_ready_usage(self): - """ - record non-ready usage count - - """ - async with self._lock: - self._not_ready += 1 - - async def get_bur_time_outs(self): - """ - Get block until ready timeout. - - :return: block until ready timeouts count - :rtype: int - """ - async with self._lock: - return self._block_until_ready_timeout - - async def get_non_ready_usage(self): - """ - Get non-ready usage. - - :return: non-ready usage count - :rtype: int - """ - async with self._lock: - return self._not_ready - - async def get_stats(self): - """ - Get config stats. - - :return: dict of all config stats. - :rtype: dict - """ - async with self._lock: - return { - 'bT': self._block_until_ready_timeout, - 'nR': self._not_ready, - 'tR': self._time_until_ready, - 'oM': self._operation_mode, - 'sT': self._storage_type, - 'sE': self._streaming_enabled, - 'rR': { - 'sp': self._refresh_rate[_ConfigParams.SPLITS_REFRESH_RATE.value], - 'se': self._refresh_rate[_ConfigParams.SEGMENTS_REFRESH_RATE.value], - 'im': self._refresh_rate[_ConfigParams.IMPRESSIONS_REFRESH_RATE.value], - 'ev': self._refresh_rate[_ConfigParams.EVENTS_REFRESH_RATE.value], - 'te': self._refresh_rate[_ConfigParams.TELEMETRY_REFRESH_RATE.value]}, - 'uO': { - 's': self._url_override[_ApiURLs.SDK_URL.value], - 'e': self._url_override[_ApiURLs.EVENTS_URL.value], - 'a': self._url_override[_ApiURLs.AUTH_URL.value], - 'st': self._url_override[_ApiURLs.STREAMING_URL.value], - 't': self._url_override[_ApiURLs.TELEMETRY_URL.value]}, - 'iQ': self._impressions_queue_size, - 'eQ': self._events_queue_size, - 'iM': self._impressions_mode, - 'iL': self._impression_listener, - 'hp': self._http_proxy, - 'aF': self._active_factory_count, - 'rF': self._redundant_factory_count, - 'fsT': self._flag_sets, - 'fsI': self._flag_sets_invalid - } \ No newline at end of file diff --git a/splitio/models/token.py b/splitio/models/token.py deleted file mode 100644 index f2b0cf9c..00000000 --- a/splitio/models/token.py +++ /dev/null @@ -1,83 +0,0 @@ -"""Token module""" - -import base64 -import json - - -class Token(object): - """Token object class.""" - - def __init__(self, push_enabled, token, channels, exp, iat): - """ - Class constructor. - - :param push_enabled: flag push enabled. - :type push_enabled: bool - - :param token: Token from auth. - :type token: str - - :param channels: Channels parsed from token. - :type channels: str - - :param exp: exp parsed from token. - :type exp: int - - :param iat: iat parsed from token. - :type iat: int - """ - self._push_enabled = push_enabled - self._token = token - self._channels = channels - self._exp = exp - self._iat = iat - - @property - def push_enabled(self): - """Return push_enabled""" - return self._push_enabled - - @property - def token(self): - """Return token""" - return self._token - - @property - def channels(self): - """Return channels""" - return self._channels - - @property - def exp(self): - """Return exp""" - return self._exp - - @property - def iat(self): - """Return iat""" - return self._iat - - -def from_raw(raw_token): - """ - Parse a new token from a raw token response. - - :param raw_token: Token parsed from auth response. - :type raw_token: dict - - :return: New token model object - :rtype: splitio.models.token.Token - """ - if not 'pushEnabled' in raw_token or not 'token' in raw_token: - return Token(False, None, None, None, None) - - token = raw_token['token'] - push_enabled = raw_token['pushEnabled'] - token_parts = token.strip().split('.') - - if not push_enabled or len(token_parts) < 2: - return Token(False, None, None, None, None) - - to_decode = token_parts[1] - decoded_token = json.loads(base64.b64decode(to_decode + '='*(-len(to_decode) % 4))) - return Token(push_enabled, token, json.loads(decoded_token['x-ably-capability']), decoded_token['exp'], decoded_token['iat']) \ No newline at end of file diff --git a/splitio/push/manager.py b/splitio/push/manager.py index 2046d610..1ecd4b49 100644 --- a/splitio/push/manager.py +++ b/splitio/push/manager.py @@ -14,7 +14,7 @@ MessageType from splitio.push.processor import MessageProcessor, MessageProcessorAsync from splitio.push.status_tracker import PushStatusTracker, Status, PushStatusTrackerAsync -from splitio.models.telemetry import StreamingEventTypes +from harness_commons.models.telemetry import StreamingEventTypes if sys.version_info.major == 3 and sys.version_info.minor < 10: from splitio.optional.loaders import _anext as anext @@ -188,7 +188,7 @@ def _setup_next_token_refresh(self, token): Schedule next token refresh. :param token: Last fetched token. - :type token: splitio.models.token.Token + :type token: harness_commons.models.token.Token """ if self._next_refresh is not None: self._next_refresh.cancel() @@ -391,7 +391,7 @@ async def _token_refresh(self, current_token): """Refresh auth token. :param current_token: token (parsed) JWT - :type current_token: splitio.models.token.Token + :type current_token: harness_commons.models.token.Token """ _LOGGER.debug("Next token refresh in " + str(self._get_time_period(current_token)) + " seconds") await asyncio.sleep(self._get_time_period(current_token)) diff --git a/splitio/push/splitsse.py b/splitio/push/splitsse.py index 788648d4..120e27f5 100644 --- a/splitio/push/splitsse.py +++ b/splitio/push/splitsse.py @@ -57,7 +57,7 @@ def _build_url(self, token): Build the url to connect to and return it as a string. :param token: (parsed) JWT - :type token: splitio.models.token.Token + :type token: harness_commons.models.token.Token :returns: true if the connection was successful. False otherwise. :rtype: bool @@ -135,7 +135,7 @@ def start(self, token): Open a connection to start listening for events. :param token: (parsed) JWT - :type token: splitio.models.token.Token + :type token: harness_commons.models.token.Token :returns: true if the connection was successful. False otherwise. :rtype: bool @@ -203,7 +203,7 @@ async def start(self, token): Open a connection to start listening for events. :param token: (parsed) JWT - :type token: splitio.models.token.Token + :type token: harness_commons.models.token.Token :returns: yield events received from SSEClientAsync object :rtype: SSEEvent diff --git a/splitio/push/status_tracker.py b/splitio/push/status_tracker.py index ec11cb48..723f837d 100644 --- a/splitio/push/status_tracker.py +++ b/splitio/push/status_tracker.py @@ -4,7 +4,7 @@ from splitio.push.parser import ControlType from splitio.util.time import get_current_epoch_time_ms -from splitio.models.telemetry import StreamingEventTypes, SSEConnectionError, SSEStreamingStatus +from harness_commons.models.telemetry import StreamingEventTypes, SSEConnectionError, SSEStreamingStatus _LOGGER = logging.getLogger(__name__) @@ -91,7 +91,7 @@ def _get_next_status(self): Return the next status to propagate based on the last status. :returns: Next status and Streaming status for telemetry event. - :rtype: Tuple(splitio.push.status_tracker.Status, splitio.models.telemetry.SSEStreamingStatus) + :rtype: Tuple(splitio.push.status_tracker.Status, harness_commons.models.telemetry.SSEStreamingStatus) """ if self._last_status_propagated == Status.PUSH_SUBSYSTEM_UP: if not self._occupancy_ok() \ diff --git a/splitio/push/workers.py b/splitio/push/workers.py index e0dd8369..76a4d879 100644 --- a/splitio/push/workers.py +++ b/splitio/push/workers.py @@ -9,8 +9,8 @@ from enum import Enum from splitio.models.splits import from_raw -from splitio.models.rule_based_segments import from_raw as rbs_from_raw -from splitio.models.telemetry import UpdateFromSSE +from harness_commons.models.rule_based_segments import from_raw as rbs_from_raw +from harness_commons.models.telemetry import UpdateFromSSE from splitio.push import SplitStorageException from splitio.push.parser import UpdateType from splitio.optional.loaders import asyncio diff --git a/splitio/recorder/recorder.py b/splitio/recorder/recorder.py index 4c0ec155..35ddedcf 100644 --- a/splitio/recorder/recorder.py +++ b/splitio/recorder/recorder.py @@ -5,8 +5,8 @@ from splitio.client.config import DEFAULT_DATA_SAMPLING from splitio.client.listener import ImpressionListenerException -from splitio.models.telemetry import MethodExceptionsAndLatencies -from splitio.models import telemetry +from harness_commons.models.telemetry import MethodExceptionsAndLatencies +from harness_commons.models import telemetry from splitio.optional.loaders import asyncio _LOGGER = logging.getLogger(__name__) @@ -86,7 +86,7 @@ def _send_impressions_to_listener(self, impressions): Send impression result to custom listener. :param impressions: List of impression objects with attributes - :type impressions: list[tuple[splitio.models.impression.Impression, dict]] + :type impressions: list[tuple[harness_commons.models.impression.Impression, dict]] """ if self._listener is not None: try: @@ -120,7 +120,7 @@ async def _send_impressions_to_listener_async(self, impressions): Send impression result to custom listener. :param impressions: List of impression objects with attributes - :type impressions: list[tuple[splitio.models.impression.Impression, dict]] + :type impressions: list[tuple[harness_commons.models.impression.Impression, dict]] """ if self._listener is not None: try: @@ -183,7 +183,7 @@ def record_track_stats(self, event, latency): Record stats for tracking events. :param event: events tracked - :type event: splitio.models.events.EventWrapper + :type event: harness_commons.models.events.EventWrapper """ self._telemetry_evaluation_producer.record_latency(MethodExceptionsAndLatencies.TRACK, latency) return self._event_sotrage.put(event) @@ -244,7 +244,7 @@ async def record_track_stats(self, event, latency): Record stats for tracking events. :param event: events tracked - :type event: splitio.models.events.EventWrapper + :type event: harness_commons.models.events.EventWrapper """ await self._telemetry_evaluation_producer.record_latency(MethodExceptionsAndLatencies.TRACK, latency) return await self._event_sotrage.put(event) @@ -319,7 +319,7 @@ def record_track_stats(self, event, latency): Record stats for tracking events. :param event: events tracked - :type event: splitio.models.events.EventWrapper + :type event: harness_commons.models.events.EventWrapper """ try: pipe = self._make_pipe() @@ -410,7 +410,7 @@ async def record_track_stats(self, event, latency): Record stats for tracking events. :param event: events tracked - :type event: splitio.models.events.EventWrapper + :type event: harness_commons.models.events.EventWrapper """ try: pipe = self._make_pipe() diff --git a/splitio/storage/__init__.py b/splitio/storage/__init__.py index 079ee863..45f1e835 100644 --- a/splitio/storage/__init__.py +++ b/splitio/storage/__init__.py @@ -129,7 +129,7 @@ def put(self, segment): Store a segment. :param segment: Segment to store. - :type segment: splitio.models.segment.Segment + :type segment: harness_commons.models.segment.Segment """ pass @@ -377,9 +377,9 @@ def update(self, to_add, to_delete, new_change_number): Update rule based segment.. :param to_add: List of rule based segment. to add - :type to_add: list[splitio.models.rule_based_segments.RuleBasedSegment] + :type to_add: list[harness_commons.models.rule_based_segments.RuleBasedSegment] :param to_delete: List of rule based segment. to delete - :type to_delete: list[splitio.models.rule_based_segments.RuleBasedSegment] + :type to_delete: list[harness_commons.models.rule_based_segments.RuleBasedSegment] :param new_change_number: New change number. :type new_change_number: int """ diff --git a/splitio/storage/inmemmory.py b/splitio/storage/inmemmory.py index db71f7fd..2fb7e1fb 100644 --- a/splitio/storage/inmemmory.py +++ b/splitio/storage/inmemmory.py @@ -4,12 +4,12 @@ import queue from collections import Counter -from splitio.models.segments import Segment -from splitio.models.telemetry import HTTPErrors, HTTPLatencies, MethodExceptions, MethodLatencies, LastSynchronization, StreamingEvents, TelemetryConfig, TelemetryCounters, CounterConstants, \ +from harness_commons.models.segments import Segment +from harness_commons.models.telemetry import HTTPErrors, HTTPLatencies, MethodExceptions, MethodLatencies, LastSynchronization, StreamingEvents, TelemetryConfig, TelemetryCounters, CounterConstants, \ HTTPErrorsAsync, HTTPLatenciesAsync, MethodExceptionsAsync, MethodLatenciesAsync, LastSynchronizationAsync, StreamingEventsAsync, TelemetryConfigAsync, TelemetryCountersAsync -from splitio.models.events import SdkInternalEvent +from harness_commons.models.events import SdkInternalEvent from splitio.events.events_metadata import EventsMetadata, SdkEventType -from splitio.models.notification import SdkInternalEventNotification +from harness_commons.models.notification import SdkInternalEventNotification from splitio.storage import FlagSetsFilter, SplitStorage, SegmentStorage, ImpressionStorage, EventStorage, TelemetryStorage, RuleBasedSegmentsStorage from splitio.optional.loaders import asyncio @@ -135,7 +135,7 @@ def get(self, segment_name): :param segment_name: Name of the segment to fetch. :type segment_name: str - :rtype: splitio.models.rule_based_segments.RuleBasedSegment + :rtype: harness_commons.models.rule_based_segments.RuleBasedSegment """ with self._lock: return self._rule_based_segments.get(segment_name) @@ -145,9 +145,9 @@ def update(self, to_add, to_delete, new_change_number): Update rule based segment. :param to_add: List of rule based segment. to add - :type to_add: list[splitio.models.rule_based_segments.RuleBasedSegment] + :type to_add: list[harness_commons.models.rule_based_segments.RuleBasedSegment] :param to_delete: List of rule based segment. to delete - :type to_delete: list[splitio.models.rule_based_segments.RuleBasedSegment] + :type to_delete: list[harness_commons.models.rule_based_segments.RuleBasedSegment] :param new_change_number: New change number. :type new_change_number: int """ @@ -165,7 +165,7 @@ def _put(self, rule_based_segment): Store a rule based segment. :param rule_based_segment: RuleBasedSegment object. - :type rule_based_segment: splitio.models.rule_based_segments.RuleBasedSegment + :type rule_based_segment: harness_commons.models.rule_based_segments.RuleBasedSegment """ with self._lock: self._rule_based_segments[rule_based_segment.name] = rule_based_segment @@ -267,7 +267,7 @@ async def get(self, segment_name): :param segment_name: Name of the segment to fetch. :type segment_name: str - :rtype: splitio.models.rule_based_segments.RuleBasedSegment + :rtype: harness_commons.models.rule_based_segments.RuleBasedSegment """ async with self._lock: return self._rule_based_segments.get(segment_name) @@ -277,9 +277,9 @@ async def update(self, to_add, to_delete, new_change_number): Update rule based segment. :param to_add: List of rule based segment. to add - :type to_add: list[splitio.models.rule_based_segments.RuleBasedSegment] + :type to_add: list[harness_commons.models.rule_based_segments.RuleBasedSegment] :param to_delete: List of rule based segment. to delete - :type to_delete: list[splitio.models.rule_based_segments.RuleBasedSegment] + :type to_delete: list[harness_commons.models.rule_based_segments.RuleBasedSegment] :param new_change_number: New change number. :type new_change_number: int """ @@ -297,7 +297,7 @@ async def _put(self, rule_based_segment): Store a rule based segment. :param rule_based_segment: RuleBasedSegment object. - :type rule_based_segment: splitio.models.rule_based_segments.RuleBasedSegment + :type rule_based_segment: harness_commons.models.rule_based_segments.RuleBasedSegment """ async with self._lock: self._rule_based_segments[rule_based_segment.name] = rule_based_segment @@ -991,7 +991,7 @@ def put(self, segment): Store a segment. :param segment: Segment to store. - :type segment: splitio.models.segment.Segment + :type segment: harness_commons.models.segment.Segment """ with self._lock: self._segments[segment.name] = segment @@ -1133,7 +1133,7 @@ async def put(self, segment): Store a segment. :param segment: Segment to store. - :type segment: splitio.models.segment.Segment + :type segment: harness_commons.models.segment.Segment """ async with self._lock: self._segments[segment.name] = segment diff --git a/splitio/storage/pluggable.py b/splitio/storage/pluggable.py index 71e487c6..6b20ce1b 100644 --- a/splitio/storage/pluggable.py +++ b/splitio/storage/pluggable.py @@ -5,9 +5,10 @@ import threading from splitio.optional.loaders import asyncio -from splitio.models import splits, segments, rule_based_segments -from splitio.models.impressions import Impression -from splitio.models.telemetry import MethodExceptions, MethodLatencies, TelemetryConfig, MAX_TAGS,\ +from splitio.models import splits +from harness_commons.models import segments, rule_based_segments +from harness_commons.models.impressions import Impression +from harness_commons.models.telemetry import MethodExceptions, MethodLatencies, TelemetryConfig, MAX_TAGS,\ MethodLatenciesAsync, MethodExceptionsAsync, TelemetryConfigAsync from splitio.storage import FlagSetsFilter, SplitStorage, SegmentStorage, ImpressionStorage, EventStorage, TelemetryStorage, RuleBasedSegmentsStorage from splitio.util.storage_helper import get_valid_flag_sets, combine_valid_flag_sets @@ -80,9 +81,9 @@ def update(self, to_add, to_delete, new_change_number): Update rule based segment.. :param to_add: List of rule based segment. to add - :type to_add: list[splitio.models.rule_based_segments.RuleBasedSegment] + :type to_add: list[harness_commons.models.rule_based_segments.RuleBasedSegment] :param to_delete: List of rule based segment. to delete - :type to_delete: list[splitio.models.rule_based_segments.RuleBasedSegment] + :type to_delete: list[harness_commons.models.rule_based_segments.RuleBasedSegment] :param new_change_number: New change number. :type new_change_number: int """ @@ -186,7 +187,7 @@ def fetch_many(self, rb_segment_names): :type rb_segment_names: list(str) :return: A dict with rule based segment objects parsed from queue. - :rtype: dict(rb_segment_names, splitio.models.rile_based_segment.RuleBasedSegment) + :rtype: dict(rb_segment_names, harness_commons.models.rule_based_segments.RuleBasedSegment) """ try: prefix_added = [self._prefix.format(segment_name=rb_segment_name) for rb_segment_name in rb_segment_names] @@ -283,7 +284,7 @@ async def fetch_many(self, rb_segment_names): :type rb_segment_names: list(str) :return: A dict with rule based segment objects parsed from queue. - :rtype: dict(rb_segment_names, splitio.models.rile_based_segment.RuleBasedSegment) + :rtype: dict(rb_segment_names, harness_commons.models.rule_based_segments.RuleBasedSegment) """ try: prefix_added = [self._prefix.format(segment_name=rb_segment_name) for rb_segment_name in rb_segment_names] @@ -982,7 +983,7 @@ def get(self, segment_name): :type segment_name: str :return: segment object - :rtype: splitio.models.segments.Segment + :rtype: harness_commons.models.segments.Segment """ pass @@ -991,7 +992,7 @@ def put(self, segment): Store a segment. :param segment: Segment to store. - :type segment: splitio.models.segment.Segment + :type segment: harness_commons.models.segments.Segment """ pass # TODO: To be added when producer mode is aupported @@ -1083,7 +1084,7 @@ def get(self, segment_name): :type segment_name: str :return: segment object - :rtype: splitio.models.segments.Segment + :rtype: harness_commons.models.segments.Segment """ try: return segments.from_raw({'name': segment_name, 'added': self._pluggable_adapter.get_items(self._prefix.format(segment_name=segment_name)), 'removed': [], 'till': self._pluggable_adapter.get(self._segment_till_prefix.format(segment_name=segment_name))}) @@ -1172,7 +1173,7 @@ async def get(self, segment_name): :type segment_name: str :return: segment object - :rtype: splitio.models.segments.Segment + :rtype: harness_commons.models.segments.Segment """ try: return segments.from_raw({'name': segment_name, 'added': await self._pluggable_adapter.get_items(self._prefix.format(segment_name=segment_name)), 'removed': [], 'till': await self._pluggable_adapter.get(self._segment_till_prefix.format(segment_name=segment_name))}) @@ -1213,10 +1214,10 @@ def _wrap_impressions(self, impressions): Wrap impressions to be stored in storage :param impressions: Impression to add to the queue. - :type impressions: splitio.models.impressions.Impression + :type impressions: harness_commons.models.impressions.Impression :return: Processed impressions. - :rtype: list[splitio.models.impressions.Impression] + :rtype: list[harness_commons.models.impressions.Impression] """ bulk_impressions = [] for impression in impressions: @@ -1242,7 +1243,7 @@ def put(self, impressions): Add an impression to the pluggable storage. :param impressions: Impression to add to the queue. - :type impressions: splitio.models.impressions.Impression + :type impressions: harness_commons.models.impressions.Impression :return: Whether the impression has been added or not. :rtype: bool @@ -1297,7 +1298,7 @@ def put(self, impressions): Add an impression to the pluggable storage. :param impressions: Impression to add to the queue. - :type impressions: splitio.models.impressions.Impression + :type impressions: harness_commons.models.impressions.Impression :return: Whether the impression has been added or not. :rtype: bool @@ -1347,7 +1348,7 @@ async def put(self, impressions): Add an impression to the pluggable storage. :param impressions: Impression to add to the queue. - :type impressions: splitio.models.impressions.Impression + :type impressions: harness_commons.models.impressions.Impression :return: Whether the impression has been added or not. :rtype: bool @@ -1423,7 +1424,7 @@ def put(self, events): Add an event to the redis storage. :param event: Event to add to the queue. - :type event: splitio.models.events.Event + :type event: harness_commons.models.events.Event :return: Whether the event has been added or not. :rtype: bool @@ -1477,7 +1478,7 @@ def put(self, events): Add an event to the redis storage. :param event: Event to add to the queue. - :type event: splitio.models.events.Event + :type event: harness_commons.models.events.Event :return: Whether the event has been added or not. :rtype: bool @@ -1527,7 +1528,7 @@ async def put(self, events): Add an event to the redis storage. :param event: Event to add to the queue. - :type event: splitio.models.events.Event + :type event: harness_commons.models.events.Event :return: Whether the event has been added or not. :rtype: bool diff --git a/splitio/storage/redis.py b/splitio/storage/redis.py index b8fe27ad..2a9f3f70 100644 --- a/splitio/storage/redis.py +++ b/splitio/storage/redis.py @@ -3,9 +3,10 @@ import logging import threading -from splitio.models.impressions import Impression -from splitio.models import splits, segments, rule_based_segments -from splitio.models.telemetry import TelemetryConfig, TelemetryConfigAsync +from harness_commons.models.impressions import Impression +from splitio.models import splits +from harness_commons.models import segments, rule_based_segments +from harness_commons.models.telemetry import TelemetryConfig, TelemetryConfigAsync from splitio.storage import SplitStorage, SegmentStorage, ImpressionStorage, EventStorage, \ ImpressionPipelinedStorage, TelemetryStorage, FlagSetsFilter, RuleBasedSegmentsStorage from splitio.storage.adapters.redis import RedisAdapterException @@ -69,9 +70,9 @@ def update(self, to_add, to_delete, new_change_number): Update rule based segment.. :param to_add: List of rule based segment. to add - :type to_add: list[splitio.models.rule_based_segments.RuleBasedSegment] + :type to_add: list[harness_commons.models.rule_based_segments.RuleBasedSegment] :param to_delete: List of rule based segment. to delete - :type to_delete: list[splitio.models.rule_based_segments.RuleBasedSegment] + :type to_delete: list[harness_commons.models.rule_based_segments.RuleBasedSegment] :param new_change_number: New change number. :type new_change_number: int """ @@ -139,7 +140,7 @@ def fetch_many(self, segment_names): :type segment_names: list(str) :return: A dict with rule based segment objects parsed from redis. - :rtype: dict(segment_name, splitio.models.rule_based_segment.RuleBasedSegment) + :rtype: dict(segment_name, harness_commons.models.rule_based_segments.RuleBasedSegment) """ to_return = dict() if len(segment_names) == 0: @@ -216,9 +217,9 @@ async def update(self, to_add, to_delete, new_change_number): Update rule based segment.. :param to_add: List of rule based segment. to add - :type to_add: list[splitio.models.rule_based_segments.RuleBasedSegment] + :type to_add: list[harness_commons.models.rule_based_segments.RuleBasedSegment] :param to_delete: List of rule based segment. to delete - :type to_delete: list[splitio.models.rule_based_segments.RuleBasedSegment] + :type to_delete: list[harness_commons.models.rule_based_segments.RuleBasedSegment] :param new_change_number: New change number. :type new_change_number: int """ @@ -286,7 +287,7 @@ async def fetch_many(self, segment_names): :type segment_names: list(str) :return: A dict with rule based segment objects parsed from redis. - :rtype: dict(segment_name, splitio.models.rule_based_segment.RuleBasedSegment) + :rtype: dict(segment_name, harness_commons.models.rule_based_segments.RuleBasedSegment) """ to_return = dict() if len(segment_names) == 0: @@ -872,7 +873,7 @@ def put(self, segment): Store a segment. :param segment: Segment to store. - :type segment: splitio.models.segment.Segment + :type segment: harness_commons.models.segments.Segment """ raise NotImplementedError('Only redis-consumer mode is supported.') @@ -935,7 +936,7 @@ def get(self, segment_name): :type segment_name: str :return: Segment object is key exists. None otherwise. - :rtype: splitio.models.segments.Segment + :rtype: harness_commons.models.segments.Segment """ try: keys = (self._redis.smembers(self._get_key(segment_name))) @@ -1014,7 +1015,7 @@ async def get(self, segment_name): :type segment_name: str :return: Segment object is key exists. None otherwise. - :rtype: splitio.models.segments.Segment + :rtype: harness_commons.models.segments.Segment """ try: keys = (await self._redis.smembers(self._get_key(segment_name))) @@ -1084,10 +1085,10 @@ def _wrap_impressions(self, impressions): Wrap impressions to be stored in redis :param impressions: Impression to add to the queue. - :type impressions: splitio.models.impressions.Impression + :type impressions: harness_commons.models.impressions.Impression :return: Processed impressions. - :rtype: list[splitio.models.impressions.Impression] + :rtype: list[harness_commons.models.impressions.Impression] """ bulk_impressions = [] for impression in impressions: @@ -1142,7 +1143,7 @@ def put(self, impressions): Add an impression to the redis storage. :param impressions: Impression to add to the queue. - :type impressions: splitio.models.impressions.Impression + :type impressions: harness_commons.models.impressions.Impression :return: Whether the impression has been added or not. :rtype: bool @@ -1197,7 +1198,7 @@ def put(self, impressions): Add an impression to the redis storage. :param impressions: Impression to add to the queue. - :type impressions: splitio.models.impressions.Impression + :type impressions: harness_commons.models.impressions.Impression :return: Whether the impression has been added or not. :rtype: bool @@ -1248,7 +1249,7 @@ async def put(self, impressions): Add an impression to the redis storage. :param impressions: Impression to add to the queue. - :type impressions: splitio.models.impressions.Impression + :type impressions: harness_commons.models.impressions.Impression :return: Whether the impression has been added or not. :rtype: bool @@ -1312,7 +1313,7 @@ def put(self, events): Add an event to the redis storage. :param event: Event to add to the queue. - :type event: splitio.models.events.Event + :type event: harness_commons.models.events.Event :return: Whether the event has been added or not. :rtype: bool @@ -1365,7 +1366,7 @@ def put(self, events): Add an event to the redis storage. :param event: Event to add to the queue. - :type event: splitio.models.events.Event + :type event: harness_commons.models.events.Event :return: Whether the event has been added or not. :rtype: bool @@ -1416,7 +1417,7 @@ async def put(self, events): Add an event to the redis storage. :param event: Event to add to the queue. - :type event: splitio.models.events.Event + :type event: harness_commons.models.events.Event :return: Whether the event has been added or not. :rtype: bool diff --git a/splitio/sync/manager.py b/splitio/sync/manager.py index 7254a92e..47fa5b57 100644 --- a/splitio/sync/manager.py +++ b/splitio/sync/manager.py @@ -9,7 +9,7 @@ from splitio.api import APIException from splitio.util.backoff import Backoff from splitio.util.time import get_current_epoch_time_ms -from splitio.models.telemetry import SSESyncMode, StreamingEventTypes +from harness_commons.models.telemetry import SSESyncMode, StreamingEventTypes from splitio.sync.synchronizer import _SYNC_ALL_NO_RETRIES _LOGGER = logging.getLogger(__name__) diff --git a/splitio/sync/segment.py b/splitio/sync/segment.py index a87759e1..61c8681a 100644 --- a/splitio/sync/segment.py +++ b/splitio/sync/segment.py @@ -6,7 +6,7 @@ from splitio.api import APIException from splitio.api.commons import FetchOptions from splitio.tasks.util import workerpool -from splitio.models import segments +from harness_commons.models import segments from splitio.util.backoff import Backoff from splitio.optional.loaders import asyncio, aiofiles from splitio.sync import util diff --git a/splitio/sync/split.py b/splitio/sync/split.py index c1b5aa39..4b00c0d3 100644 --- a/splitio/sync/split.py +++ b/splitio/sync/split.py @@ -10,7 +10,8 @@ from splitio.api import APIException, APIUriException from splitio.api.commons import FetchOptions from splitio.client.input_validator import validate_flag_sets -from splitio.models import splits, rule_based_segments +from splitio.models import splits +from harness_commons.models import rule_based_segments from splitio.util.backoff import Backoff from splitio.util.time import get_current_epoch_time_ms from splitio.util.storage_helper import update_feature_flag_storage, update_feature_flag_storage_async, \ diff --git a/splitio/util/storage_helper.py b/splitio/util/storage_helper.py index 81fdef65..c1074a1e 100644 --- a/splitio/util/storage_helper.py +++ b/splitio/util/storage_helper.py @@ -1,7 +1,6 @@ """Storage Helper.""" import logging from splitio.models import splits -from splitio.models import rule_based_segments _LOGGER = logging.getLogger(__name__) @@ -43,7 +42,7 @@ def update_rule_based_segment_storage(rule_based_segment_storage, rule_based_seg :param rule_based_segment_storage: rule based segment storage instance :type rule_based_segment_storage: splitio.storage.RuleBasedSegmentStorage :param rule_based_segments: rule based segment instance to validate. - :type rule_based_segments: splitio.models.rule_based_segments.RuleBasedSegment + :type rule_based_segments: harness_commons.models.rule_based_segments.RuleBasedSegment :param: last change number :type: int @@ -121,7 +120,7 @@ async def update_rule_based_segment_storage_async(rule_based_segment_storage, ru :param rule_based_segment_storage: rule based segment storage instance :type rule_based_segment_storage: splitio.storage.RuleBasedSegmentStorage :param rule_based_segments: rule based segment instance to validate. - :type rule_based_segments: splitio.models.rule_based_segments.RuleBasedSegment + :type rule_based_segments: harness_commons.models.rule_based_segments.RuleBasedSegment :param: last change number :type: int diff --git a/tests/api/test_events.py b/tests/api/test_events.py index 07fe9473..eeb3deca 100644 --- a/tests/api/test_events.py +++ b/tests/api/test_events.py @@ -4,7 +4,7 @@ import unittest.mock as mock from splitio.api import events, client, APIException -from splitio.models.events import Event +from harness_commons.models.events import Event from splitio.client.util import get_metadata from splitio.client.config import DEFAULT_CONFIG from splitio.version import __version__ diff --git a/tests/api/test_impressions_api.py b/tests/api/test_impressions_api.py index b022a464..28e99668 100644 --- a/tests/api/test_impressions_api.py +++ b/tests/api/test_impressions_api.py @@ -4,7 +4,7 @@ import unittest.mock as mock from splitio.api import impressions, client, APIException -from splitio.models.impressions import Impression +from harness_commons.models.impressions import Impression from splitio.engine.impressions.impressions import ImpressionsMode from splitio.engine.impressions.manager import Counter from splitio.client.util import get_metadata diff --git a/tests/api/test_telemetry_api.py b/tests/api/test_telemetry_api.py index 5a857789..639c7b6f 100644 --- a/tests/api/test_telemetry_api.py +++ b/tests/api/test_telemetry_api.py @@ -4,7 +4,7 @@ import unittest.mock as mock from splitio.api import telemetry, client, APIException -#from splitio.models.telemetry import +#from harness_commons.models.telemetry import from splitio.client.util import get_metadata from splitio.client.config import DEFAULT_CONFIG from splitio.version import __version__ diff --git a/tests/api/test_util.py b/tests/api/test_util.py index 51876f52..c7097708 100644 --- a/tests/api/test_util.py +++ b/tests/api/test_util.py @@ -7,7 +7,7 @@ from splitio.client.util import SdkMetadata from splitio.engine.telemetry import TelemetryStorageProducer from splitio.storage.inmemmory import InMemoryTelemetryStorage -from splitio.models.telemetry import HTTPExceptionsAndLatencies +from harness_commons.models.telemetry import HTTPExceptionsAndLatencies class UtilTests(object): diff --git a/tests/client/test_client.py b/tests/client/test_client.py index 1efd4143..b34ce75b 100644 --- a/tests/client/test_client.py +++ b/tests/client/test_client.py @@ -9,10 +9,10 @@ from splitio.client.client import Client, _LOGGER as _logger, CONTROL, ClientAsync, EvaluationOptions from splitio.client.factory import SplitFactory, Status as FactoryStatus, SplitFactoryAsync from splitio.events.events_manager import EventsManager, EventsManagerAsync -from splitio.models.fallback_config import FallbackTreatmentsConfiguration, FallbackTreatmentCalculator -from splitio.models.fallback_treatment import FallbackTreatment -from splitio.models.impressions import Impression, Label -from splitio.models.events import Event, EventWrapper, SdkEvent +from harness_commons.models.fallback_config import FallbackTreatmentsConfiguration, FallbackTreatmentCalculator +from harness_commons.models.fallback_treatment import FallbackTreatment +from harness_commons.models.impressions import Impression, Label +from harness_commons.models.events import Event, EventWrapper, SdkEvent from splitio.storage import EventStorage, ImpressionStorage, SegmentStorage, SplitStorage, RuleBasedSegmentsStorage from splitio.storage.inmemmory import InMemorySplitStorage, InMemorySegmentStorage, \ InMemoryImpressionStorage, InMemoryTelemetryStorage, InMemorySplitStorageAsync, \ diff --git a/tests/client/test_config.py b/tests/client/test_config.py index e08a1d4b..f773f929 100644 --- a/tests/client/test_config.py +++ b/tests/client/test_config.py @@ -3,8 +3,8 @@ import pytest from splitio.client import config from splitio.engine.impressions.impressions import ImpressionsMode -from splitio.models.fallback_treatment import FallbackTreatment -from splitio.models.fallback_config import FallbackTreatmentsConfiguration +from harness_commons.models.fallback_treatment import FallbackTreatment +from harness_commons.models.fallback_config import FallbackTreatmentsConfiguration class ConfigSanitizationTests(object): """Inmemory storage-based integration tests.""" diff --git a/tests/client/test_factory.py b/tests/client/test_factory.py index 1512507c..3cd59524 100644 --- a/tests/client/test_factory.py +++ b/tests/client/test_factory.py @@ -22,9 +22,9 @@ from splitio.events.events_task import EventsTask from splitio.events.events_manager import EventsManagerAsync from splitio.models.splits import from_raw -from splitio.models.fallback_config import FallbackTreatmentsConfiguration, FallbackTreatmentCalculator -from splitio.models.fallback_treatment import FallbackTreatment -from splitio.models.events import SdkInternalEvent +from harness_commons.models.fallback_config import FallbackTreatmentsConfiguration, FallbackTreatmentCalculator +from harness_commons.models.fallback_treatment import FallbackTreatment +from harness_commons.models.events import SdkInternalEvent from splitio.recorder.recorder import PipelinedRecorder, StandardRecorder, StandardRecorderAsync from splitio.storage import redis, inmemmory, pluggable, EventStorage from splitio.storage.inmemmory import InMemorySplitStorage, InMemorySegmentStorage, \ @@ -1092,11 +1092,11 @@ async def synchronize_config(*_): async def record_ready_time(*_): pass - mocker.patch('splitio.models.telemetry.TelemetryConfigAsync.record_ready_time', new=record_ready_time) + mocker.patch('harness_commons.models.telemetry.TelemetryConfigAsync.record_ready_time', new=record_ready_time) async def record_active_and_redundant_factories(*_): pass - mocker.patch('splitio.models.telemetry.TelemetryConfigAsync.record_active_and_redundant_factories', new=record_active_and_redundant_factories) + mocker.patch('harness_commons.models.telemetry.TelemetryConfigAsync.record_active_and_redundant_factories', new=record_active_and_redundant_factories) # Start factory and make assertions factory = await get_factory_async('some_api_key', config={'streamingEmabled': False}) diff --git a/tests/client/test_input_validator.py b/tests/client/test_input_validator.py index e1634f54..550ecc2f 100644 --- a/tests/client/test_input_validator.py +++ b/tests/client/test_input_validator.py @@ -11,13 +11,13 @@ from splitio.storage.inmemmory import InMemoryTelemetryStorage, InMemoryTelemetryStorageAsync, \ InMemorySplitStorage, InMemorySplitStorageAsync, InMemoryRuleBasedSegmentStorage, InMemoryRuleBasedSegmentStorageAsync from splitio.models.splits import Split -from splitio.models.fallback_config import FallbackTreatmentCalculator +from harness_commons.models.fallback_config import FallbackTreatmentCalculator from splitio.client import input_validator from splitio.client.manager import SplitManager, SplitManagerAsync from splitio.recorder.recorder import StandardRecorder, StandardRecorderAsync from splitio.engine.telemetry import TelemetryStorageProducer, TelemetryStorageProducerAsync from splitio.engine.impressions.impressions import Manager as ImpressionManager -from splitio.models.fallback_treatment import FallbackTreatment +from harness_commons.models.fallback_treatment import FallbackTreatment class ClientInputValidationTests(object): """Input validation test cases.""" diff --git a/tests/client/test_localhost.py b/tests/client/test_localhost.py index 598d6100..387a7409 100644 --- a/tests/client/test_localhost.py +++ b/tests/client/test_localhost.py @@ -5,7 +5,7 @@ from splitio.client import localhost from splitio.sync.split import LocalSplitSynchronizer from splitio.models.splits import Split -from splitio.models.grammar.matchers import AllKeysMatcher +from harness_commons.models.grammar.matchers import AllKeysMatcher from splitio.storage import SplitStorage, RuleBasedSegmentsStorage diff --git a/tests/models/grammar/files/splits_prereq.json b/tests/engine/files/splits_prereq.json similarity index 100% rename from tests/models/grammar/files/splits_prereq.json rename to tests/engine/files/splits_prereq.json diff --git a/tests/engine/test_evaluator.py b/tests/engine/test_evaluator.py index edf510c0..2cd18d66 100644 --- a/tests/engine/test_evaluator.py +++ b/tests/engine/test_evaluator.py @@ -8,13 +8,13 @@ import asyncio from splitio.models.splits import Split, Status, from_raw, Prerequisites -from splitio.models import segments -from splitio.models.grammar.condition import Condition, ConditionType -from splitio.models.impressions import Label -from splitio.models.grammar import condition -from splitio.models import rule_based_segments -from splitio.models.fallback_treatment import FallbackTreatment -from splitio.models.fallback_config import FallbackTreatmentsConfiguration, FallbackTreatmentCalculator +from harness_commons.models import segments +from harness_commons.models.grammar.condition import Condition, ConditionType +from splitio.models.label import Label +from harness_commons.models.grammar import condition +from harness_commons.models import rule_based_segments +from harness_commons.models.fallback_treatment import FallbackTreatment +from harness_commons.models.fallback_config import FallbackTreatmentsConfiguration, FallbackTreatmentCalculator from splitio.engine import evaluator, splitters from splitio.engine.evaluator import EvaluationContext from splitio.storage.inmemmory import InMemorySplitStorage, InMemorySegmentStorage, InMemoryRuleBasedSegmentStorage, \ @@ -335,7 +335,7 @@ def test_using_rbs_in_excluded(self): assert e.eval_with_context('bilal2@split.io', 'bilal2@split.io', 'some', {'email': 'bilal2@split.io'}, ctx)['treatment'] == "on" def test_prerequisites(self): - splits_load = os.path.join(os.path.dirname(__file__), '../models/grammar/files', 'splits_prereq.json') + splits_load = os.path.join(os.path.dirname(__file__), 'files', 'splits_prereq.json') with open(splits_load, 'r') as flo: data = json.loads(flo.read()) e = evaluator.Evaluator(splitters.Splitter()) @@ -501,7 +501,7 @@ async def test_using_rbs_in_excluded_async(self): @pytest.mark.asyncio async def test_prerequisites(self): - splits_load = os.path.join(os.path.dirname(__file__), '../models/grammar/files', 'splits_prereq.json') + splits_load = os.path.join(os.path.dirname(__file__), 'files', 'splits_prereq.json') with open(splits_load, 'r') as flo: data = json.loads(flo.read()) e = evaluator.Evaluator(splitters.Splitter()) diff --git a/tests/engine/test_impressions.py b/tests/engine/test_impressions.py index 715bfe1b..112667b8 100644 --- a/tests/engine/test_impressions.py +++ b/tests/engine/test_impressions.py @@ -5,9 +5,9 @@ from splitio.engine.impressions.impressions import Manager, ImpressionsMode from splitio.engine.impressions.manager import Hasher, Observer, Counter, truncate_time from splitio.engine.impressions.strategies import StrategyDebugMode, StrategyOptimizedMode, StrategyNoneMode -from splitio.models.impressions import Impression, ImpressionDecorated +from harness_commons.models.impressions import Impression, ImpressionDecorated from splitio.client.listener import ImpressionListenerWrapper -import splitio.models.telemetry as ModelTelemetry +import harness_commons.models.telemetry as ModelTelemetry from splitio.engine.telemetry import TelemetryStorageProducer from splitio.storage.inmemmory import InMemoryTelemetryStorage diff --git a/tests/engine/test_splitter.py b/tests/engine/test_splitter.py index d4dc2f3b..da815f2d 100644 --- a/tests/engine/test_splitter.py +++ b/tests/engine/test_splitter.py @@ -1,6 +1,6 @@ """Splitter test module.""" -from splitio.models.grammar.partitions import Partition +from harness_commons.models.grammar.partitions import Partition from splitio.engine.splitters import Splitter, CONTROL diff --git a/tests/events/test_events_delivery.py b/tests/events/test_events_delivery.py index 27076de4..5040f459 100644 --- a/tests/events/test_events_delivery.py +++ b/tests/events/test_events_delivery.py @@ -1,7 +1,7 @@ """EventsManager test module.""" import pytest -from splitio.models.events import SdkEvent, SdkInternalEvent +from harness_commons.models.events import SdkEvent, SdkInternalEvent from splitio.events.events_metadata import EventsMetadata from splitio.events.events_delivery import EventsDelivery from splitio.events.events_metadata import SdkEventType diff --git a/tests/events/test_events_manager.py b/tests/events/test_events_manager.py index 6222b68b..eb96b0a6 100644 --- a/tests/events/test_events_manager.py +++ b/tests/events/test_events_manager.py @@ -2,7 +2,7 @@ import pytest import asyncio -from splitio.models.events import SdkEvent, SdkInternalEvent +from harness_commons.models.events import SdkEvent, SdkInternalEvent from splitio.events.events_metadata import EventsMetadata from splitio.events.events_manager_config import EventsManagerConfig from splitio.events.events_delivery import EventsDelivery diff --git a/tests/events/test_events_manager_config.py b/tests/events/test_events_manager_config.py index aa70c4d8..9283cc56 100644 --- a/tests/events/test_events_manager_config.py +++ b/tests/events/test_events_manager_config.py @@ -2,7 +2,7 @@ import pytest from splitio.events.events_manager_config import EventsManagerConfig -from splitio.models.events import SdkEvent, SdkInternalEvent +from harness_commons.models.events import SdkEvent, SdkInternalEvent class EventsManagerConfigTests(object): """Tests for EventsManagerConfig.""" diff --git a/tests/events/test_events_task.py b/tests/events/test_events_task.py index d667f76c..71d7ec0f 100644 --- a/tests/events/test_events_task.py +++ b/tests/events/test_events_task.py @@ -4,8 +4,8 @@ import time import asyncio -from splitio.models.events import SdkInternalEvent -from splitio.models.notification import SdkInternalEventNotification +from harness_commons.models.events import SdkInternalEvent +from harness_commons.models.notification import SdkInternalEventNotification from splitio.events.events_metadata import EventsMetadata from splitio.events.events_metadata import SdkEventType from splitio.events.events_task import EventsTask, EventsTaskAsync diff --git a/tests/integration/test_client_e2e.py b/tests/integration/test_client_e2e.py index 26efcd42..67c0482a 100644 --- a/tests/integration/test_client_e2e.py +++ b/tests/integration/test_client_e2e.py @@ -27,10 +27,11 @@ from splitio.events.events_manager import EventsManager, EventsManagerAsync from splitio.events.events_manager_config import EventsManagerConfig from splitio.events.events_task import EventsTask, EventsTaskAsync -from splitio.models import splits, segments, rule_based_segments -from splitio.models.events import SdkEvent -from splitio.models.fallback_config import FallbackTreatmentsConfiguration, FallbackTreatmentCalculator -from splitio.models.fallback_treatment import FallbackTreatment +from splitio.models import splits +from harness_commons.models import segments, rule_based_segments +from harness_commons.models.events import SdkEvent +from harness_commons.models.fallback_config import FallbackTreatmentsConfiguration, FallbackTreatmentCalculator +from harness_commons.models.fallback_treatment import FallbackTreatment from splitio.recorder.recorder import StandardRecorder, PipelinedRecorder, StandardRecorderAsync, PipelinedRecorderAsync from splitio.storage.inmemmory import InMemoryEventStorage, InMemoryImpressionStorage, \ InMemorySegmentStorage, InMemorySplitStorage, InMemoryTelemetryStorage, InMemorySplitStorageAsync,\ diff --git a/tests/integration/test_pluggable_integration.py b/tests/integration/test_pluggable_integration.py index 59534193..76942c53 100644 --- a/tests/integration/test_pluggable_integration.py +++ b/tests/integration/test_pluggable_integration.py @@ -5,7 +5,8 @@ import os from splitio.client.util import get_metadata -from splitio.models import splits, impressions, events +from splitio.models import splits +from harness_commons.models import impressions, events from splitio.storage.pluggable import PluggableEventsStorage, PluggableImpressionsStorage, PluggableSegmentStorage, \ PluggableSplitStorage, PluggableEventsStorageAsync, PluggableImpressionsStorageAsync, PluggableSegmentStorageAsync,\ PluggableSplitStorageAsync diff --git a/tests/integration/test_redis_integration.py b/tests/integration/test_redis_integration.py index 4c85beda..4adbfab6 100644 --- a/tests/integration/test_redis_integration.py +++ b/tests/integration/test_redis_integration.py @@ -5,7 +5,8 @@ import os from splitio.client.util import get_metadata -from splitio.models import splits, impressions, events +from splitio.models import splits +from harness_commons.models import impressions, events from splitio.storage.redis import RedisSplitStorage, RedisSegmentStorage, RedisImpressionsStorage, \ RedisEventsStorage, RedisEventsStorageAsync, RedisImpressionsStorageAsync, RedisSegmentStorageAsync, \ RedisSplitStorageAsync diff --git a/tests/integration/test_streaming_e2e.py b/tests/integration/test_streaming_e2e.py index d7b3103a..d542d31f 100644 --- a/tests/integration/test_streaming_e2e.py +++ b/tests/integration/test_streaming_e2e.py @@ -10,11 +10,11 @@ from queue import Queue from splitio.optional.loaders import asyncio from splitio.client.factory import get_factory, get_factory_async -from splitio.models.events import SdkEvent +from harness_commons.models.events import SdkEvent from splitio.events.events_metadata import SdkEventType from tests.helpers.mockserver import SSEMockServer, SplitMockServer from urllib.parse import parse_qs -from splitio.models.telemetry import StreamingEventTypes, SSESyncMode +from harness_commons.models.telemetry import StreamingEventTypes, SSESyncMode class StreamingIntegrationTests(object): diff --git a/tests/models/grammar/files/between-semver.csv b/tests/models/grammar/files/between-semver.csv deleted file mode 100644 index 71bdf3b2..00000000 --- a/tests/models/grammar/files/between-semver.csv +++ /dev/null @@ -1,18 +0,0 @@ -version1,version2,version3,expected -1.1.1,2.2.2,3.3.3,true -1.1.1-rc.1,1.1.1-rc.2,1.1.1-rc.3,true -1.0.0-alpha,1.0.0-alpha.1,1.0.0-alpha.beta,true -1.0.0-alpha.1,1.0.0-alpha.beta,1.0.0-beta,true -1.0.0-alpha.beta,1.0.0-beta,1.0.0-beta.2,true -1.0.0-beta,1.0.0-beta.2,1.0.0-beta.11,true -1.0.0-beta.2,1.0.0-beta.11,1.0.0-rc.1,true -1.0.0-beta.11,1.0.0-rc.1,1.0.0,true -1.1.2,1.1.3,1.1.4,true -1.2.1,1.3.1,1.4.1,true -2.0.0,3.0.0,4.0.0,true -2.2.2,2.2.3-rc1,2.2.3,true -2.2.2,2.3.2-rc100,2.3.3,true -1.0.0-rc.1+build.1,1.2.3-beta,1.2.3-rc.1+build.123,true -3.3.3,3.3.3-alpha,3.3.4,false -2.2.2-rc.1,2.2.2+metadata,2.2.2-rc.10,false -1.1.1-rc.1,1.1.1-rc.3,1.1.1-rc.2,false \ No newline at end of file diff --git a/tests/models/grammar/files/equal-to-semver.csv b/tests/models/grammar/files/equal-to-semver.csv deleted file mode 100644 index 87d8db5a..00000000 --- a/tests/models/grammar/files/equal-to-semver.csv +++ /dev/null @@ -1,7 +0,0 @@ -version1,version2,equals -1.1.1,1.1.1,true -1.1.1,1.1.1+metadata,false -1.1.1,1.1.1-rc.1,false -88.88.88,88.88.88,true -1.2.3----RC-SNAPSHOT.12.9.1--.12,1.2.3----RC-SNAPSHOT.12.9.1--.12,true -10.2.3-DEV-SNAPSHOT,10.2.3-SNAPSHOT-123,false \ No newline at end of file diff --git a/tests/models/grammar/files/invalid-semantic-versions.csv b/tests/models/grammar/files/invalid-semantic-versions.csv deleted file mode 100644 index 7a7f9fbc..00000000 --- a/tests/models/grammar/files/invalid-semantic-versions.csv +++ /dev/null @@ -1,28 +0,0 @@ -invalid -1 -1.2 -1.alpha.2 -+invalid --invalid --invalid+invalid --invalid.01 -alpha -alpha.beta -alpha.beta.1 -alpha.1 -alpha+beta -alpha_beta -alpha. -alpha.. -beta --alpha. -1.2 -1.2.3.DEV -1.2-SNAPSHOT -1.2.31.2.3----RC-SNAPSHOT.12.09.1--..12+788 -1.2-RC-SNAPSHOT --1.0.3-gamma+b7718 -+justmeta -1.1.1+ -1.1.1- -#99999999999999999999999.999999999999999999.99999999999999999----RC-SNAPSHOT.12.09.1--------------------------------..12 \ No newline at end of file diff --git a/tests/models/grammar/files/regex.txt b/tests/models/grammar/files/regex.txt deleted file mode 100644 index d776a132..00000000 --- a/tests/models/grammar/files/regex.txt +++ /dev/null @@ -1,145 +0,0 @@ -abc#abc#true -abc#zabcd#true -abc#bc#false -abc#ab#false -^abc#abc#true -^abc#abcbdc#true -^abc#abcabc#true -^abc#zabcabc#false -abc$#abcabc#true -abc$#zabcabc#true -abc$#abcabcz#false -a|b#abcabcz#true -a|b#zczcz#false -^abc|abc$#abcabc#true -^abc|abc$#zabcab#false -ab{2,4}c#abbc#true -ab{2,4}c#abbbc#true -ab{2,4}c#abbbbc#true -ab{2,4}c#abc#false -ab{2,4}c#abzbbc#false -ab{2,4}c#abbbbbbbbbbc#false -ab{2,}c#abbc#true -ab{2,}c#abbbc#true -ab{2,}c#abbbbc#true -ab{2,}c#abc#false -ab{2,}c#abzbbc#false -ab{2,}c#abbbbbbbbbbc#true -ab*c#ac#true -ab*c#abc#true -ab*c#abbc#true -ab*c#abbbc#true -ab*c#ab#false -ab*c#bc#false -ab+c#ac#false -ab+c#abc#true -ab+c#abbc#true -ab+c#abbbc#true -ab+c#ab#false -ab+c#bc#false -ab?c#ac#true -ab?c#abc#true -ab?c#abbc#false -ab?c#abbbc#false -ab?c#ab#false -ab?c#bc#false -a.c#abc#true -a.c#adc#true -a.c#azc#true -a.c#xdc#false -a.c#ac#false -a\.c#abc#false -a\.c#adc#false -a\.c#azc#false -a\.c#xdc#false -a\.c#ac#false -a\.c#a.c#true -[abc]#a#true -[abc]#b#true -[abc]#c#true -[abc]#z#false -[abc]#ab#true -[abc]#ac#true -[Aa]bc#a#false -[Aa]bc#b#false -[Aa]bc#c#false -[Aa]bc#z#false -[Aa]bc#ab#false -[Aa]bc#ac#false -[Aa]bc#abc#true -[Aa]bc#Abc#true -[abc]+#a#true -[abc]+#aba#true -[abc]+#abba#true -[abc]+#acbabcacaa#true -[abc]+#axbaxcaxax#true -[abc]+#xxzyxzyxyx#false -[^abc]+#acbaccacaa#false -[^abc]+#acbacaaa#false -[^abc]+#aa#false -[^abc]+#xzy#true -\d\d#11#true -\d\d#a1#false -\d\d#1b1a1#false -\d\d#1a1#false -\w+#foo#true -\w+#12bar8#true -\w+#foo_1#true -\w+#foo-1#true -\w+#foo- 1#true -\w+#foo- %$1#true -\w+#%$#false -\W+#foo#false -\W+#12bar8#false -\W+#foo_1#false -\W+#foo-1#true -\W+#foo_ 1#true -\W+#foo1#false -\W+#%$#true -100\s*mk#100mk#true -100\s*mk#100 mk#true -100\s*mk#100 X mk#false -abc\b#abc!#true -abc\b#abcd#false -perl\B#perlert#true -perl\B#perl stuff#false -(abc){3}#abcabcabc#true -(abc){3}#abcacabc#false -(abc){3}#abc#false -^[a-z0-9_-]{3,16}$#my-us3r_n4m3#true -^[a-z0-9_-]{3,16}$#commonusername#true -^[a-z0-9_-]{3,16}$#n0#false -^[a-z0-9_-]{3,16}$#th1s1s-wayt00_l0ngt0beausername#false -^[a-z0-9-]+$#my-title-here#true -^[a-z0-9-]+$#my_title_here#false -^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$#john@doe.com#true -^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$#john@doe.something#false -^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$#johndoe.sg#false -^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$#http://split.io/about#true -^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$#http://google.com/some/file!.html#false -^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$#73.60.124.136#true -^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$#256.60.124.136#false -^\d+$#123#true -^\d+$#4323#true -^\d+$#4566663#true -^\d+$#-10#false -^\d+$#456.666.3#false -^-\d+$#4566663#false -^-\d+$#-10#true -^-\d+$#456.666.3#false -^-?\d+$#3534#true -^-?\d+$#-3534#true -^-?\d+$#35.34#false -^-?\d+$#-35.34#false -^\d*\.?\d+$#12.3#true -^\d*\.?\d+$#-12.3#false -^-\d*\.?\d+$#12.3#false -^-\d*\.?\d+$#-12.3#true -^-?\d*\.?\d+$#12.3#true -^-?\d*\.?\d+$#-12.3#true -^-?\d*\.?\d+$#-1a2.a3#false -^(19|20)\d{2}$#1900#true -^(19|20)\d{2}$#2005#true -^(19|20)\d{2}$#1810#false -^([1-9]|0[1-9]|[12][0-9]|3[01])\D([1-9]|0[1-9]|1[012])\D(19[0-9][0-9]|20[0-9][0-9])$#11/11/2011#true -^([1-9]|0[1-9]|[12][0-9]|3[01])\D([1-9]|0[1-9]|1[012])\D(19[0-9][0-9]|20[0-9][0-9])$#13/13/2011#false diff --git a/tests/models/grammar/files/valid-semantic-versions.csv b/tests/models/grammar/files/valid-semantic-versions.csv deleted file mode 100644 index f491e77f..00000000 --- a/tests/models/grammar/files/valid-semantic-versions.csv +++ /dev/null @@ -1,25 +0,0 @@ -higher,lower -1.1.2,1.1.1 -1.0.0,1.0.0-rc.1 -1.1.0-rc.1,1.0.0-beta.11 -1.0.0-beta.11,1.0.0-beta.2 -1.0.0-beta.2,1.0.0-beta -1.0.0-beta,1.0.0-alpha.beta -1.0.0-alpha.beta,1.0.0-alpha.1 -1.0.0-alpha.1,1.0.0-alpha -2.2.2-rc.2+metadata-lalala,2.2.2-rc.1.2 -1.2.3,0.0.4 -1.1.2+meta,1.1.2-prerelease+meta -1.0.0-beta,1.0.0-alpha -1.0.0-alpha0.valid,1.0.0-alpha.0valid -1.0.0-rc.1+build.1,1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay -10.2.3-DEV-SNAPSHOT,1.2.3-SNAPSHOT-123 -1.1.1-rc2,1.0.0-0A.is.legal -1.2.3----RC-SNAPSHOT.12.9.1--.12+788,1.2.3----R-S.12.9.1--.12+meta -1.2.3----RC-SNAPSHOT.12.9.1--.12.88,1.2.3----RC-SNAPSHOT.12.9.1--.12 -9223372036854775807.9223372036854775807.9223372036854775807,9223372036854775807.9223372036854775807.9223372036854775806 -1.1.1-alpha.beta.rc.build.java.pr.support.10,1.1.1-alpha.beta.rc.build.java.pr.support -1.1.2,1.1.1 -1.2.1,1.1.1 -2.1.1,1.1.1 -1.1.1-rc.1,1.1.1-rc.0 \ No newline at end of file diff --git a/tests/models/grammar/test_conditions.py b/tests/models/grammar/test_conditions.py deleted file mode 100644 index abdcf0a1..00000000 --- a/tests/models/grammar/test_conditions.py +++ /dev/null @@ -1,78 +0,0 @@ -"""Condition model tests module.""" - -from splitio.models.grammar import condition -from splitio.models.grammar import partitions -from splitio.models.grammar import matchers - -class ConditionTests(object): - """Test the condition object model.""" - - raw = { - 'partitions': [ - {'treatment': 'on', 'size': 50}, - {'treatment': 'off', 'size': 50} - ], - 'contitionType': 'WHITELIST', - 'label': 'some_label', - 'matcherGroup': { - 'matchers': [ - { - 'matcherType': 'ALL_KEYS', - 'negate': False, - } - ], - 'combiner': 'AND' - } - } - - def test_parse(self): - """Test parsing from raw dict.""" - parsed = condition.from_raw(self.raw) - assert isinstance(parsed, condition.Condition) - assert parsed.label == 'some_label' - assert parsed.condition_type == condition.ConditionType.WHITELIST - assert isinstance(parsed.matchers[0], matchers.AllKeysMatcher) - assert isinstance(parsed.partitions[0], partitions.Partition) - assert parsed.partitions[0].treatment == 'on' - assert parsed.partitions[0].size == 50 - assert parsed.partitions[1].treatment == 'off' - assert parsed.partitions[1].size == 50 - assert parsed._combiner == condition._MATCHER_COMBINERS['AND'] - - def test_segment_names(self, mocker): - """Test fetching segment_names.""" - matcher1 = mocker.Mock(spec=matchers.UserDefinedSegmentMatcher) - matcher2 = mocker.Mock(spec=matchers.UserDefinedSegmentMatcher) - matcher1._segment_name = 'segment1' - matcher2._segment_name = 'segment2' - cond = condition.Condition([matcher1, matcher2], condition._MATCHER_COMBINERS['AND'], [], 'some_label') - assert cond.get_segment_names() == ['segment1', 'segment2'] - - def test_to_json(self): - """Test JSON serialization of a condition.""" - as_json = condition.from_raw(self.raw).to_json() - assert as_json['partitions'] == [ - {'treatment': 'on', 'size': 50}, - {'treatment': 'off', 'size': 50} - ] - assert as_json['conditionType'] == 'WHITELIST' - assert as_json['label'] == 'some_label' - assert as_json['matcherGroup']['matchers'][0]['matcherType'] == 'ALL_KEYS' - assert as_json['matcherGroup']['matchers'][0]['negate'] == False - assert as_json['matcherGroup']['combiner'] == 'AND' - - def test_matches(self, mocker): - """Test that matches works properly.""" - matcher1_mock = mocker.Mock(spec=matchers.base.Matcher) - matcher2_mock = mocker.Mock(spec=matchers.base.Matcher) - matcher1_mock.evaluate.return_value = True - matcher2_mock.evaluate.return_value = True - cond = condition.Condition( - [matcher1_mock, matcher2_mock], - condition._MATCHER_COMBINERS['AND'], - [partitions.Partition('on', 50), partitions.Partition('off', 50)], - 'some_label' - ) - assert cond.matches('some_key', {'a': 1}, {'some_context_option': 0}) == True - assert matcher1_mock.evaluate.mock_calls == [mocker.call('some_key', {'a': 1}, {'some_context_option': 0})] - assert matcher2_mock.evaluate.mock_calls == [mocker.call('some_key', {'a': 1}, {'some_context_option': 0})] diff --git a/tests/models/grammar/test_matchers.py b/tests/models/grammar/test_matchers.py deleted file mode 100644 index 71922431..00000000 --- a/tests/models/grammar/test_matchers.py +++ /dev/null @@ -1,1169 +0,0 @@ -"""Matchers tests module.""" -#pylint: disable=protected-access,line-too-long,unsubscriptable-object - -import abc -import calendar -import json -import os.path -import re -import pytest - -from datetime import datetime - -from splitio.models.grammar import matchers -from splitio.models.grammar.matchers.prerequisites import PrerequisitesMatcher -from splitio.models import splits -from splitio.models import rule_based_segments -from splitio.models.grammar import condition -from splitio.models.grammar.matchers.utils.utils import Semver -from splitio.storage import SegmentStorage -from splitio.engine.evaluator import Evaluator, EvaluationContext -from tests.integration import splits_json - -class MatcherTestsBase(object): - """Abstract class to make sure we test all relevant methods.""" - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def test_from_raw(self, mocker): - """Test parsing from raw json/dict.""" - pass - - @abc.abstractmethod - def test_matcher_behaviour(self, mocker): - """Test if the matcher works properly.""" - pass - - @abc.abstractmethod - def test_to_json(self): - """Test that the object serializes to JSON properly.""" - pass - - -class AllKeysMatcherTests(MatcherTestsBase): - """Test AllKeys matcher methods.""" - - raw = { - 'matcherType': "ALL_KEYS", - 'negate': False - } - - def test_from_raw(self, mocker): - """Test parsing from raw json/dict.""" - parsed = matchers.from_raw(self.raw) - assert isinstance(parsed, matchers.AllKeysMatcher) - - def test_matcher_behaviour(self, mocker): - """Test if the matcher works properly.""" - matcher = matchers.AllKeysMatcher(self.raw) - assert matcher.evaluate(None) is False - assert matcher.evaluate('asd') is True - assert matcher.evaluate('asd', {'a': 1}, {}) is True - - def test_to_json(self): - """Test that the object serializes to JSON properly.""" - as_json = matchers.AllKeysMatcher(self.raw).to_json() - assert as_json['matcherType'] == 'ALL_KEYS' - - -class BetweenMatcherTests(MatcherTestsBase): - """Test in between matcher behaviour.""" - - raw_number = { - 'matcherType': 'BETWEEN', - 'negate': False, - 'betweenMatcherData': { - 'start': 1, - 'end': 3, - 'dataType': 'NUMBER' - } - } - - raw_date = { - 'matcherType': 'BETWEEN', - 'negate': False, - 'betweenMatcherData': { - 'start': int(calendar.timegm((datetime(2019, 12, 21, 9, 30, 45)).timetuple())) * 1000, - 'end': int(calendar.timegm((datetime(2019, 12, 23, 9, 30, 45)).timetuple())) * 1000, - 'dataType': 'DATETIME' - } - } - - def test_from_raw(self, mocker): - """Test parsing from raw json/dict.""" - parsed_number = matchers.from_raw(self.raw_number) - assert isinstance(parsed_number, matchers.BetweenMatcher) - assert parsed_number._data_type == 'NUMBER' - assert parsed_number._negate is False - assert parsed_number._original_lower == 1 - assert parsed_number._original_upper == 3 - assert parsed_number._lower == 1 - assert parsed_number._upper == 3 - - parsed_date = matchers.from_raw(self.raw_date) - assert isinstance(parsed_number, matchers.BetweenMatcher) - assert parsed_date._data_type == 'DATETIME' - assert parsed_date._original_lower == int(calendar.timegm((datetime(2019, 12, 21, 9, 30, 45)).timetuple())) * 1000 - assert parsed_date._original_upper == int(calendar.timegm((datetime(2019, 12, 23, 9, 30, 45)).timetuple())) * 1000 - assert parsed_date._lower == int(calendar.timegm((datetime(2019, 12, 21, 9, 30, 0)).timetuple())) - assert parsed_date._upper == int(calendar.timegm((datetime(2019, 12, 23, 9, 30, 0)).timetuple())) - - def test_matcher_behaviour(self, mocker): - """Test if the matcher works properly.""" - parsed_number = matchers.BetweenMatcher(self.raw_number) - assert parsed_number.evaluate(0) is False - assert parsed_number.evaluate(1) is True - assert parsed_number.evaluate(2) is True - assert parsed_number.evaluate(3) is True - assert parsed_number.evaluate(4) is False - assert parsed_number.evaluate('a') is False - assert parsed_number.evaluate([]) is False - assert parsed_number.evaluate({}) is False - assert parsed_number.evaluate(True) is False - assert parsed_number.evaluate(object()) is False - - - parsed_date = matchers.BetweenMatcher(self.raw_date) - assert parsed_date.evaluate(int(calendar.timegm((datetime(2019, 12, 20, 9, 30)).timetuple()))) is False - assert parsed_date.evaluate(int(calendar.timegm((datetime(2019, 12, 21, 9, 30, 45)).timetuple()))) is True - assert parsed_date.evaluate(int(calendar.timegm((datetime(2019, 12, 22, 9, 30)).timetuple()))) is True - assert parsed_date.evaluate(int(calendar.timegm((datetime(2019, 12, 23, 9, 30, 45)).timetuple()))) is True - assert parsed_date.evaluate(int(calendar.timegm((datetime(2019, 12, 24, 9, 30)).timetuple()))) is False - assert parsed_date.evaluate('a') is False - assert parsed_date.evaluate([]) is False - assert parsed_date.evaluate({}) is False - assert parsed_date.evaluate(True) is False - assert parsed_date.evaluate(object()) is False - - def test_to_json(self): - """Test that the object serializes to JSON properly.""" - as_json_number = matchers.BetweenMatcher(self.raw_number).to_json() - assert as_json_number['betweenMatcherData']['start'] == 1 - assert as_json_number['betweenMatcherData']['end'] == 3 - assert as_json_number['betweenMatcherData']['dataType'] == 'NUMBER' - - as_json_date = matchers.BetweenMatcher(self.raw_date).to_json() - assert as_json_date['betweenMatcherData']['start'] == int(calendar.timegm((datetime(2019, 12, 21, 9, 30, 45)).timetuple()) * 1000) - assert as_json_date['betweenMatcherData']['end'] == int(calendar.timegm((datetime(2019, 12, 23, 9, 30, 45)).timetuple()) * 1000) - assert as_json_date['betweenMatcherData']['dataType'] == 'DATETIME' - - -class EqualToMatcherTests(MatcherTestsBase): - """Test equal to matcher.""" - - raw_number = { - 'matcherType': 'EQUAL_TO', - 'negate': False, - 'unaryNumericMatcherData': { - 'value': 5, - 'dataType': 'NUMBER' - } - } - - raw_date = { - 'matcherType': 'EQUAL_TO', - 'negate': False, - 'unaryNumericMatcherData': { - 'value': int(calendar.timegm((datetime(2019, 12, 21, 9, 30, 45)).timetuple())) * 1000, - 'dataType': 'DATETIME' - } - } - - def test_from_raw(self, mocker): - """Test parsing from raw json/dict.""" - parsed_number = matchers.from_raw(self.raw_number) - assert isinstance(parsed_number, matchers.EqualToMatcher) - assert parsed_number._data_type == 'NUMBER' - assert parsed_number._original_value == 5 - assert parsed_number._value == 5 - - parsed_date = matchers.from_raw(self.raw_date) - assert isinstance(parsed_date, matchers.EqualToMatcher) - assert parsed_date._data_type == 'DATETIME' - assert parsed_date._original_value == int(calendar.timegm((datetime(2019, 12, 21, 9, 30, 45, 0)).timetuple())) * 1000 - assert parsed_date._value == int(calendar.timegm((datetime(2019, 12, 21, 0, 0, 0)).timetuple())) - - def test_matcher_behaviour(self, mocker): - """Test if the matcher works properly.""" - matcher_number = matchers.EqualToMatcher(self.raw_number) - assert matcher_number.evaluate(4) is False - assert matcher_number.evaluate(5) is True - assert matcher_number.evaluate(6) is False - assert matcher_number.evaluate('a') is False - assert matcher_number.evaluate([]) is False - assert matcher_number.evaluate({}) is False - assert matcher_number.evaluate(True) is False - assert matcher_number.evaluate(object()) is False - - matcher_date = matchers.EqualToMatcher(self.raw_date) - assert matcher_date.evaluate(int(calendar.timegm((datetime(2019, 12, 21, 9, 30, 45)).timetuple()))) is True - assert matcher_date.evaluate(int(calendar.timegm((datetime(2019, 12, 21, 0, 0, 0)).timetuple()))) is True - assert matcher_date.evaluate(int(calendar.timegm((datetime(2019, 12, 20, 0, 0, 0)).timetuple()))) is False - assert matcher_date.evaluate(int(calendar.timegm((datetime(2019, 12, 22, 0, 0, 0)).timetuple()))) is False - assert matcher_date.evaluate('a') is False - assert matcher_date.evaluate([]) is False - assert matcher_date.evaluate({}) is False - assert matcher_date.evaluate(True) is False - assert matcher_date.evaluate(object()) is False - - - def test_to_json(self): - """Test that the object serializes to JSON properly.""" - as_json_number = matchers.from_raw(self.raw_number).to_json() - assert as_json_number['unaryNumericMatcherData']['dataType'] == 'NUMBER' - assert as_json_number['unaryNumericMatcherData']['value'] == 5 - assert as_json_number['matcherType'] == 'EQUAL_TO' - assert as_json_number['negate'] is False - - as_json_number = matchers.from_raw(self.raw_date).to_json() - assert as_json_number['unaryNumericMatcherData']['dataType'] == 'DATETIME' - assert as_json_number['unaryNumericMatcherData']['value'] == int(calendar.timegm((datetime(2019, 12, 21, 9, 30, 45)).timetuple())) * 1000 - assert as_json_number['matcherType'] == 'EQUAL_TO' - assert as_json_number['negate'] is False - - -class GreaterOrEqualMatcherTests(MatcherTestsBase): - """Test greater or equal matcher.""" - - raw_number = { - 'matcherType': 'GREATER_THAN_OR_EQUAL_TO', - 'negate': False, - 'unaryNumericMatcherData': { - 'value': 5, - 'dataType': 'NUMBER' - } - } - - raw_date = { - 'matcherType': 'GREATER_THAN_OR_EQUAL_TO', - 'negate': False, - 'unaryNumericMatcherData': { - 'value': int(calendar.timegm((datetime(2019, 12, 21, 9, 30, 45)).timetuple())) * 1000, - 'dataType': 'DATETIME' - } - } - - def test_from_raw(self, mocker): - """Test parsing from raw json/dict.""" - parsed_number = matchers.from_raw(self.raw_number) - assert isinstance(parsed_number, matchers.GreaterThanOrEqualMatcher) - assert parsed_number._data_type == 'NUMBER' - assert parsed_number._original_value == 5 - assert parsed_number._value == 5 - assert parsed_number.evaluate('a') is False - assert parsed_number.evaluate([]) is False - assert parsed_number.evaluate({}) is False - assert parsed_number.evaluate(True) is False - assert parsed_number.evaluate(object()) is False - - parsed_date = matchers.from_raw(self.raw_date) - assert isinstance(parsed_date, matchers.GreaterThanOrEqualMatcher) - assert parsed_date._data_type == 'DATETIME' - assert parsed_date._original_value == int(calendar.timegm((datetime(2019, 12, 21, 9, 30, 45, 0)).timetuple())) * 1000 - assert parsed_date._value == int(calendar.timegm((datetime(2019, 12, 21, 9, 30, 0)).timetuple())) - assert parsed_date.evaluate('a') is False - assert parsed_date.evaluate([]) is False - assert parsed_date.evaluate({}) is False - assert parsed_date.evaluate(True) is False - assert parsed_date.evaluate(object()) is False - - - def test_matcher_behaviour(self, mocker): - """Test if the matcher works properly.""" - matcher_number = matchers.GreaterThanOrEqualMatcher(self.raw_number) - assert matcher_number.evaluate(4) is False - assert matcher_number.evaluate(5) is True - assert matcher_number.evaluate(6) is True - assert matcher_number.evaluate('a') is False - assert matcher_number.evaluate([]) is False - assert matcher_number.evaluate({}) is False - assert matcher_number.evaluate(True) is False - assert matcher_number.evaluate(object()) is False - - matcher_date = matchers.GreaterThanOrEqualMatcher(self.raw_date) - assert matcher_date.evaluate(int(calendar.timegm((datetime(2019, 12, 20, 0, 0, 0)).timetuple()))) is False - assert matcher_date.evaluate(int(calendar.timegm((datetime(2019, 12, 21, 0, 0, 0)).timetuple()))) is False - assert matcher_date.evaluate(int(calendar.timegm((datetime(2019, 12, 21, 9, 30, 0)).timetuple()))) is True - assert matcher_date.evaluate(int(calendar.timegm((datetime(2019, 12, 21, 9, 30, 45)).timetuple()))) is True - assert matcher_date.evaluate(int(calendar.timegm((datetime(2019, 12, 22, 0, 0, 0)).timetuple()))) is True - assert matcher_date.evaluate('a') is False - assert matcher_date.evaluate([]) is False - assert matcher_date.evaluate({}) is False - assert matcher_date.evaluate(True) is False - assert matcher_date.evaluate(object()) is False - - def test_to_json(self): - """Test that the object serializes to JSON properly.""" - as_json_number = matchers.from_raw(self.raw_number).to_json() - assert as_json_number['unaryNumericMatcherData']['dataType'] == 'NUMBER' - assert as_json_number['unaryNumericMatcherData']['value'] == 5 - assert as_json_number['matcherType'] == 'GREATER_THAN_OR_EQUAL_TO' - assert as_json_number['negate'] is False - - as_json_number = matchers.from_raw(self.raw_date).to_json() - assert as_json_number['unaryNumericMatcherData']['dataType'] == 'DATETIME' - assert as_json_number['unaryNumericMatcherData']['value'] == int(calendar.timegm((datetime(2019, 12, 21, 9, 30, 45)).timetuple())) * 1000 - assert as_json_number['matcherType'] == 'GREATER_THAN_OR_EQUAL_TO' - assert as_json_number['negate'] is False - - -class LessOrEqualMatcherTests(MatcherTestsBase): - """Test less than or equal matcher.""" - - raw_number = { - 'matcherType': 'LESS_THAN_OR_EQUAL_TO', - 'negate': False, - 'unaryNumericMatcherData': { - 'value': 5, - 'dataType': 'NUMBER' - } - } - - raw_date = { - 'matcherType': 'LESS_THAN_OR_EQUAL_TO', - 'negate': False, - 'unaryNumericMatcherData': { - 'value': int(calendar.timegm((datetime(2019, 12, 21, 9, 30, 45)).timetuple())) * 1000, - 'dataType': 'DATETIME' - } - } - - def test_from_raw(self, mocker): - """Test parsing from raw json/dict.""" - parsed_number = matchers.from_raw(self.raw_number) - assert isinstance(parsed_number, matchers.LessThanOrEqualMatcher) - assert parsed_number._data_type == 'NUMBER' - assert parsed_number._original_value == 5 - assert parsed_number._value == 5 - - parsed_date = matchers.from_raw(self.raw_date) - assert isinstance(parsed_date, matchers.LessThanOrEqualMatcher) - assert parsed_date._data_type == 'DATETIME' - assert parsed_date._original_value == int(calendar.timegm((datetime(2019, 12, 21, 9, 30, 45, 0)).timetuple())) * 1000 - assert parsed_date._value == int(calendar.timegm((datetime(2019, 12, 21, 9, 30, 0)).timetuple())) - - def test_matcher_behaviour(self, mocker): - """Test if the matcher works properly.""" - matcher_number = matchers.LessThanOrEqualMatcher(self.raw_number) - assert matcher_number.evaluate(4) is True - assert matcher_number.evaluate(5) is True - assert matcher_number.evaluate(6) is False - assert matcher_number.evaluate('a') is False - assert matcher_number.evaluate([]) is False - assert matcher_number.evaluate({}) is False - assert matcher_number.evaluate(True) is False - assert matcher_number.evaluate(object()) is False - - - matcher_date = matchers.LessThanOrEqualMatcher(self.raw_date) - assert matcher_date.evaluate(int(calendar.timegm((datetime(2019, 12, 20, 0, 0, 0)).timetuple()))) is True - assert matcher_date.evaluate(int(calendar.timegm((datetime(2019, 12, 21, 0, 0, 0)).timetuple()))) is True - assert matcher_date.evaluate(int(calendar.timegm((datetime(2019, 12, 21, 9, 30, 0)).timetuple()))) is True - assert matcher_date.evaluate(int(calendar.timegm((datetime(2019, 12, 21, 9, 31, 45)).timetuple()))) is False - assert matcher_date.evaluate(int(calendar.timegm((datetime(2019, 12, 22, 0, 0, 0)).timetuple()))) is False - assert matcher_date.evaluate('a') is False - assert matcher_date.evaluate([]) is False - assert matcher_date.evaluate({}) is False - assert matcher_date.evaluate(True) is False - assert matcher_date.evaluate(object()) is False - - - def test_to_json(self): - """Test that the object serializes to JSON properly.""" - as_json_number = matchers.from_raw(self.raw_number).to_json() - assert as_json_number['unaryNumericMatcherData']['dataType'] == 'NUMBER' - assert as_json_number['unaryNumericMatcherData']['value'] == 5 - assert as_json_number['matcherType'] == 'LESS_THAN_OR_EQUAL_TO' - assert as_json_number['negate'] is False - - as_json_number = matchers.from_raw(self.raw_date).to_json() - assert as_json_number['unaryNumericMatcherData']['dataType'] == 'DATETIME' - assert as_json_number['unaryNumericMatcherData']['value'] == int(calendar.timegm((datetime(2019, 12, 21, 9, 30, 45)).timetuple())) * 1000 - assert as_json_number['matcherType'] == 'LESS_THAN_OR_EQUAL_TO' - assert as_json_number['negate'] is False - - -class UserDefinedSegmentMatcherTests(MatcherTestsBase): - """Test user defined segment matcher.""" - - raw = { - 'matcherType': 'IN_SEGMENT', - 'negate': False, - 'userDefinedSegmentMatcherData': { - 'segmentName': 'some_segment' - } - } - - def test_from_raw(self, mocker): - """Test parsing from raw json/dict.""" - parsed = matchers.from_raw(self.raw) - assert isinstance(parsed, matchers.UserDefinedSegmentMatcher) - assert parsed._segment_name == 'some_segment' - - def test_matcher_behaviour(self, mocker): - """Test if the matcher works properly.""" - matcher = matchers.UserDefinedSegmentMatcher(self.raw) - - # Test that if the key if the storage wrapper finds the key in the segment, it matches. - assert matcher.evaluate('some_key', {}, {'evaluator': None, 'ec': EvaluationContext([],{'some_segment': True}, {})}) is True - # Test that if the key if the storage wrapper doesn't find the key in the segment, it fails. - assert matcher.evaluate('some_key', {}, {'evaluator': None, 'ec': EvaluationContext([], {'some_segment': False}, {})}) is False - - def test_to_json(self): - """Test that the object serializes to JSON properly.""" - as_json = matchers.UserDefinedSegmentMatcher(self.raw).to_json() - assert as_json['userDefinedSegmentMatcherData']['segmentName'] == 'some_segment' - assert as_json['matcherType'] == 'IN_SEGMENT' - assert as_json['negate'] is False - - -class WhitelistMatcherTests(MatcherTestsBase): - """Test whitelist matcher.""" - - raw = { - 'matcherType': 'WHITELIST', - 'negate': False, - 'whitelistMatcherData': { - 'whitelist': ['key1', 'key2', 'key3'], - } - } - - def test_from_raw(self, mocker): - """Test parsing from raw json/dict.""" - parsed = matchers.from_raw(self.raw) - assert isinstance(parsed, matchers.WhitelistMatcher) - assert parsed._whitelist == frozenset(['key1', 'key2', 'key3']) - - def test_matcher_behaviour(self, mocker): - """Test if the matcher works properly.""" - matcher = matchers.WhitelistMatcher(self.raw) - assert matcher.evaluate('key1') is True - assert matcher.evaluate('key2') is True - assert matcher.evaluate('key3') is True - assert matcher.evaluate('key4') is False - assert matcher.evaluate(None) is False - - assert matcher.evaluate([]) is False - assert matcher.evaluate({}) is False - assert matcher.evaluate(123) is False - assert matcher.evaluate(True) is False - assert matcher.evaluate(False) is False - - def test_to_json(self): - """Test that the object serializes to JSON properly.""" - as_json = matchers.WhitelistMatcher(self.raw).to_json() - assert 'key1' in as_json['whitelistMatcherData']['whitelist'] - assert 'key2' in as_json['whitelistMatcherData']['whitelist'] - assert 'key3' in as_json['whitelistMatcherData']['whitelist'] - assert as_json['matcherType'] == 'WHITELIST' - assert as_json['negate'] is False - - -class StartsWithMatcherTests(MatcherTestsBase): - """Test StartsWith matcher.""" - - raw = { - 'matcherType': 'STARTS_WITH', - 'negate': False, - 'whitelistMatcherData': { - 'whitelist': ['key1', 'key2', 'key3'], - } - } - - def test_from_raw(self, mocker): - """Test parsing from raw json/dict.""" - parsed = matchers.from_raw(self.raw) - assert isinstance(parsed, matchers.StartsWithMatcher) - assert parsed._whitelist == frozenset(['key1', 'key2', 'key3']) - - def test_matcher_behaviour(self, mocker): - """Test if the matcher works properly.""" - matcher = matchers.StartsWithMatcher(self.raw) - assert matcher.evaluate('key1AA') is True - assert matcher.evaluate('key2BB') is True - assert matcher.evaluate('key3CC') is True - assert matcher.evaluate('key4DD') is False - assert matcher.evaluate('Akey1A') is False - assert matcher.evaluate(None) is False - assert matcher.evaluate([]) is False - assert matcher.evaluate({}) is False - assert matcher.evaluate(123) is False - assert matcher.evaluate(True) is False - assert matcher.evaluate(False) is False - - def test_to_json(self): - """Test that the object serializes to JSON properly.""" - as_json = matchers.StartsWithMatcher(self.raw).to_json() - assert 'key1' in as_json['whitelistMatcherData']['whitelist'] - assert 'key2' in as_json['whitelistMatcherData']['whitelist'] - assert 'key3' in as_json['whitelistMatcherData']['whitelist'] - assert as_json['matcherType'] == 'STARTS_WITH' - assert as_json['negate'] is False - - -class EndsWithMatcherTests(MatcherTestsBase): - """Test EndsWith matcher.""" - - raw = { - 'matcherType': 'ENDS_WITH', - 'negate': False, - 'whitelistMatcherData': { - 'whitelist': ['key1', 'key2', 'key3'], - } - } - - def test_from_raw(self, mocker): - """Test parsing from raw json/dict.""" - parsed = matchers.from_raw(self.raw) - assert isinstance(parsed, matchers.EndsWithMatcher) - assert parsed._whitelist == frozenset(['key1', 'key2', 'key3']) - - def test_matcher_behaviour(self, mocker): - """Test if the matcher works properly.""" - matcher = matchers.EndsWithMatcher(self.raw) - assert matcher.evaluate('AAkey1') is True - assert matcher.evaluate('BBkey2') is True - assert matcher.evaluate('CCkey3') is True - assert matcher.evaluate('DDkey4') is False - assert matcher.evaluate('Akey1A') is False - assert matcher.evaluate(None) is False - assert matcher.evaluate([]) is False - assert matcher.evaluate({}) is False - assert matcher.evaluate(123) is False - assert matcher.evaluate(True) is False - assert matcher.evaluate(False) is False - - def test_to_json(self): - """Test that the object serializes to JSON properly.""" - as_json = matchers.EndsWithMatcher(self.raw).to_json() - assert 'key1' in as_json['whitelistMatcherData']['whitelist'] - assert 'key2' in as_json['whitelistMatcherData']['whitelist'] - assert 'key3' in as_json['whitelistMatcherData']['whitelist'] - assert as_json['matcherType'] == 'ENDS_WITH' - assert as_json['negate'] is False - - -class ContainsStringMatcherTests(MatcherTestsBase): - """Test string matcher.""" - - raw = { - 'matcherType': 'CONTAINS_STRING', - 'negate': False, - 'whitelistMatcherData': { - 'whitelist': ['key1', 'key2', 'key3'], - } - } - - def test_from_raw(self, mocker): - """Test parsing from raw json/dict.""" - parsed = matchers.from_raw(self.raw) - assert isinstance(parsed, matchers.ContainsStringMatcher) - assert parsed._whitelist == frozenset(['key1', 'key2', 'key3']) - - def test_matcher_behaviour(self, mocker): - """Test if the matcher works properly.""" - matcher = matchers.ContainsStringMatcher(self.raw) - assert matcher.evaluate('AAkey1') is True - assert matcher.evaluate('BBkey2') is True - assert matcher.evaluate('CCkey3') is True - assert matcher.evaluate('Akey1A') is True - assert matcher.evaluate('DDkey4') is False - assert matcher.evaluate('asdsad') is False - assert matcher.evaluate(None) is False - assert matcher.evaluate([]) is False - assert matcher.evaluate({}) is False - assert matcher.evaluate(123) is False - assert matcher.evaluate(True) is False - assert matcher.evaluate(False) is False - - def test_to_json(self): - """Test that the object serializes to JSON properly.""" - as_json = matchers.ContainsStringMatcher(self.raw).to_json() - assert 'key1' in as_json['whitelistMatcherData']['whitelist'] - assert 'key2' in as_json['whitelistMatcherData']['whitelist'] - assert 'key3' in as_json['whitelistMatcherData']['whitelist'] - assert as_json['matcherType'] == 'CONTAINS_STRING' - assert as_json['negate'] is False - - -class AllOfSetMatcherTests(MatcherTestsBase): - """Test all of set matcher.""" - - raw = { - 'matcherType': 'CONTAINS_ALL_OF_SET', - 'negate': False, - 'whitelistMatcherData': { - 'whitelist': ['key1', 'key2', 'key3'], - } - } - - def test_from_raw(self, mocker): - """Test parsing from raw json/dict.""" - parsed = matchers.from_raw(self.raw) - assert isinstance(parsed, matchers.ContainsAllOfSetMatcher) - assert parsed._whitelist == frozenset(['key1', 'key2', 'key3']) - - def test_matcher_behaviour(self, mocker): - """Test if the matcher works properly.""" - matcher = matchers.ContainsAllOfSetMatcher(self.raw) - assert matcher.evaluate(['key1', 'key2', 'key3']) is True - assert matcher.evaluate(['key1', 'key2', 'key3', 'key4']) is True - assert matcher.evaluate(['key4', 'key3', 'key1', 'key5', 'key2']) is True - assert matcher.evaluate(['key1', 'key2']) is False - assert matcher.evaluate([]) is False - assert matcher.evaluate('asdsad') is False - assert matcher.evaluate(3) is False - assert matcher.evaluate(None) is False - assert matcher.evaluate({}) is False - assert matcher.evaluate(object()) is False - assert matcher.evaluate(True) is False - - def test_to_json(self): - """Test that the object serializes to JSON properly.""" - as_json = matchers.ContainsAllOfSetMatcher(self.raw).to_json() - assert 'key1' in as_json['whitelistMatcherData']['whitelist'] - assert 'key2' in as_json['whitelistMatcherData']['whitelist'] - assert 'key3' in as_json['whitelistMatcherData']['whitelist'] - assert as_json['matcherType'] == 'CONTAINS_ALL_OF_SET' - assert as_json['negate'] is False - - -class AnyOfSetMatcherTests(MatcherTestsBase): - """Test any of set matcher.""" - - raw = { - 'matcherType': 'CONTAINS_ANY_OF_SET', - 'negate': False, - 'whitelistMatcherData': { - 'whitelist': ['key1', 'key2', 'key3'], - } - } - - def test_from_raw(self, mocker): - """Test parsing from raw json/dict.""" - parsed = matchers.from_raw(self.raw) - assert isinstance(parsed, matchers.ContainsAnyOfSetMatcher) - assert parsed._whitelist == frozenset(['key1', 'key2', 'key3']) - - def test_matcher_behaviour(self, mocker): - """Test if the matcher works properly.""" - matcher = matchers.ContainsAnyOfSetMatcher(self.raw) - assert matcher.evaluate(['key1', 'key2', 'key3']) is True - assert matcher.evaluate(['key1', 'key2', 'key3', 'key4']) is True - assert matcher.evaluate(['key4', 'key3', 'key1', 'key5', 'key2']) is True - assert matcher.evaluate(['key1', 'key2']) is True - assert matcher.evaluate([]) is False - assert matcher.evaluate('asdsad') is False - assert matcher.evaluate(3) is False - assert matcher.evaluate(None) is False - assert matcher.evaluate({}) is False - assert matcher.evaluate(object()) is False - assert matcher.evaluate(True) is False - - def test_to_json(self): - """Test that the object serializes to JSON properly.""" - as_json = matchers.ContainsAnyOfSetMatcher(self.raw).to_json() - assert 'key1' in as_json['whitelistMatcherData']['whitelist'] - assert 'key2' in as_json['whitelistMatcherData']['whitelist'] - assert 'key3' in as_json['whitelistMatcherData']['whitelist'] - assert as_json['matcherType'] == 'CONTAINS_ANY_OF_SET' - assert as_json['negate'] is False - - -class EqualToSetMatcherTests(MatcherTestsBase): - """Test equal to set matcher.""" - - raw = { - 'matcherType': 'EQUAL_TO_SET', - 'negate': False, - 'whitelistMatcherData': { - 'whitelist': ['key1', 'key2', 'key3'], - } - } - - def test_from_raw(self, mocker): - """Test parsing from raw json/dict.""" - parsed = matchers.from_raw(self.raw) - assert isinstance(parsed, matchers.EqualToSetMatcher) - assert parsed._whitelist == frozenset(['key1', 'key2', 'key3']) - - def test_matcher_behaviour(self, mocker): - """Test if the matcher works properly.""" - matcher = matchers.EqualToSetMatcher(self.raw) - assert matcher.evaluate(['key1', 'key2', 'key3']) is True - assert matcher.evaluate(['key3', 'key2', 'key1']) is True - assert matcher.evaluate(['key1', 'key2', 'key3', 'key4']) is False - assert matcher.evaluate(['key4', 'key3', 'key1', 'key5', 'key2']) is False - assert matcher.evaluate(['key1', 'key2']) is False - assert matcher.evaluate([]) is False - assert matcher.evaluate('asdsad') is False - assert matcher.evaluate(3) is False - assert matcher.evaluate(None) is False - - def test_to_json(self): - """Test that the object serializes to JSON properly.""" - as_json = matchers.EqualToSetMatcher(self.raw).to_json() - assert 'key1' in as_json['whitelistMatcherData']['whitelist'] - assert 'key2' in as_json['whitelistMatcherData']['whitelist'] - assert 'key3' in as_json['whitelistMatcherData']['whitelist'] - assert as_json['matcherType'] == 'EQUAL_TO_SET' - assert as_json['negate'] is False - - -class PartOfSetMatcherTests(MatcherTestsBase): - """Test part of set matcher.""" - - raw = { - 'matcherType': 'PART_OF_SET', - 'negate': False, - 'whitelistMatcherData': { - 'whitelist': ['key1', 'key2', 'key3'], - } - } - - def test_from_raw(self, mocker): - """Test parsing from raw json/dict.""" - parsed = matchers.from_raw(self.raw) - assert isinstance(parsed, matchers.PartOfSetMatcher) - assert parsed._whitelist == frozenset(['key1', 'key2', 'key3']) - - def test_matcher_behaviour(self, mocker): - """Test if the matcher works properly.""" - matcher = matchers.PartOfSetMatcher(self.raw) - assert matcher.evaluate(['key1', 'key2', 'key3']) is True - assert matcher.evaluate(['key3', 'key2', 'key1']) is True - assert matcher.evaluate(['key1']) is True - assert matcher.evaluate(['key1', 'key2']) is True - assert matcher.evaluate(['key4', 'key3', 'key1', 'key5', 'key2']) is False - assert matcher.evaluate([]) is False - assert matcher.evaluate('asdsad') is False - assert matcher.evaluate(3) is False - assert matcher.evaluate(None) is False - assert matcher.evaluate({}) is False - assert matcher.evaluate(object()) is False - assert matcher.evaluate(True) is False - - def test_to_json(self): - """Test that the object serializes to JSON properly.""" - as_json = matchers.PartOfSetMatcher(self.raw).to_json() - assert 'key1' in as_json['whitelistMatcherData']['whitelist'] - assert 'key2' in as_json['whitelistMatcherData']['whitelist'] - assert 'key3' in as_json['whitelistMatcherData']['whitelist'] - assert as_json['matcherType'] == 'PART_OF_SET' - assert as_json['negate'] is False - - -class DependencyMatcherTests(MatcherTestsBase): - """tests for dependency matcher.""" - - raw = { - 'matcherType': 'IN_SPLIT_TREATMENT', - 'negate': False, - 'dependencyMatcherData': { - 'split': 'some_split', - 'treatments': ['on', 'almost_on'] - } - } - - def test_from_raw(self, mocker): - """Test parsing from raw json/dict.""" - parsed = matchers.from_raw(self.raw) - assert isinstance(parsed, matchers.DependencyMatcher) - assert parsed._split_name == 'some_split' - assert parsed._treatments == ['on', 'almost_on'] - - def test_matcher_behaviour(self, mocker): - """Test if the matcher works properly.""" - cond_raw = self.raw.copy() - cond_raw['dependencyMatcherData']['split'] = 'SPLIT_2' - parsed = matchers.DependencyMatcher(cond_raw) - evaluator = mocker.Mock(spec=Evaluator) - - cond = condition.from_raw(splits_json["splitChange1_1"]['ff']['d'][0]['conditions'][0]) - split = splits.from_raw(splits_json["splitChange1_1"]['ff']['d'][0]) - - evaluator.eval_with_context.return_value = {'treatment': 'on'} - assert parsed.evaluate('SPLIT_2', {}, {'evaluator': evaluator, 'ec': [{'flags': [split], 'segment_memberships': {}}]}) is True - - evaluator.eval_with_context.return_value = {'treatment': 'off'} - assert parsed.evaluate('SPLIT_2', {}, {'evaluator': evaluator, 'ec': [{'flags': [split], 'segment_memberships': {}}]}) is False - - assert evaluator.eval_with_context.mock_calls == [ - mocker.call('SPLIT_2', None, 'SPLIT_2', {}, [{'flags': [split], 'segment_memberships': {}}]), - mocker.call('SPLIT_2', None, 'SPLIT_2', {}, [{'flags': [split], 'segment_memberships': {}}]) - ] - - assert parsed.evaluate([], {}, {'evaluator': evaluator, 'ec': [{'flags': [split], 'segment_memberships': {}}]}) is False - assert parsed.evaluate({}, {}, {'evaluator': evaluator, 'ec': [{'flags': [split], 'segment_memberships': {}}]}) is False - assert parsed.evaluate(123, {}, {'evaluator': evaluator, 'ec': [{'flags': [split], 'segment_memberships': {}}]}) is False - assert parsed.evaluate(object(), {}, {'evaluator': evaluator, 'ec': [{'flags': [split], 'segment_memberships': {}}]}) is False - - def test_to_json(self): - """Test that the object serializes to JSON properly.""" - as_json = matchers.DependencyMatcher(self.raw).to_json() - assert as_json['matcherType'] == 'IN_SPLIT_TREATMENT' - assert as_json['dependencyMatcherData']['split'] == 'SPLIT_2' - assert as_json['dependencyMatcherData']['treatments'] == ['on', 'almost_on'] - - -class BooleanMatcherTests(MatcherTestsBase): - """Boolean matcher test cases.""" - - raw = { - 'negate': False, - 'matcherType': 'EQUAL_TO_BOOLEAN', - 'booleanMatcherData': True - } - - def test_from_raw(self, mocker): - """Test parsing from raw json/dict.""" - parsed = matchers.from_raw(self.raw) - assert isinstance(parsed, matchers.BooleanMatcher) - assert parsed._data - - def test_matcher_behaviour(self, mocker): - """Test if the matcher works properly.""" - parsed = matchers.BooleanMatcher(self.raw) - assert parsed.evaluate(True) is True - assert parsed.evaluate('true') is True - assert parsed.evaluate('True') is True - assert parsed.evaluate('tRUe') is True - assert parsed.evaluate('dasd') is False - assert parsed.evaluate(123) is False - assert parsed.evaluate(None) is False - - def test_to_json(self): - """Test that the object serializes to JSON properly.""" - as_json = matchers.BooleanMatcher(self.raw).to_json() - assert as_json['matcherType'] == 'EQUAL_TO_BOOLEAN' - assert as_json['booleanMatcherData'] - - -class RegexMatcherTests(MatcherTestsBase): - """Regex matcher test cases.""" - - raw = { - 'negate': False, - 'matcherType': 'MATCHES_STRING', - 'stringMatcherData': "^[a-z][A-Z][0-9]$" - } - - def test_from_raw(self, mocker): - """Test parsing from raw json/dict.""" - parsed = matchers.from_raw(self.raw) - assert isinstance(parsed, matchers.RegexMatcher) - assert parsed._data == "^[a-z][A-Z][0-9]$" - assert parsed._regex == re.compile("^[a-z][A-Z][0-9]$") - - def test_matcher_behaviour(self, mocker): - """Test if the matcher works properly.""" - filename = os.path.join(os.path.dirname(__file__), 'files', 'regex.txt') - with open(filename, 'r') as flo: - test_cases = flo.read().split('\n') - for test_case in test_cases: - if not test_case: - continue - - regex, string, should_match = test_case.split('#') - raw = { - 'negate': False, - 'matcherType': 'MATCHES_STRING', - 'stringMatcherData': regex - } - - parsed = matchers.RegexMatcher(raw) - assert parsed.evaluate(string) == json.loads(should_match) - - def test_to_json(self): - """Test that the object serializes to JSON properly.""" - as_json = matchers.RegexMatcher(self.raw).to_json() - assert as_json['matcherType'] == 'MATCHES_STRING' - assert as_json['stringMatcherData'] == "^[a-z][A-Z][0-9]$" - -class EqualToSemverMatcherTests(MatcherTestsBase): - """Semver equalto matcher test cases.""" - - raw = { - 'negate': False, - 'matcherType': 'EQUAL_TO_SEMVER', - 'stringMatcherData': "2.1.8" - } - - def test_from_raw(self, mocker): - """Test parsing from raw json/dict.""" - parsed = matchers.from_raw(self.raw) - assert isinstance(parsed, matchers.EqualToSemverMatcher) - assert parsed._semver is not None - assert parsed._semver.version == "2.1.8" - assert isinstance(parsed._semver, Semver) - assert parsed._semver._major == 2 - assert parsed._semver._minor == 1 - assert parsed._semver._patch == 8 - assert parsed._semver._pre_release == [] - - def test_matcher_behaviour(self, mocker): - """Test if the matcher works properly.""" - parsed = matchers.from_raw(self.raw) - assert not parsed._match("2.1.8+rc") - assert parsed._match("2.1.8") - assert not parsed._match("2.1.5") - assert not parsed._match("2.1.5-rc1") - assert not parsed._match(None) - assert not parsed._match("semver") - - def test_to_json(self): - """Test that the object serializes to JSON properly.""" - as_json = matchers.EqualToSemverMatcher(self.raw).to_json() - assert as_json['matcherType'] == 'EQUAL_TO_SEMVER' - assert as_json['stringMatcherData'] == "2.1.8" - - def test_to_str(self): - """Test that the object serializes to str properly.""" - as_str = matchers.EqualToSemverMatcher(self.raw) - assert str(as_str) == "equal semver 2.1.8" - -class GreaterThanOrEqualToSemverMatcherTests(MatcherTestsBase): - """Semver greater or equalto matcher test cases.""" - - raw = { - 'negate': False, - 'matcherType': 'GREATER_THAN_OR_EQUAL_TO_SEMVER', - 'stringMatcherData': "2.1.8" - } - - def test_from_raw(self, mocker): - """Test parsing from raw json/dict.""" - parsed = matchers.from_raw(self.raw) - assert isinstance(parsed, matchers.GreaterThanOrEqualToSemverMatcher) - assert parsed._semver is not None - assert parsed._semver.version == "2.1.8" - assert isinstance(parsed._semver, Semver) - assert parsed._semver._major == 2 - assert parsed._semver._minor == 1 - assert parsed._semver._patch == 8 - assert parsed._semver._pre_release == [] - - def test_matcher_behaviour(self, mocker): - """Test if the matcher works properly.""" - parsed = matchers.from_raw(self.raw) - assert parsed._match("2.1.8+rc") - assert parsed._match("2.1.8") - assert parsed._match("2.1.11") - assert not parsed._match("2.1.5") - assert not parsed._match("2.1.5-rc1") - assert not parsed._match(None) - assert not parsed._match("semver") - - def test_to_json(self): - """Test that the object serializes to JSON properly.""" - as_json = matchers.GreaterThanOrEqualToSemverMatcher(self.raw).to_json() - assert as_json['matcherType'] == 'GREATER_THAN_OR_EQUAL_TO_SEMVER' - assert as_json['stringMatcherData'] == "2.1.8" - - def test_to_str(self): - """Test that the object serializes to str properly.""" - as_str = matchers.GreaterThanOrEqualToSemverMatcher(self.raw) - assert str(as_str) == "greater than or equal to semver 2.1.8" - -class LessThanOrEqualToSemverMatcherTests(MatcherTestsBase): - """Semver less or equalto matcher test cases.""" - - raw = { - 'negate': False, - 'matcherType': 'LESS_THAN_OR_EQUAL_TO_SEMVER', - 'stringMatcherData': "2.1.8" - } - - def test_from_raw(self, mocker): - """Test parsing from raw json/dict.""" - parsed = matchers.from_raw(self.raw) - assert isinstance(parsed, matchers.LessThanOrEqualToSemverMatcher) - assert parsed._semver is not None - assert parsed._semver.version == "2.1.8" - assert isinstance(parsed._semver, Semver) - assert parsed._semver._major == 2 - assert parsed._semver._minor == 1 - assert parsed._semver._patch == 8 - assert parsed._semver._pre_release == [] - - def test_matcher_behaviour(self, mocker): - """Test if the matcher works properly.""" - parsed = matchers.from_raw(self.raw) - assert parsed._match("2.1.8+rc") - assert parsed._match("2.1.8") - assert not parsed._match("2.1.11") - assert parsed._match("2.1.5") - assert parsed._match("2.1.5-rc1") - assert not parsed._match(None) - assert not parsed._match("semver") - - def test_to_json(self): - """Test that the object serializes to JSON properly.""" - as_json = matchers.LessThanOrEqualToSemverMatcher(self.raw).to_json() - assert as_json['matcherType'] == 'LESS_THAN_OR_EQUAL_TO_SEMVER' - assert as_json['stringMatcherData'] == "2.1.8" - - def test_to_str(self): - """Test that the object serializes to str properly.""" - as_str = matchers.LessThanOrEqualToSemverMatcher(self.raw) - assert str(as_str) == "less than or equal to semver 2.1.8" - -class BetweenSemverMatcherTests(MatcherTestsBase): - """Semver between matcher test cases.""" - - raw = { - 'negate': False, - 'matcherType': 'BETWEEN_SEMVER', - 'betweenStringMatcherData': {"start": "2.1.8", "end": "2.1.11"} - } - - def test_from_raw(self, mocker): - """Test parsing from raw json/dict.""" - parsed = matchers.from_raw(self.raw) - assert isinstance(parsed, matchers.BetweenSemverMatcher) - assert isinstance(parsed._semver_start, Semver) - assert isinstance(parsed._semver_end, Semver) - assert parsed._semver_start.version == "2.1.8" - assert parsed._semver_start._major == 2 - assert parsed._semver_start._minor == 1 - assert parsed._semver_start._patch == 8 - assert parsed._semver_start._pre_release == [] - - assert parsed._semver_end.version == "2.1.11" - assert parsed._semver_end._major == 2 - assert parsed._semver_end._minor == 1 - assert parsed._semver_end._patch == 11 - assert parsed._semver_end._pre_release == [] - - def test_matcher_behaviour(self, mocker): - """Test if the matcher works properly.""" - parsed = matchers.from_raw(self.raw) - assert parsed._match("2.1.8+rc") - assert parsed._match("2.1.9") - assert parsed._match("2.1.11-rc12") - assert not parsed._match("2.1.5") - assert not parsed._match("2.1.12-rc1") - assert not parsed._match(None) - assert not parsed._match("semver") - - def test_to_json(self): - """Test that the object serializes to JSON properly.""" - as_json = matchers.BetweenSemverMatcher(self.raw).to_json() - assert as_json['matcherType'] == 'BETWEEN_SEMVER' - assert as_json['betweenStringMatcherData'] == {"start": "2.1.8", "end": "2.1.11"} - - def test_to_str(self): - """Test that the object serializes to str properly.""" - as_str = matchers.BetweenSemverMatcher(self.raw) - assert str(as_str) == "between semver 2.1.8 and 2.1.11" - -class InListSemverMatcherTests(MatcherTestsBase): - """Semver inlist matcher test cases.""" - - raw = { - 'negate': False, - 'matcherType': 'IN_LIST_SEMVER', - 'whitelistMatcherData': {"whitelist": ["2.1.8", "2.1.11"]} - } - - def test_from_raw(self, mocker): - """Test parsing from raw json/dict.""" - parsed = matchers.from_raw(self.raw) - assert isinstance(parsed, matchers.InListSemverMatcher) - assert parsed._data == ["2.1.8", "2.1.11"] - assert [isinstance(item, str) for item in parsed._semver_list] - assert "2.1.8" in parsed._semver_list - assert "2.1.11" in parsed._semver_list - - def test_matcher_behaviour(self, mocker): - """Test if the matcher works properly.""" - parsed = matchers.from_raw(self.raw) - assert not parsed._match("2.1.8+rc") - assert parsed._match("2.1.8") - assert not parsed._match("2.1.11-rc12") - assert parsed._match("2.1.11") - assert not parsed._match("2.1.7") - assert not parsed._match(None) - assert not parsed._match("semver") - - def test_to_json(self): - """Test that the object serializes to JSON properly.""" - as_json = matchers.InListSemverMatcher(self.raw).to_json() - assert as_json['matcherType'] == 'IN_LIST_SEMVER' - assert as_json['whitelistMatcherData'] == {"whitelist": ["2.1.8", "2.1.11"]} - - def test_to_str(self): - """Test that the object serializes to str properly.""" - as_str = matchers.InListSemverMatcher(self.raw) - assert str(as_str) == "in list semver ['2.1.8', '2.1.11']" - -class RuleBasedMatcherTests(MatcherTestsBase): - """Rule based segment matcher test cases.""" - - raw ={ - "keySelector": { - "trafficType": "user" - }, - "matcherType": "IN_RULE_BASED_SEGMENT", - "negate": False, - "userDefinedSegmentMatcherData": { - "segmentName": "sample_rule_based_segment" - } - } - - def test_from_raw(self, mocker): - """Test parsing from raw json/dict.""" - parsed = matchers.from_raw(self.raw) - assert isinstance(parsed, matchers.RuleBasedSegmentMatcher) - - def test_to_json(self): - """Test that the object serializes to JSON properly.""" - as_json = matchers.AllKeysMatcher(self.raw).to_json() - assert as_json['matcherType'] == 'IN_RULE_BASED_SEGMENT' - - def test_matcher_behaviour(self, mocker): - """Test if the matcher works properly.""" - rbs_segments = os.path.join(os.path.dirname(__file__), '../../engine/files', 'rule_base_segments3.json') - with open(rbs_segments, 'r') as flo: - data = json.loads(flo.read()) - - rbs = rule_based_segments.from_raw(data["rbs"]["d"][0]) - matcher = matchers.RuleBasedSegmentMatcher(self.raw) - ec ={'ec': EvaluationContext( - {}, - {"segment1": False}, - {"sample_rule_based_segment": rbs} - )} - assert matcher._match(None, context=ec) is False - assert matcher._match('bilal@split.io', context=ec) is False - assert matcher._match('bilal@split.io', {'email': 'bilal@split.io'}, context=ec) is True - -class PrerequisitesMatcherTests(MatcherTestsBase): - """tests for prerequisites matcher.""" - - def test_init(self, mocker): - """Test init.""" - split_load = os.path.join(os.path.dirname(__file__), 'files', 'splits_prereq.json') - with open(split_load, 'r') as flo: - data = json.loads(flo.read()) - - prereq = splits.from_raw_prerequisites(data['ff']['d'][0]['prerequisites']) - parsed = PrerequisitesMatcher(prereq) - assert parsed._prerequisites == prereq - - def test_matcher_behaviour(self, mocker): - """Test if the matcher works properly.""" - split_load = os.path.join(os.path.dirname(__file__), 'files', 'splits_prereq.json') - with open(split_load, 'r') as flo: - data = json.loads(flo.read()) - prereq = splits.from_raw_prerequisites(data['ff']['d'][3]['prerequisites']) - parsed = PrerequisitesMatcher(prereq) - evaluator = mocker.Mock(spec=Evaluator) - - - evaluator.eval_with_context.return_value = {'treatment': 'on'} - assert parsed.match('SPLIT_2', {}, {'evaluator': evaluator, 'ec': [{'flags': ['prereq_chain'], 'segment_memberships': {}}]}) is True - - evaluator.eval_with_context.return_value = {'treatment': 'off'} - assert parsed.match('SPLIT_2', {}, {'evaluator': evaluator, 'ec': [{'flags': ['prereq_chain'], 'segment_memberships': {}}]}) is False \ No newline at end of file diff --git a/tests/models/grammar/test_partitions.py b/tests/models/grammar/test_partitions.py deleted file mode 100644 index c3c49d12..00000000 --- a/tests/models/grammar/test_partitions.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Partitions test module.""" - -from splitio.models.grammar import partitions - -class PartitionTests(object): - """Partition model tests.""" - - raw = { - 'treatment': 'on', - 'size': 50 - } - - def test_parse(self): - """Test that the partition is parsed correctly.""" - p = partitions.from_raw(self.raw) - assert isinstance(p, partitions.Partition) - assert p.treatment == 'on' - assert p.size == 50 - - def test_to_json(self): - """Test the JSON representation.""" - as_json = partitions.from_raw(self.raw).to_json() - assert as_json['treatment'] == 'on' - assert as_json['size'] == 50 diff --git a/tests/models/grammar/test_semver.py b/tests/models/grammar/test_semver.py deleted file mode 100644 index 2a2b1b85..00000000 --- a/tests/models/grammar/test_semver.py +++ /dev/null @@ -1,71 +0,0 @@ -"""Condition model tests module.""" -import csv -import os - -from splitio.models.grammar.matchers.utils.utils import build_semver_or_none - -valid_versions = os.path.join(os.path.dirname(__file__), 'files', 'valid-semantic-versions.csv') -invalid_versions = os.path.join(os.path.dirname(__file__), 'files', 'invalid-semantic-versions.csv') -equalto_versions = os.path.join(os.path.dirname(__file__), 'files', 'equal-to-semver.csv') -between_versions = os.path.join(os.path.dirname(__file__), 'files', 'between-semver.csv') - -class SemverTests(object): - """Test the semver object model.""" - - def test_valid_versions(self): - with open(valid_versions) as csvfile: - reader = csv.DictReader(csvfile) - for row in reader: - assert build_semver_or_none(row['higher']) is not None - assert build_semver_or_none(row['lower']) is not None - - def test_invalid_versions(self): - with open(invalid_versions) as csvfile: - reader = csv.DictReader(csvfile) - for row in reader: - assert build_semver_or_none(row['invalid']) is None - - def test_compare(self): - with open(valid_versions) as csvfile: - reader = csv.DictReader(csvfile) - for row in reader: - higher = build_semver_or_none(row['higher']) - lower = build_semver_or_none(row['lower']) - assert higher is not None - assert lower is not None - assert higher.compare(lower) == 1 - assert lower.compare(higher) == -1 - - with open(equalto_versions) as csvfile: - reader = csv.DictReader(csvfile) - for row in reader: - version1 = build_semver_or_none(row['version1']) - version2 = build_semver_or_none(row['version2']) - assert version1 is not None - assert version2 is not None - if row['equals'] == "true": - assert version1.version == version2.version - else: - assert version1.version != version2.version - - with open(between_versions) as csvfile: - reader = csv.DictReader(csvfile) - for row in reader: - version1 = build_semver_or_none(row['version1']) - version2 = build_semver_or_none(row['version2']) - version3 = build_semver_or_none(row['version3']) - assert version1 is not None - assert version2 is not None - assert version3 is not None - if row['expected'] == "true": - assert version2.compare(version1) >= 0 and version3.compare(version2) >= 0 - else: - assert version2.compare(version1) < 0 or version3.compare(version2) < 0 - - def test_leading_zeros(self): - semver = build_semver_or_none('1.01.2') - assert semver is not None - assert semver.version == '1.1.2' - semver2 = build_semver_or_none('1.01.2-rc.01') - assert semver2 is not None - assert semver2.version == '1.1.2-rc.1' diff --git a/tests/models/test_fallback.py b/tests/models/test_fallback.py deleted file mode 100644 index aadb6007..00000000 --- a/tests/models/test_fallback.py +++ /dev/null @@ -1,63 +0,0 @@ -from splitio.models.fallback_treatment import FallbackTreatment -from splitio.models.fallback_config import FallbackTreatmentsConfiguration, FallbackTreatmentCalculator - -class FallbackTreatmentModelTests(object): - """Fallback treatment model tests.""" - - def test_working(self): - fallback_treatment = FallbackTreatment("on", '{"prop": "val"}') - assert fallback_treatment.config == '{"prop": "val"}' - assert fallback_treatment.treatment == 'on' - - fallback_treatment = FallbackTreatment("off") - assert fallback_treatment.config == None - assert fallback_treatment.treatment == 'off' - -class FallbackTreatmentsConfigModelTests(object): - """Fallback treatment configuration model tests.""" - - def test_working(self): - global_fb = FallbackTreatment("on") - flag_fb = FallbackTreatment("off") - fallback_config = FallbackTreatmentsConfiguration(global_fb, {"flag1": flag_fb}) - assert fallback_config.global_fallback_treatment == global_fb - assert fallback_config.by_flag_fallback_treatment == {"flag1": flag_fb} - - fallback_config.global_fallback_treatment = None - assert fallback_config.global_fallback_treatment == None - - fallback_config.by_flag_fallback_treatment["flag2"] = flag_fb - assert fallback_config.by_flag_fallback_treatment == {"flag1": flag_fb, "flag2": flag_fb} - - fallback_config = FallbackTreatmentsConfiguration("on", {"flag1": "off"}) - assert isinstance(fallback_config.global_fallback_treatment, FallbackTreatment) - assert fallback_config.global_fallback_treatment.treatment == "on" - - assert isinstance(fallback_config.by_flag_fallback_treatment["flag1"], FallbackTreatment) - assert fallback_config.by_flag_fallback_treatment["flag1"].treatment == "off" - - -class FallbackTreatmentCalculatorTests(object): - """Fallback treatment calculator model tests.""" - - def test_working(self): - fallback_config = FallbackTreatmentsConfiguration(FallbackTreatment("on" ,"{}"), None) - fallback_calculator = FallbackTreatmentCalculator(fallback_config) - assert fallback_calculator.fallback_treatments_configuration == fallback_config - assert fallback_calculator._label_prefix == "fallback - " - - fallback_treatment = fallback_calculator.resolve("feature", "not ready") - assert fallback_treatment.treatment == "on" - assert fallback_treatment.label == "fallback - not ready" - assert fallback_treatment.config == "{}" - - fallback_calculator._fallback_treatments_configuration = FallbackTreatmentsConfiguration(FallbackTreatment("on" ,"{}"), {'feature': FallbackTreatment("off" , '{"prop": "val"}')}) - fallback_treatment = fallback_calculator.resolve("feature", "not ready") - assert fallback_treatment.treatment == "off" - assert fallback_treatment.label == "fallback - not ready" - assert fallback_treatment.config == '{"prop": "val"}' - - fallback_treatment = fallback_calculator.resolve("feature2", "not ready") - assert fallback_treatment.treatment == "on" - assert fallback_treatment.label == "fallback - not ready" - assert fallback_treatment.config == "{}" diff --git a/tests/models/test_notification.py b/tests/models/test_notification.py deleted file mode 100644 index 3042647d..00000000 --- a/tests/models/test_notification.py +++ /dev/null @@ -1,51 +0,0 @@ -import pytest - -from splitio.models.notification import wrap_notification, SplitChangeNotification, SplitKillNotification, SegmentChangeNotification, ControlNotification - -class NotificationTests(object): - """Notification model tests.""" - - def test_wrap_notification(self): - with pytest.raises(ValueError): - wrap_notification('{"type":"WRONG","controlType":"STREAMING_PAUSED"}', 'control_pri') - - with pytest.raises(ValueError): - wrap_notification('sadasd', 'control_pri') - - with pytest.raises(TypeError): - wrap_notification(None, 'control_pri') - - with pytest.raises(ValueError): - wrap_notification('{"type":"SPLIT_UPDATE","changeNumber":1591996754396}', None) - - with pytest.raises(KeyError): - wrap_notification('{"type":"SPLIT_UPDATE"}', 'NDA5ODc2MTAyNg==_MzAyODY0NDkyOA==_splits') - - with pytest.raises(ValueError): - wrap_notification('{"type":"CONTROL","controlType":"STREAMING_PAUSEDD"}', 'control_pri') - - n0 = wrap_notification('{"type":"SPLIT_UPDATE","changeNumber":1591996754396}', 'NDA5ODc2MTAyNg==_MzAyODY0NDkyOA==_splits') - assert isinstance(n0, SplitChangeNotification) - assert n0.channel == 'NDA5ODc2MTAyNg==_MzAyODY0NDkyOA==_splits' - assert n0.notification_type.name == 'SPLIT_UPDATE' - - n1 = wrap_notification('{"type":"SPLIT_KILL","changeNumber":1591996754396,"defaultTreatment":"some","splitName":"test"}', 'NDA5ODc2MTAyNg==_MzAyODY0NDkyOA==_splits') - assert isinstance(n1, SplitKillNotification) - assert n1.channel == 'NDA5ODc2MTAyNg==_MzAyODY0NDkyOA==_splits' - assert n1.change_number == 1591996754396 - assert n1.default_treatment == 'some' - assert n1.split_name == 'test' - assert n1.notification_type.name == 'SPLIT_KILL' - - n2 = wrap_notification('{"type":"SEGMENT_UPDATE","changeNumber":1591996754396,"segmentName":"some"}', 'NDA5ODc2MTAyNg==_MzAyODY0NDkyOA==_segments') - assert isinstance(n2, SegmentChangeNotification) - assert n2.channel == 'NDA5ODc2MTAyNg==_MzAyODY0NDkyOA==_segments' - assert n2.change_number == 1591996754396 - assert n2.segment_name == 'some' - assert n2.notification_type.name == 'SEGMENT_UPDATE' - - n3 = wrap_notification('{"type":"CONTROL","controlType":"STREAMING_PAUSED"}', 'control_pri') - assert isinstance(n3, ControlNotification) - assert n3.channel == 'control_pri' - assert n3.control_type.name == 'STREAMING_PAUSED' - assert n3.notification_type.name == 'CONTROL' diff --git a/tests/models/test_rule_based_segments.py b/tests/models/test_rule_based_segments.py deleted file mode 100644 index 98e35fe8..00000000 --- a/tests/models/test_rule_based_segments.py +++ /dev/null @@ -1,103 +0,0 @@ -"""Split model tests module.""" -import copy -from splitio.models import rule_based_segments -from splitio.models import splits -from splitio.models.grammar.condition import Condition -from splitio.models.grammar.matchers.rule_based_segment import RuleBasedSegmentMatcher - -class RuleBasedSegmentModelTests(object): - """Rule based segment model tests.""" - - raw = { - "changeNumber": 123, - "name": "sample_rule_based_segment", - "status": "ACTIVE", - "trafficTypeName": "user", - "excluded":{ - "keys":["mauro@split.io","gaston@split.io"], - "segments":[] - }, - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": "email" - }, - "matcherType": "ENDS_WITH", - "negate": False, - "whitelistMatcherData": { - "whitelist": [ - "@split.io" - ] - } - } - ] - } - } - ] - } - - def test_from_raw(self): - """Test split model parsing.""" - parsed = rule_based_segments.from_raw(self.raw) - assert isinstance(parsed, rule_based_segments.RuleBasedSegment) - assert parsed.change_number == 123 - assert parsed.name == 'sample_rule_based_segment' - assert parsed.status == splits.Status.ACTIVE - assert len(parsed.conditions) == 1 - assert parsed.excluded.get_excluded_keys() == ["mauro@split.io","gaston@split.io"] - assert parsed.excluded.get_excluded_segments() == [] - conditions = parsed.conditions[0].to_json() - assert conditions['matcherGroup']['matchers'][0] == { - 'betweenMatcherData': None, 'booleanMatcherData': None, 'dependencyMatcherData': None, - 'stringMatcherData': None, 'unaryNumericMatcherData': None, 'userDefinedSegmentMatcherData': None, - "keySelector": { - "attribute": "email" - }, - "matcherType": "ENDS_WITH", - "negate": False, - "whitelistMatcherData": { - "whitelist": [ - "@split.io" - ] - } - } - - def test_incorrect_matcher(self): - """Test incorrect matcher in split model parsing.""" - rbs = copy.deepcopy(self.raw) - rbs['conditions'][0]['matcherGroup']['matchers'][0]['matcherType'] = 'INVALID_MATCHER' - rbs = rule_based_segments.from_raw(rbs) - assert rbs.conditions[0].to_json() == splits._DEFAULT_CONDITIONS_TEMPLATE - - # using multiple conditions - rbs = copy.deepcopy(self.raw) - rbs['conditions'].append(rbs['conditions'][0]) - rbs['conditions'][0]['matcherGroup']['matchers'][0]['matcherType'] = 'INVALID_MATCHER' - parsed = rule_based_segments.from_raw(rbs) - assert parsed.conditions[0].to_json() == splits._DEFAULT_CONDITIONS_TEMPLATE - - def test_get_condition_segment_names(self): - rbs = copy.deepcopy(self.raw) - rbs['conditions'].append( - {"matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "matcherType": "IN_SEGMENT", - "negate": False, - "userDefinedSegmentMatcherData": { - "segmentName": "employees" - }, - "whitelistMatcherData": None - } - ] - }, - }) - rbs = rule_based_segments.from_raw(rbs) - - assert rbs.get_condition_segment_names() == {"employees"} \ No newline at end of file diff --git a/tests/models/test_splits.py b/tests/models/test_splits.py index 472ecde9..606a0921 100644 --- a/tests/models/test_splits.py +++ b/tests/models/test_splits.py @@ -2,7 +2,7 @@ import copy from splitio.models import splits -from splitio.models.grammar.condition import Condition +from harness_commons.models.grammar.condition import Condition class SplitTests(object): """Split model tests.""" @@ -91,10 +91,10 @@ def test_from_raw(self): flag1 = False flag2 = False for prerequisite in parsed.prerequisites: - if prerequisite.feature_flag_name == 'flag1': + if prerequisite.definition_name == 'flag1': flag1 = True assert prerequisite.treatments == ['on','v1'] - if prerequisite.feature_flag_name == 'flag2': + if prerequisite.definition_name == 'flag2': flag2 = True assert prerequisite.treatments == ['off'] assert flag1 diff --git a/tests/models/test_telemetry_model.py b/tests/models/test_telemetry_model.py deleted file mode 100644 index 7032c359..00000000 --- a/tests/models/test_telemetry_model.py +++ /dev/null @@ -1,662 +0,0 @@ -"""Telemetry model test module.""" -import os -import random -import pytest - -from splitio.models.telemetry import StorageType, OperationMode, MethodLatencies, MethodExceptions, \ - HTTPLatencies, HTTPErrors, LastSynchronization, TelemetryCounters, TelemetryConfig, \ - StreamingEvent, StreamingEvents, MethodExceptionsAsync, HTTPLatenciesAsync, HTTPErrorsAsync, LastSynchronizationAsync, \ - TelemetryCountersAsync, TelemetryConfigAsync, StreamingEventsAsync, MethodLatenciesAsync, UpdateFromSSE - -import splitio.models.telemetry as ModelTelemetry - -class TelemetryModelTests(object): - """Telemetry model test cases.""" - - def test_latency_bucket_index(self): - for i in range(50000): - latency = random.randint(10, 9987885) - old_bucket = 0 - result_bucket = 0 - counter = -1 - for j in ModelTelemetry.BUCKETS: - counter += 1 - if old_bucket == 0: - if latency < j: - old_bucket = 0 - break - old_bucket = j - continue - if counter == ModelTelemetry.MAX_LATENCY_BUCKET_COUNT - 1: - result_bucket = 22 - break - if latency > old_bucket and latency <= j: - result_bucket = counter - break - old_bucket = j - print(latency, old_bucket, j) - assert(result_bucket == ModelTelemetry.get_latency_bucket_index(latency)) - - def test_storage_type_and_operation_mode(self, mocker): - assert(StorageType.MEMORY.value == 'memory') - assert(StorageType.REDIS.value == 'redis') - assert(OperationMode.STANDALONE.value == 'standalone') - assert(OperationMode.CONSUMER.value == 'consumer') - - def test_method_latencies(self, mocker): - method_latencies = MethodLatencies() - - method_latencies.pop_all() # should not raise exception - for method in ModelTelemetry.MethodExceptionsAndLatencies: - method_latencies.add_latency(method, 50) - if method.value == 'treatment': - assert(method_latencies._treatment[ModelTelemetry.get_latency_bucket_index(50)] == 1) - elif method.value == 'treatments': - assert(method_latencies._treatments[ModelTelemetry.get_latency_bucket_index(50)] == 1) - elif method.value == 'treatment_with_config': - assert(method_latencies._treatment_with_config[ModelTelemetry.get_latency_bucket_index(50)] == 1) - elif method.value == 'treatments_with_config': - assert(method_latencies._treatments_with_config[ModelTelemetry.get_latency_bucket_index(50)] == 1) - elif method.value == 'treatments_by_flag_set': - assert(method_latencies._treatments_by_flag_set[ModelTelemetry.get_latency_bucket_index(50)] == 1) - elif method.value == 'treatments_by_flag_sets': - assert(method_latencies._treatments_by_flag_sets[ModelTelemetry.get_latency_bucket_index(50)] == 1) - elif method.value == 'treatments_with_config_by_flag_set': - assert(method_latencies._treatments_with_config_by_flag_set[ModelTelemetry.get_latency_bucket_index(50)] == 1) - elif method.value == 'treatments_with_config_by_flag_sets': - assert(method_latencies._treatments_with_config_by_flag_sets[ModelTelemetry.get_latency_bucket_index(50)] == 1) - elif method.value == 'track': - assert(method_latencies._track[ModelTelemetry.get_latency_bucket_index(50)] == 1) - - method_latencies.add_latency(method, 50000000) - if method.value == 'treatment': - assert(method_latencies._treatment[ModelTelemetry.get_latency_bucket_index(50000000)] == 1) - if method.value == 'treatments': - assert(method_latencies._treatments[ModelTelemetry.get_latency_bucket_index(50000000)] == 1) - if method.value == 'treatment_with_config': - assert(method_latencies._treatment_with_config[ModelTelemetry.get_latency_bucket_index(50000000)] == 1) - if method.value == 'treatments_with_config': - assert(method_latencies._treatments_with_config[ModelTelemetry.get_latency_bucket_index(50000000)] == 1) - elif method.value == 'treatments_by_flag_set': - assert(method_latencies._treatments_by_flag_set[ModelTelemetry.get_latency_bucket_index(50000000)] == 1) - elif method.value == 'treatments_by_flag_sets': - assert(method_latencies._treatments_by_flag_sets[ModelTelemetry.get_latency_bucket_index(50000000)] == 1) - elif method.value == 'treatments_with_config_by_flag_set': - assert(method_latencies._treatments_with_config_by_flag_set[ModelTelemetry.get_latency_bucket_index(50000000)] == 1) - elif method.value == 'treatments_with_config_by_flag_sets': - assert(method_latencies._treatments_with_config_by_flag_sets[ModelTelemetry.get_latency_bucket_index(50000000)] == 1) - if method.value == 'track': - assert(method_latencies._track[ModelTelemetry.get_latency_bucket_index(50000000)] == 1) - - method_latencies.pop_all() - assert(method_latencies._track == [0] * 23) - assert(method_latencies._treatment == [0] * 23) - assert(method_latencies._treatments == [0] * 23) - assert(method_latencies._treatment_with_config == [0] * 23) - assert(method_latencies._treatments_with_config == [0] * 23) - assert(method_latencies._treatments_by_flag_set == [0] * 23) - assert(method_latencies._treatments_by_flag_sets == [0] * 23) - assert(method_latencies._treatments_with_config_by_flag_set == [0] * 23) - assert(method_latencies._treatments_with_config_by_flag_sets == [0] * 23) - - method_latencies.add_latency(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENT, 10) - [method_latencies.add_latency(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENTS, 20) for i in range(2)] - method_latencies.add_latency(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENT_WITH_CONFIG, 50) - method_latencies.add_latency(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG, 20) - [method_latencies.add_latency(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SET, 20) for i in range(3)] - [method_latencies.add_latency(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SETS, 20) for i in range(4)] - [method_latencies.add_latency(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SET, 20) for i in range(5)] - [method_latencies.add_latency(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SETS, 20) for i in range(6)] - method_latencies.add_latency(ModelTelemetry.MethodExceptionsAndLatencies.TRACK, 20) - latencies = method_latencies.pop_all() - assert(latencies == {'methodLatencies': {'treatment': [1] + [0] * 22, - 'treatments': [2] + [0] * 22, - 'treatment_with_config': [1] + [0] * 22, - 'treatments_with_config': [1] + [0] * 22, - 'treatments_by_flag_set': [3] + [0] * 22, - 'treatments_by_flag_sets': [4] + [0] * 22, - 'treatments_with_config_by_flag_set': [5] + [0] * 22, - 'treatments_with_config_by_flag_sets': [6] + [0] * 22, - 'track': [1] + [0] * 22}}) - - def test_http_latencies(self, mocker): - http_latencies = HTTPLatencies() - - http_latencies.pop_all() # should not raise exception - for resource in ModelTelemetry.HTTPExceptionsAndLatencies: - if self._get_http_latency(resource, http_latencies) == None: - continue - http_latencies.add_latency(resource, 50) - assert(self._get_http_latency(resource, http_latencies)[ModelTelemetry.get_latency_bucket_index(50)] == 1) - http_latencies.add_latency(resource, 50000000) - assert(self._get_http_latency(resource, http_latencies)[ModelTelemetry.get_latency_bucket_index(50000000)] == 1) - for j in range(10): - latency = random.randint(1001, 4987885) - current_count = self._get_http_latency(resource, http_latencies)[ModelTelemetry.get_latency_bucket_index(latency)] - [http_latencies.add_latency(resource, latency) for i in range(2)] - assert(self._get_http_latency(resource, http_latencies)[ModelTelemetry.get_latency_bucket_index(latency)] == 2 + current_count) - - http_latencies.pop_all() - assert(http_latencies._event == [0] * 23) - assert(http_latencies._impression == [0] * 23) - assert(http_latencies._impression_count == [0] * 23) - assert(http_latencies._segment == [0] * 23) - assert(http_latencies._split == [0] * 23) - assert(http_latencies._telemetry == [0] * 23) - assert(http_latencies._token == [0] * 23) - - http_latencies.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.SPLIT, 10) - [http_latencies.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.IMPRESSION, i) for i in [10, 20]] - http_latencies.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.SEGMENT, 40) - http_latencies.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.IMPRESSION_COUNT, 60) - http_latencies.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.EVENT, 90) - http_latencies.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.TELEMETRY, 70) - [http_latencies.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.TOKEN, i) for i in [10, 15]] - latencies = http_latencies.pop_all() - assert(latencies == {'httpLatencies': {'split': [1] + [0] * 22, 'segment': [1] + [0] * 22, 'impression': [2] + [0] * 22, 'impressionCount': [1] + [0] * 22, 'event': [1] + [0] * 22, 'telemetry': [1] + [0] * 22, 'token': [2] + [0] * 22}}) - - def _get_http_latency(self, resource, storage): - if resource == ModelTelemetry.HTTPExceptionsAndLatencies.SPLIT: - return storage._split - elif resource == ModelTelemetry.HTTPExceptionsAndLatencies.SEGMENT: - return storage._segment - elif resource == ModelTelemetry.HTTPExceptionsAndLatencies.IMPRESSION: - return storage._impression - elif resource == ModelTelemetry.HTTPExceptionsAndLatencies.IMPRESSION_COUNT: - return storage._impression_count - elif resource == ModelTelemetry.HTTPExceptionsAndLatencies.EVENT: - return storage._event - elif resource == ModelTelemetry.HTTPExceptionsAndLatencies.TELEMETRY: - return storage._telemetry - elif resource == ModelTelemetry.HTTPExceptionsAndLatencies.TOKEN: - return storage._token - else: - return - - def test_method_exceptions(self, mocker): - method_exception = MethodExceptions() - - exceptions = method_exception.pop_all() # should not raise exception - [method_exception.add_exception(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENT) for i in range(2)] - method_exception.add_exception(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENTS) - method_exception.add_exception(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENT_WITH_CONFIG) - [method_exception.add_exception(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG) for i in range(5)] - [method_exception.add_exception(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SET) for i in range(6)] - [method_exception.add_exception(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SETS) for i in range(7)] - [method_exception.add_exception(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SET) for i in range(8)] - [method_exception.add_exception(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SETS) for i in range(9)] - [method_exception.add_exception(ModelTelemetry.MethodExceptionsAndLatencies.TRACK) for i in range(3)] - exceptions = method_exception.pop_all() - - assert(method_exception._treatment == 0) - assert(method_exception._treatments == 0) - assert(method_exception._treatment_with_config == 0) - assert(method_exception._treatments_with_config == 0) - assert(method_exception._treatments_by_flag_set == 0) - assert(method_exception._treatments_by_flag_sets == 0) - assert(method_exception._treatments_with_config_by_flag_set == 0) - assert(method_exception._treatments_with_config_by_flag_sets == 0) - assert(method_exception._track == 0) - assert(exceptions == {'methodExceptions': {'treatment': 2, - 'treatments': 1, - 'treatment_with_config': 1, - 'treatments_with_config': 5, - 'treatments_by_flag_set': 6, - 'treatments_by_flag_sets': 7, - 'treatments_with_config_by_flag_set': 8, - 'treatments_with_config_by_flag_sets': 9, - 'track': 3}}) - - def test_http_errors(self, mocker): - http_error = HTTPErrors() - errors = http_error.pop_all() # should not raise exception - [http_error.add_error(ModelTelemetry.HTTPExceptionsAndLatencies.SEGMENT, str(i)) for i in [500, 501, 502]] - [http_error.add_error(ModelTelemetry.HTTPExceptionsAndLatencies.SPLIT, str(i)) for i in [400, 401, 402]] - http_error.add_error(ModelTelemetry.HTTPExceptionsAndLatencies.IMPRESSION, '502') - [http_error.add_error(ModelTelemetry.HTTPExceptionsAndLatencies.IMPRESSION_COUNT, str(i)) for i in [501, 502]] - http_error.add_error(ModelTelemetry.HTTPExceptionsAndLatencies.EVENT, '501') - http_error.add_error(ModelTelemetry.HTTPExceptionsAndLatencies.TELEMETRY, '505') - [http_error.add_error(ModelTelemetry.HTTPExceptionsAndLatencies.TOKEN, '502') for i in range(5)] - errors = http_error.pop_all() - assert(errors == {'httpErrors': {'split': {'400': 1, '401': 1, '402': 1}, 'segment': {'500': 1, '501': 1, '502': 1}, - 'impression': {'502': 1}, 'impressionCount': {'501': 1, '502': 1}, - 'event': {'501': 1}, 'telemetry': {'505': 1}, 'token': {'502': 5}}}) - assert(http_error._split == {}) - assert(http_error._segment == {}) - assert(http_error._impression == {}) - assert(http_error._impression_count == {}) - assert(http_error._event == {}) - assert(http_error._telemetry == {}) - - def test_last_synchronization(self, mocker): - last_synchronization = LastSynchronization() - last_synchronization.get_all() # should not raise exception - last_synchronization.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.SPLIT, 10) - last_synchronization.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.IMPRESSION, 20) - last_synchronization.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.SEGMENT, 40) - last_synchronization.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.IMPRESSION_COUNT, 60) - last_synchronization.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.EVENT, 90) - last_synchronization.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.TELEMETRY, 70) - last_synchronization.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.TOKEN, 15) - assert(last_synchronization.get_all() == {'lastSynchronizations': {'split': 10, 'segment': 40, 'impression': 20, 'impressionCount': 60, 'event': 90, 'telemetry': 70, 'token': 15}}) - - def test_telemetry_counters(self): - telemetry_counter = TelemetryCounters() - assert(telemetry_counter._impressions_queued == 0) - assert(telemetry_counter._impressions_deduped == 0) - assert(telemetry_counter._impressions_dropped == 0) - assert(telemetry_counter._events_dropped == 0) - assert(telemetry_counter._events_queued == 0) - assert(telemetry_counter._auth_rejections == 0) - assert(telemetry_counter._token_refreshes == 0) - assert(telemetry_counter._update_from_sse == {}) - - assert(telemetry_counter.get_session_length() == 0) - telemetry_counter.record_session_length(20) - assert(telemetry_counter.get_session_length() == 20) - - assert(telemetry_counter.pop_auth_rejections() == 0) - [telemetry_counter.record_auth_rejections() for i in range(5)] - auth_rejections = telemetry_counter.pop_auth_rejections() - assert(telemetry_counter._auth_rejections == 0) - assert(auth_rejections == 5) - - assert(telemetry_counter.pop_token_refreshes() == 0) - [telemetry_counter.record_token_refreshes() for i in range(3)] - token_refreshes = telemetry_counter.pop_token_refreshes() - assert(telemetry_counter._token_refreshes == 0) - assert(token_refreshes == 3) - - assert(telemetry_counter.get_counter_stats(ModelTelemetry.CounterConstants.IMPRESSIONS_QUEUED) == 0) - assert(telemetry_counter.get_counter_stats(ModelTelemetry.CounterConstants.IMPRESSIONS_DEDUPED) == 0) - assert(telemetry_counter.get_counter_stats(ModelTelemetry.CounterConstants.IMPRESSIONS_DROPPED) == 0) - assert(telemetry_counter.get_counter_stats(ModelTelemetry.CounterConstants.EVENTS_QUEUED) == 0) - assert(telemetry_counter.get_counter_stats(ModelTelemetry.CounterConstants.EVENTS_DROPPED) == 0) - telemetry_counter.record_impressions_value(ModelTelemetry.CounterConstants.IMPRESSIONS_QUEUED, 10) - assert(telemetry_counter._impressions_queued == 10) - telemetry_counter.record_impressions_value(ModelTelemetry.CounterConstants.IMPRESSIONS_DEDUPED, 14) - assert(telemetry_counter._impressions_deduped == 14) - telemetry_counter.record_impressions_value(ModelTelemetry.CounterConstants.IMPRESSIONS_DROPPED, 2) - assert(telemetry_counter._impressions_dropped == 2) - telemetry_counter.record_events_value(ModelTelemetry.CounterConstants.EVENTS_QUEUED, 30) - assert(telemetry_counter._events_queued == 30) - telemetry_counter.record_events_value(ModelTelemetry.CounterConstants.EVENTS_DROPPED, 1) - assert(telemetry_counter._events_dropped == 1) - telemetry_counter.record_update_from_sse(UpdateFromSSE.SPLIT_UPDATE) - assert(telemetry_counter._update_from_sse[UpdateFromSSE.SPLIT_UPDATE.value] == 1) - updates = telemetry_counter.pop_update_from_sse(UpdateFromSSE.SPLIT_UPDATE) - assert(telemetry_counter._update_from_sse[UpdateFromSSE.SPLIT_UPDATE.value] == 0) - assert(updates == 1) - - def test_streaming_event(self, mocker): - streaming_event = StreamingEvent((ModelTelemetry.StreamingEventTypes.CONNECTION_ESTABLISHED, 'split', 1234)) - assert(streaming_event.type == ModelTelemetry.StreamingEventTypes.CONNECTION_ESTABLISHED.value) - assert(streaming_event.data == 'split') - assert(streaming_event.time == 1234) - - def test_streaming_events(self, mocker): - streaming_events = StreamingEvents() - events = streaming_events.pop_streaming_events() # should not raise exception - streaming_events.record_streaming_event((ModelTelemetry.StreamingEventTypes.CONNECTION_ESTABLISHED, 'split', 1234)) - streaming_events.record_streaming_event((ModelTelemetry.StreamingEventTypes.STREAMING_STATUS, 'split', 1234)) - events = streaming_events.pop_streaming_events() - assert(streaming_events._streaming_events == []) - assert(events == {'streamingEvents': [{'e': ModelTelemetry.StreamingEventTypes.CONNECTION_ESTABLISHED.value, 'd': 'split', 't': 1234}, - {'e': ModelTelemetry.StreamingEventTypes.STREAMING_STATUS.value, 'd': 'split', 't': 1234}]}) - - def test_telemetry_config(self): - telemetry_config = TelemetryConfig() - stats = telemetry_config.get_stats() # should not raise exception - config = {'operationMode': 'standalone', - 'streamingEnabled': True, - 'impressionsQueueSize': 100, - 'eventsQueueSize': 200, - 'impressionsMode': 'DEBUG','' - 'impressionListener': None, - 'featuresRefreshRate': 30, - 'segmentsRefreshRate': 30, - 'impressionsRefreshRate': 60, - 'eventsPushRate': 60, - 'metricsRefreshRate': 10, - 'storageType': None, - 'flagSetsFilter': None - } - telemetry_config.record_config(config, {}, 5, 2) - assert(telemetry_config.get_stats() == {'oM': 0, - 'sT': telemetry_config._get_storage_type(config['operationMode'], config['storageType']), - 'sE': config['streamingEnabled'], - 'rR': {'sp': 30, 'se': 30, 'im': 60, 'ev': 60, 'te': 10}, - 'uO': {'s': False, 'e': False, 'a': False, 'st': False, 't': False}, - 'iQ': config['impressionsQueueSize'], - 'eQ': config['eventsQueueSize'], - 'iM': telemetry_config._get_impressions_mode(config['impressionsMode']), - 'iL': True if config['impressionListener'] is not None else False, - 'hp': telemetry_config._check_if_proxy_detected(), - 'tR': 0, - 'nR': 0, - 'bT': 0, - 'aF': 0, - 'rF': 0, - 'fsT': 5, - 'fsI': 2} - ) - - telemetry_config.record_ready_time(10) - assert(telemetry_config._time_until_ready == 10) - - assert(telemetry_config.get_bur_time_outs() == 0) - [telemetry_config.record_bur_time_out() for i in range(2)] - assert(telemetry_config.get_bur_time_outs() == 2) - - assert(telemetry_config.get_non_ready_usage() == 0) - [telemetry_config.record_not_ready_usage() for i in range(5)] - assert(telemetry_config.get_non_ready_usage() == 5) - - os.environ["https_proxy"] = "some_host_ip" - assert(telemetry_config._check_if_proxy_detected() == True) - - del os.environ["https_proxy"] - assert(telemetry_config._check_if_proxy_detected() == False) - - os.environ["HTTPS_proxy"] = "some_host_ip" - assert(telemetry_config._check_if_proxy_detected() == True) - - del os.environ["HTTPS_proxy"] - assert(telemetry_config._check_if_proxy_detected() == False) - -class TelemetryModelAsyncTests(object): - """Telemetry model async test cases.""" - - @pytest.mark.asyncio - async def test_method_latencies(self, mocker): - method_latencies = await MethodLatenciesAsync.create() - - for method in ModelTelemetry.MethodExceptionsAndLatencies: - await method_latencies.add_latency(method, 50) - if method.value == 'treatment': - assert(method_latencies._treatment[ModelTelemetry.get_latency_bucket_index(50)] == 1) - elif method.value == 'treatments': - assert(method_latencies._treatments[ModelTelemetry.get_latency_bucket_index(50)] == 1) - elif method.value == 'treatment_with_config': - assert(method_latencies._treatment_with_config[ModelTelemetry.get_latency_bucket_index(50)] == 1) - elif method.value == 'treatments_with_config': - assert(method_latencies._treatments_with_config[ModelTelemetry.get_latency_bucket_index(50)] == 1) - elif method.value == 'treatments_by_flag_set': - assert(method_latencies._treatments_by_flag_set[ModelTelemetry.get_latency_bucket_index(50)] == 1) - elif method.value == 'treatments_by_flag_sets': - assert(method_latencies._treatments_by_flag_sets[ModelTelemetry.get_latency_bucket_index(50)] == 1) - elif method.value == 'treatments_with_config_by_flag_set': - assert(method_latencies._treatments_with_config_by_flag_set[ModelTelemetry.get_latency_bucket_index(50)] == 1) - elif method.value == 'treatments_with_config_by_flag_sets': - assert(method_latencies._treatments_with_config_by_flag_sets[ModelTelemetry.get_latency_bucket_index(50)] == 1) - elif method.value == 'track': - assert(method_latencies._track[ModelTelemetry.get_latency_bucket_index(50)] == 1) - - await method_latencies.add_latency(method, 50000000) - if method.value == 'treatment': - assert(method_latencies._treatment[ModelTelemetry.get_latency_bucket_index(50000000)] == 1) - if method.value == 'treatments': - assert(method_latencies._treatments[ModelTelemetry.get_latency_bucket_index(50000000)] == 1) - if method.value == 'treatment_with_config': - assert(method_latencies._treatment_with_config[ModelTelemetry.get_latency_bucket_index(50000000)] == 1) - if method.value == 'treatments_with_config': - assert(method_latencies._treatments_with_config[ModelTelemetry.get_latency_bucket_index(50000000)] == 1) - elif method.value == 'treatments_by_flag_set': - assert(method_latencies._treatments_by_flag_set[ModelTelemetry.get_latency_bucket_index(50000000)] == 1) - elif method.value == 'treatments_by_flag_sets': - assert(method_latencies._treatments_by_flag_sets[ModelTelemetry.get_latency_bucket_index(50000000)] == 1) - elif method.value == 'treatments_with_config_by_flag_set': - assert(method_latencies._treatments_with_config_by_flag_set[ModelTelemetry.get_latency_bucket_index(50000000)] == 1) - elif method.value == 'treatments_with_config_by_flag_sets': - assert(method_latencies._treatments_with_config_by_flag_sets[ModelTelemetry.get_latency_bucket_index(50000000)] == 1) - if method.value == 'track': - assert(method_latencies._track[ModelTelemetry.get_latency_bucket_index(50000000)] == 1) - - await method_latencies.pop_all() - assert(method_latencies._track == [0] * 23) - assert(method_latencies._treatment == [0] * 23) - assert(method_latencies._treatments == [0] * 23) - assert(method_latencies._treatment_with_config == [0] * 23) - assert(method_latencies._treatments_with_config == [0] * 23) - assert(method_latencies._treatments_by_flag_set == [0] * 23) - assert(method_latencies._treatments_by_flag_sets == [0] * 23) - assert(method_latencies._treatments_with_config_by_flag_set == [0] * 23) - assert(method_latencies._treatments_with_config_by_flag_sets == [0] * 23) - - await method_latencies.add_latency(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENT, 10) - [await method_latencies.add_latency(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENTS, 20) for i in range(2)] - await method_latencies.add_latency(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENT_WITH_CONFIG, 50) - await method_latencies.add_latency(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG, 20) - [await method_latencies.add_latency(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SET, 20) for i in range(3)] - [await method_latencies.add_latency(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SETS, 20) for i in range(4)] - [await method_latencies.add_latency(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SET, 20) for i in range(5)] - [await method_latencies.add_latency(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SETS, 20) for i in range(6)] - await method_latencies.add_latency(ModelTelemetry.MethodExceptionsAndLatencies.TRACK, 20) - latencies = await method_latencies.pop_all() - assert(latencies == {'methodLatencies': {'treatment': [1] + [0] * 22, - 'treatments': [2] + [0] * 22, - 'treatment_with_config': [1] + [0] * 22, - 'treatments_with_config': [1] + [0] * 22, - 'treatments_by_flag_set': [3] + [0] * 22, - 'treatments_by_flag_sets': [4] + [0] * 22, - 'treatments_with_config_by_flag_set': [5] + [0] * 22, - 'treatments_with_config_by_flag_sets': [6] + [0] * 22, - 'track': [1] + [0] * 22}}) - - @pytest.mark.asyncio - async def test_http_latencies(self, mocker): - http_latencies = await HTTPLatenciesAsync.create() - - for resource in ModelTelemetry.HTTPExceptionsAndLatencies: - if self._get_http_latency(resource, http_latencies) == None: - continue - await http_latencies.add_latency(resource, 50) - assert(self._get_http_latency(resource, http_latencies)[ModelTelemetry.get_latency_bucket_index(50)] == 1) - await http_latencies.add_latency(resource, 50000000) - assert(self._get_http_latency(resource, http_latencies)[ModelTelemetry.get_latency_bucket_index(50000000)] == 1) - for j in range(10): - latency = random.randint(1001, 4987885) - current_count = self._get_http_latency(resource, http_latencies)[ModelTelemetry.get_latency_bucket_index(latency)] - [await http_latencies.add_latency(resource, latency) for i in range(2)] - assert(self._get_http_latency(resource, http_latencies)[ModelTelemetry.get_latency_bucket_index(latency)] == 2 + current_count) - - await http_latencies.pop_all() - assert(http_latencies._event == [0] * 23) - assert(http_latencies._impression == [0] * 23) - assert(http_latencies._impression_count == [0] * 23) - assert(http_latencies._segment == [0] * 23) - assert(http_latencies._split == [0] * 23) - assert(http_latencies._telemetry == [0] * 23) - assert(http_latencies._token == [0] * 23) - - await http_latencies.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.SPLIT, 10) - [await http_latencies.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.IMPRESSION, i) for i in [10, 20]] - await http_latencies.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.SEGMENT, 40) - await http_latencies.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.IMPRESSION_COUNT, 60) - await http_latencies.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.EVENT, 90) - await http_latencies.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.TELEMETRY, 70) - [await http_latencies.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.TOKEN, i) for i in [10, 15]] - latencies = await http_latencies.pop_all() - assert(latencies == {'httpLatencies': {'split': [1] + [0] * 22, 'segment': [1] + [0] * 22, 'impression': [2] + [0] * 22, 'impressionCount': [1] + [0] * 22, 'event': [1] + [0] * 22, 'telemetry': [1] + [0] * 22, 'token': [2] + [0] * 22}}) - - def _get_http_latency(self, resource, storage): - if resource == ModelTelemetry.HTTPExceptionsAndLatencies.SPLIT: - return storage._split - elif resource == ModelTelemetry.HTTPExceptionsAndLatencies.SEGMENT: - return storage._segment - elif resource == ModelTelemetry.HTTPExceptionsAndLatencies.IMPRESSION: - return storage._impression - elif resource == ModelTelemetry.HTTPExceptionsAndLatencies.IMPRESSION_COUNT: - return storage._impression_count - elif resource == ModelTelemetry.HTTPExceptionsAndLatencies.EVENT: - return storage._event - elif resource == ModelTelemetry.HTTPExceptionsAndLatencies.TELEMETRY: - return storage._telemetry - elif resource == ModelTelemetry.HTTPExceptionsAndLatencies.TOKEN: - return storage._token - else: - return - - @pytest.mark.asyncio - async def test_method_exceptions(self, mocker): - method_exception = await MethodExceptionsAsync.create() - - [await method_exception.add_exception(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENT) for i in range(2)] - await method_exception.add_exception(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENTS) - await method_exception.add_exception(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENT_WITH_CONFIG) - [await method_exception.add_exception(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG) for i in range(5)] - [await method_exception.add_exception(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SET) for i in range(6)] - [await method_exception.add_exception(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SETS) for i in range(7)] - [await method_exception.add_exception(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SET) for i in range(8)] - [await method_exception.add_exception(ModelTelemetry.MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SETS) for i in range(9)] - [await method_exception.add_exception(ModelTelemetry.MethodExceptionsAndLatencies.TRACK) for i in range(3)] - exceptions = await method_exception.pop_all() - - assert(method_exception._treatment == 0) - assert(method_exception._treatments == 0) - assert(method_exception._treatment_with_config == 0) - assert(method_exception._treatments_with_config == 0) - assert(method_exception._treatments_by_flag_set == 0) - assert(method_exception._treatments_by_flag_sets == 0) - assert(method_exception._treatments_with_config_by_flag_set == 0) - assert(method_exception._treatments_with_config_by_flag_sets == 0) - assert(method_exception._track == 0) - assert(exceptions == {'methodExceptions': {'treatment': 2, - 'treatments': 1, - 'treatment_with_config': 1, - 'treatments_with_config': 5, - 'treatments_by_flag_set': 6, - 'treatments_by_flag_sets': 7, - 'treatments_with_config_by_flag_set': 8, - 'treatments_with_config_by_flag_sets': 9, - 'track': 3}}) - - @pytest.mark.asyncio - async def test_http_errors(self, mocker): - http_error = await HTTPErrorsAsync.create() - [await http_error.add_error(ModelTelemetry.HTTPExceptionsAndLatencies.SEGMENT, str(i)) for i in [500, 501, 502]] - [await http_error.add_error(ModelTelemetry.HTTPExceptionsAndLatencies.SPLIT, str(i)) for i in [400, 401, 402]] - await http_error.add_error(ModelTelemetry.HTTPExceptionsAndLatencies.IMPRESSION, '502') - [await http_error.add_error(ModelTelemetry.HTTPExceptionsAndLatencies.IMPRESSION_COUNT, str(i)) for i in [501, 502]] - await http_error.add_error(ModelTelemetry.HTTPExceptionsAndLatencies.EVENT, '501') - await http_error.add_error(ModelTelemetry.HTTPExceptionsAndLatencies.TELEMETRY, '505') - [await http_error.add_error(ModelTelemetry.HTTPExceptionsAndLatencies.TOKEN, '502') for i in range(5)] - errors = await http_error.pop_all() - assert(errors == {'httpErrors': {'split': {'400': 1, '401': 1, '402': 1}, 'segment': {'500': 1, '501': 1, '502': 1}, - 'impression': {'502': 1}, 'impressionCount': {'501': 1, '502': 1}, - 'event': {'501': 1}, 'telemetry': {'505': 1}, 'token': {'502': 5}}}) - assert(http_error._split == {}) - assert(http_error._segment == {}) - assert(http_error._impression == {}) - assert(http_error._impression_count == {}) - assert(http_error._event == {}) - assert(http_error._telemetry == {}) - - @pytest.mark.asyncio - async def test_last_synchronization(self, mocker): - last_synchronization = await LastSynchronizationAsync.create() - await last_synchronization.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.SPLIT, 10) - await last_synchronization.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.IMPRESSION, 20) - await last_synchronization.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.SEGMENT, 40) - await last_synchronization.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.IMPRESSION_COUNT, 60) - await last_synchronization.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.EVENT, 90) - await last_synchronization.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.TELEMETRY, 70) - await last_synchronization.add_latency(ModelTelemetry.HTTPExceptionsAndLatencies.TOKEN, 15) - assert(await last_synchronization.get_all() == {'lastSynchronizations': {'split': 10, 'segment': 40, 'impression': 20, 'impressionCount': 60, 'event': 90, 'telemetry': 70, 'token': 15}}) - - @pytest.mark.asyncio - async def test_telemetry_counters(self): - telemetry_counter = await TelemetryCountersAsync.create() - assert(telemetry_counter._impressions_queued == 0) - assert(telemetry_counter._impressions_deduped == 0) - assert(telemetry_counter._impressions_dropped == 0) - assert(telemetry_counter._events_dropped == 0) - assert(telemetry_counter._events_queued == 0) - assert(telemetry_counter._auth_rejections == 0) - assert(telemetry_counter._token_refreshes == 0) - assert(telemetry_counter._update_from_sse == {}) - - await telemetry_counter.record_session_length(20) - assert(await telemetry_counter.get_session_length() == 20) - - [await telemetry_counter.record_auth_rejections() for i in range(5)] - auth_rejections = await telemetry_counter.pop_auth_rejections() - assert(telemetry_counter._auth_rejections == 0) - assert(auth_rejections == 5) - - [await telemetry_counter.record_token_refreshes() for i in range(3)] - token_refreshes = await telemetry_counter.pop_token_refreshes() - assert(telemetry_counter._token_refreshes == 0) - assert(token_refreshes == 3) - - await telemetry_counter.record_impressions_value(ModelTelemetry.CounterConstants.IMPRESSIONS_QUEUED, 10) - assert(telemetry_counter._impressions_queued == 10) - await telemetry_counter.record_impressions_value(ModelTelemetry.CounterConstants.IMPRESSIONS_DEDUPED, 14) - assert(telemetry_counter._impressions_deduped == 14) - await telemetry_counter.record_impressions_value(ModelTelemetry.CounterConstants.IMPRESSIONS_DROPPED, 2) - assert(telemetry_counter._impressions_dropped == 2) - await telemetry_counter.record_events_value(ModelTelemetry.CounterConstants.EVENTS_QUEUED, 30) - assert(telemetry_counter._events_queued == 30) - await telemetry_counter.record_events_value(ModelTelemetry.CounterConstants.EVENTS_DROPPED, 1) - assert(telemetry_counter._events_dropped == 1) - await telemetry_counter.record_update_from_sse(UpdateFromSSE.SPLIT_UPDATE) - assert(telemetry_counter._update_from_sse[UpdateFromSSE.SPLIT_UPDATE.value] == 1) - updates = await telemetry_counter.pop_update_from_sse(UpdateFromSSE.SPLIT_UPDATE) - assert(telemetry_counter._update_from_sse[UpdateFromSSE.SPLIT_UPDATE.value] == 0) - assert(updates == 1) - - @pytest.mark.asyncio - async def test_streaming_events(self, mocker): - streaming_events = await StreamingEventsAsync.create() - await streaming_events.record_streaming_event((ModelTelemetry.StreamingEventTypes.CONNECTION_ESTABLISHED, 'split', 1234)) - await streaming_events.record_streaming_event((ModelTelemetry.StreamingEventTypes.STREAMING_STATUS, 'split', 1234)) - events = await streaming_events.pop_streaming_events() - assert(streaming_events._streaming_events == []) - assert(events == {'streamingEvents': [{'e': ModelTelemetry.StreamingEventTypes.CONNECTION_ESTABLISHED.value, 'd': 'split', 't': 1234}, - {'e': ModelTelemetry.StreamingEventTypes.STREAMING_STATUS.value, 'd': 'split', 't': 1234}]}) - - @pytest.mark.asyncio - async def test_telemetry_config(self): - telemetry_config = await TelemetryConfigAsync.create() - config = {'operationMode': 'standalone', - 'streamingEnabled': True, - 'impressionsQueueSize': 100, - 'eventsQueueSize': 200, - 'impressionsMode': 'DEBUG','' - 'impressionListener': None, - 'featuresRefreshRate': 30, - 'segmentsRefreshRate': 30, - 'impressionsRefreshRate': 60, - 'eventsPushRate': 60, - 'metricsRefreshRate': 10, - 'storageType': None, - 'flagSetsFilter': None - } - await telemetry_config.record_config(config, {}, 5, 2) - assert(await telemetry_config.get_stats() == {'oM': 0, - 'sT': telemetry_config._get_storage_type(config['operationMode'], config['storageType']), - 'sE': config['streamingEnabled'], - 'rR': {'sp': 30, 'se': 30, 'im': 60, 'ev': 60, 'te': 10}, - 'uO': {'s': False, 'e': False, 'a': False, 'st': False, 't': False}, - 'iQ': config['impressionsQueueSize'], - 'eQ': config['eventsQueueSize'], - 'iM': telemetry_config._get_impressions_mode(config['impressionsMode']), - 'iL': True if config['impressionListener'] is not None else False, - 'hp': telemetry_config._check_if_proxy_detected(), - 'tR': 0, - 'nR': 0, - 'bT': 0, - 'aF': 0, - 'rF': 0, - 'fsT': 5, - 'fsI': 2} - ) - - await telemetry_config.record_ready_time(10) - assert(telemetry_config._time_until_ready == 10) - - [await telemetry_config.record_bur_time_out() for i in range(2)] - assert(await telemetry_config.get_bur_time_outs() == 2) - - [await telemetry_config.record_not_ready_usage() for i in range(5)] - assert(await telemetry_config.get_non_ready_usage() == 5) diff --git a/tests/models/test_token.py b/tests/models/test_token.py deleted file mode 100644 index 35444f97..00000000 --- a/tests/models/test_token.py +++ /dev/null @@ -1,49 +0,0 @@ -"""Split model tests module.""" - -from splitio.models import token -from splitio.models.grammar.condition import Condition - - -class TokenTests(object): - """Token model tests.""" - raw_false = {'pushEnabled': False} - - def test_from_raw_false(self): - """Test token model parsing.""" - parsed = token.from_raw(self.raw_false) - assert parsed.push_enabled == False - assert parsed.iat == None - assert parsed.channels == None - assert parsed.exp == None - assert parsed.token == None - - raw_empty = { - 'pushEnabled': True, - 'token': '', - } - - def test_from_raw_empty(self): - """Test token model parsing.""" - parsed = token.from_raw(self.raw_empty) - assert parsed.push_enabled == False - assert parsed.iat == None - assert parsed.channels == None - assert parsed.exp == None - assert parsed.token == None - - raw_ok = { - 'pushEnabled': True, - 'token': 'eyJhbGciOiJIUzI1NiIsImtpZCI6IjVZOU05US45QnJtR0EiLCJ0eXAiOiJKV1QifQ.eyJ4LWFibHktY2FwYWJpbGl0eSI6IntcIk56TTJNREk1TXpjMF9NVGd5TlRnMU1UZ3dOZz09X3NlZ21lbnRzXCI6W1wic3Vic2NyaWJlXCJdLFwiTnpNMk1ESTVNemMwX01UZ3lOVGcxTVRnd05nPT1fc3BsaXRzXCI6W1wic3Vic2NyaWJlXCJdLFwiY29udHJvbF9wcmlcIjpbXCJzdWJzY3JpYmVcIixcImNoYW5uZWwtbWV0YWRhdGE6cHVibGlzaGVyc1wiXSxcImNvbnRyb2xfc2VjXCI6W1wic3Vic2NyaWJlXCIsXCJjaGFubmVsLW1ldGFkYXRhOnB1Ymxpc2hlcnNcIl19IiwieC1hYmx5LWNsaWVudElkIjoiY2xpZW50SWQiLCJleHAiOjE2MDIwODgxMjcsImlhdCI6MTYwMjA4NDUyN30.5_MjWonhs6yoFhw44hNJm3H7_YMjXpSW105DwjjppqE', - } - - def test_from_raw(self): - """Test token model parsing.""" - parsed = token.from_raw(self.raw_ok) - assert isinstance(parsed, token.Token) - assert parsed.push_enabled == True - assert parsed.iat == 1602084527 - assert parsed.exp == 1602088127 - assert parsed.channels['NzM2MDI5Mzc0_MTgyNTg1MTgwNg==_segments'] == ['subscribe'] - assert parsed.channels['NzM2MDI5Mzc0_MTgyNTg1MTgwNg==_splits'] == ['subscribe'] - assert parsed.channels['control_pri'] == ['subscribe', 'channel-metadata:publishers'] - assert parsed.channels['control_sec'] == ['subscribe', 'channel-metadata:publishers'] diff --git a/tests/push/test_manager.py b/tests/push/test_manager.py index 3525baf3..29cd45d9 100644 --- a/tests/push/test_manager.py +++ b/tests/push/test_manager.py @@ -5,7 +5,7 @@ import pytest from splitio.api import APIException -from splitio.models.token import Token +from harness_commons.models.token import Token from splitio.push.sse import SSEEvent from splitio.push.parser import parse_incoming_event, EventType, ControlType, ControlMessage, \ OccupancyMessage, SplitChangeUpdate, SplitKillUpdate, SegmentChangeUpdate @@ -16,7 +16,7 @@ from splitio.push.status_tracker import Status from splitio.engine.telemetry import TelemetryStorageProducer, TelemetryStorageProducerAsync from splitio.storage.inmemmory import InMemoryTelemetryStorage, InMemoryTelemetryStorageAsync -from splitio.models.telemetry import StreamingEventTypes +from harness_commons.models.telemetry import StreamingEventTypes from splitio.optional.loaders import asyncio from tests.helpers import Any diff --git a/tests/push/test_segment_worker.py b/tests/push/test_segment_worker.py index 0a99f466..328a6308 100644 --- a/tests/push/test_segment_worker.py +++ b/tests/push/test_segment_worker.py @@ -5,7 +5,7 @@ from splitio.api import APIException from splitio.push.workers import SegmentWorker, SegmentWorkerAsync -from splitio.models.notification import SegmentChangeNotification +from harness_commons.models.notification import SegmentChangeNotification from splitio.optional.loaders import asyncio change_number_received = None diff --git a/tests/push/test_split_worker.py b/tests/push/test_split_worker.py index 28b5408d..1a9810af 100644 --- a/tests/push/test_split_worker.py +++ b/tests/push/test_split_worker.py @@ -6,7 +6,7 @@ from splitio.api import APIException from splitio.push.workers import SplitWorker, SplitWorkerAsync -from splitio.models.notification import SplitChangeNotification +from harness_commons.models.notification import SplitChangeNotification from splitio.optional.loaders import asyncio from splitio.push.parser import SplitChangeUpdate, RBSChangeUpdate from splitio.engine.telemetry import TelemetryStorageProducer, TelemetryStorageProducerAsync diff --git a/tests/push/test_splitsse.py b/tests/push/test_splitsse.py index c461f9fe..eef89fd0 100644 --- a/tests/push/test_splitsse.py +++ b/tests/push/test_splitsse.py @@ -4,7 +4,7 @@ from queue import Queue import pytest -from splitio.models.token import Token +from harness_commons.models.token import Token from splitio.push.splitsse import SplitSSEClient, SplitSSEClientAsync from splitio.push.sse import SSEEvent, SSE_EVENT_ERROR diff --git a/tests/push/test_status_tracker.py b/tests/push/test_status_tracker.py index b77bd483..a2bdcf5d 100644 --- a/tests/push/test_status_tracker.py +++ b/tests/push/test_status_tracker.py @@ -6,7 +6,7 @@ from splitio.push.parser import ControlType, AblyError, OccupancyMessage, ControlMessage from splitio.engine.telemetry import TelemetryStorageProducer, TelemetryStorageProducerAsync from splitio.storage.inmemmory import InMemoryTelemetryStorage, InMemoryTelemetryStorageAsync -from splitio.models.telemetry import StreamingEventTypes, SSEStreamingStatus, SSEConnectionError +from harness_commons.models.telemetry import StreamingEventTypes, SSEStreamingStatus, SSEConnectionError class StatusTrackerTests(object): diff --git a/tests/recorder/test_recorder.py b/tests/recorder/test_recorder.py index cf226613..bf39596e 100644 --- a/tests/recorder/test_recorder.py +++ b/tests/recorder/test_recorder.py @@ -11,8 +11,8 @@ from splitio.storage.inmemmory import EventStorage, ImpressionStorage, InMemoryTelemetryStorage, InMemoryEventStorageAsync, InMemoryImpressionStorageAsync from splitio.storage.redis import ImpressionPipelinedStorage, EventStorage, RedisEventsStorage, RedisImpressionsStorage, RedisImpressionsStorageAsync, RedisEventsStorageAsync from splitio.storage.adapters.redis import RedisAdapter, RedisAdapterAsync -from splitio.models.impressions import Impression -from splitio.models.telemetry import MethodExceptionsAndLatencies +from harness_commons.models.impressions import Impression +from harness_commons.models.telemetry import MethodExceptionsAndLatencies from splitio.optional.loaders import asyncio class StandardRecorderTests(object): diff --git a/tests/storage/test_inmemory_storage.py b/tests/storage/test_inmemory_storage.py index d46980aa..873f8636 100644 --- a/tests/storage/test_inmemory_storage.py +++ b/tests/storage/test_inmemory_storage.py @@ -7,18 +7,18 @@ import asyncio from splitio.models.splits import Split -from splitio.models.segments import Segment -from splitio.models.impressions import Impression -from splitio.models.events import Event, EventWrapper -from splitio.models.events import SdkInternalEvent -import splitio.models.telemetry as ModelTelemetry +from harness_commons.models.segments import Segment +from harness_commons.models.impressions import Impression +from harness_commons.models.events import Event, EventWrapper +from harness_commons.models.events import SdkInternalEvent +import harness_commons.models.telemetry as ModelTelemetry from splitio.engine.telemetry import TelemetryStorageProducer, TelemetryStorageProducerAsync from splitio.events.events_metadata import SdkEventType from splitio.storage.inmemmory import InMemorySplitStorage, InMemorySegmentStorage, InMemorySegmentStorageAsync, InMemorySplitStorageAsync, \ InMemoryImpressionStorage, InMemoryEventStorage, InMemoryTelemetryStorage, InMemoryImpressionStorageAsync, InMemoryEventStorageAsync, \ InMemoryTelemetryStorageAsync, FlagSets, InMemoryRuleBasedSegmentStorage, InMemoryRuleBasedSegmentStorageAsync -from splitio.models.rule_based_segments import RuleBasedSegment -from splitio.models import rule_based_segments +from harness_commons.models.rule_based_segments import RuleBasedSegment +from harness_commons.models import rule_based_segments class FlagSetsFilterTests(object): """Flag sets filter storage tests.""" diff --git a/tests/storage/test_pluggable.py b/tests/storage/test_pluggable.py index 8b5f9a95..086059ad 100644 --- a/tests/storage/test_pluggable.py +++ b/tests/storage/test_pluggable.py @@ -6,15 +6,16 @@ from splitio.optional.loaders import asyncio from splitio.models.splits import Split -from splitio.models import splits, segments, rule_based_segments -from splitio.models.segments import Segment -from splitio.models.impressions import Impression -from splitio.models.events import Event, EventWrapper +from splitio.models import splits +from harness_commons.models import rule_based_segments +from harness_commons.models.segments import Segment +from harness_commons.models.impressions import Impression +from harness_commons.models.events import Event, EventWrapper from splitio.storage.pluggable import PluggableSplitStorage, PluggableSegmentStorage, PluggableImpressionsStorage, PluggableEventsStorage, \ PluggableTelemetryStorage, PluggableEventsStorageAsync, PluggableSegmentStorageAsync, PluggableImpressionsStorageAsync,\ PluggableSplitStorageAsync, PluggableTelemetryStorageAsync, PluggableRuleBasedSegmentsStorage, PluggableRuleBasedSegmentsStorageAsync from splitio.client.util import get_metadata, SdkMetadata -from splitio.models.telemetry import MAX_TAGS, MethodExceptionsAndLatencies, OperationMode +from harness_commons.models.telemetry import MAX_TAGS, MethodExceptionsAndLatencies, OperationMode from tests.integration import splits_json, rbsegments_json class StorageMockAdapter(object): diff --git a/tests/storage/test_redis.py b/tests/storage/test_redis.py index a45c4ad2..bb4fb774 100644 --- a/tests/storage/test_redis.py +++ b/tests/storage/test_redis.py @@ -17,10 +17,10 @@ from splitio.storage.adapters.redis import RedisAdapter, RedisAdapterException, build from redis.asyncio.client import Redis as aioredis from splitio.storage.adapters import redis -from splitio.models.segments import Segment -from splitio.models.impressions import Impression -from splitio.models.events import Event, EventWrapper -from splitio.models.telemetry import MethodExceptions, MethodLatencies, TelemetryConfig, MethodExceptionsAndLatencies, TelemetryConfigAsync +from harness_commons.models.segments import Segment +from harness_commons.models.impressions import Impression +from harness_commons.models.events import Event, EventWrapper +from harness_commons.models.telemetry import MethodExceptions, MethodLatencies, TelemetryConfig, MethodExceptionsAndLatencies, TelemetryConfigAsync class RedisSplitStorageTests(object): """Redis split storage test cases.""" @@ -1011,7 +1011,7 @@ def test_init(self, mocker): assert(isinstance(redis_telemetry._tel_config, TelemetryConfig)) assert(redis_telemetry._make_pipe is not None) - @mock.patch('splitio.models.telemetry.TelemetryConfig.record_config') + @mock.patch('harness_commons.models.telemetry.TelemetryConfig.record_config') def test_record_config(self, mocker): redis_telemetry = RedisTelemetryStorage(mocker.Mock(), mocker.Mock()) redis_telemetry.record_config(mocker.Mock(), mocker.Mock(), 0, 0) diff --git a/tests/sync/test_events_synchronizer.py b/tests/sync/test_events_synchronizer.py index 7eb52dc4..425ca5a3 100644 --- a/tests/sync/test_events_synchronizer.py +++ b/tests/sync/test_events_synchronizer.py @@ -7,7 +7,7 @@ from splitio.api.client import HttpResponse from splitio.api import APIException from splitio.storage import EventStorage -from splitio.models.events import Event +from harness_commons.models.events import Event from splitio.sync.event import EventSynchronizer, EventSynchronizerAsync diff --git a/tests/sync/test_impressions_synchronizer.py b/tests/sync/test_impressions_synchronizer.py index 00b65833..6516ea23 100644 --- a/tests/sync/test_impressions_synchronizer.py +++ b/tests/sync/test_impressions_synchronizer.py @@ -7,7 +7,7 @@ from splitio.api.client import HttpResponse from splitio.api import APIException from splitio.storage import ImpressionStorage -from splitio.models.impressions import Impression +from harness_commons.models.impressions import Impression from splitio.sync.impression import ImpressionSynchronizer, ImpressionSynchronizerAsync diff --git a/tests/sync/test_manager.py b/tests/sync/test_manager.py index 47ac3f01..68f82dff 100644 --- a/tests/sync/test_manager.py +++ b/tests/sync/test_manager.py @@ -16,7 +16,7 @@ from splitio.tasks.events_sync import EventsSyncTask from splitio.engine.telemetry import TelemetryStorageProducer, TelemetryStorageProducerAsync from splitio.storage.inmemmory import InMemoryTelemetryStorage, InMemoryTelemetryStorageAsync -from splitio.models.telemetry import SSESyncMode, StreamingEventTypes +from harness_commons.models.telemetry import SSESyncMode, StreamingEventTypes from splitio.push.manager import Status from splitio.sync.split import SplitSynchronizer, SplitSynchronizerAsync from splitio.sync.segment import SegmentSynchronizer diff --git a/tests/sync/test_segments_synchronizer.py b/tests/sync/test_segments_synchronizer.py index 5b405ef8..08aaf7b2 100644 --- a/tests/sync/test_segments_synchronizer.py +++ b/tests/sync/test_segments_synchronizer.py @@ -8,8 +8,8 @@ from splitio.storage import SplitStorage, SegmentStorage, RuleBasedSegmentsStorage from splitio.storage.inmemmory import InMemorySegmentStorage, InMemorySegmentStorageAsync, InMemorySplitStorage, InMemorySplitStorageAsync from splitio.sync.segment import SegmentSynchronizer, SegmentSynchronizerAsync, LocalSegmentSynchronizer, LocalSegmentSynchronizerAsync -from splitio.models.segments import Segment -from splitio.models import rule_based_segments +from harness_commons.models.segments import Segment +from harness_commons.models import rule_based_segments from splitio.optional.loaders import aiofiles, asyncio import pytest diff --git a/tests/sync/test_splits_synchronizer.py b/tests/sync/test_splits_synchronizer.py index b27606a4..bf770117 100644 --- a/tests/sync/test_splits_synchronizer.py +++ b/tests/sync/test_splits_synchronizer.py @@ -13,7 +13,7 @@ from splitio.storage.inmemmory import InMemorySplitStorage, InMemorySplitStorageAsync, InMemoryRuleBasedSegmentStorage, InMemoryRuleBasedSegmentStorageAsync from splitio.storage import FlagSetsFilter from splitio.models.splits import Split -from splitio.models.rule_based_segments import RuleBasedSegment +from harness_commons.models.rule_based_segments import RuleBasedSegment from splitio.sync.split import SplitSynchronizer, SplitSynchronizerAsync, LocalSplitSynchronizer, LocalSplitSynchronizerAsync, LocalhostMode from splitio.optional.loaders import aiofiles, asyncio from tests.integration import splits_json, rbsegments_json diff --git a/tests/sync/test_synchronizer.py b/tests/sync/test_synchronizer.py index 1244429b..1e4d9d97 100644 --- a/tests/sync/test_synchronizer.py +++ b/tests/sync/test_synchronizer.py @@ -17,7 +17,7 @@ from splitio.storage import SegmentStorage, SplitStorage, RuleBasedSegmentsStorage from splitio.api import APIException, APIUriException from splitio.models.splits import Split -from splitio.models.segments import Segment +from harness_commons.models.segments import Segment from splitio.storage.inmemmory import InMemorySegmentStorage, InMemorySplitStorage, InMemorySegmentStorageAsync, InMemorySplitStorageAsync, \ InMemoryRuleBasedSegmentStorage, InMemoryRuleBasedSegmentStorageAsync diff --git a/tests/sync/test_telemetry.py b/tests/sync/test_telemetry.py index dd8119e2..29f25744 100644 --- a/tests/sync/test_telemetry.py +++ b/tests/sync/test_telemetry.py @@ -8,8 +8,8 @@ from splitio.engine.telemetry import TelemetryStorageConsumer, TelemetryStorageConsumerAsync from splitio.storage.inmemmory import InMemoryTelemetryStorage, InMemoryTelemetryStorageAsync, InMemorySegmentStorage, InMemorySegmentStorageAsync, InMemorySplitStorage, InMemorySplitStorageAsync from splitio.models.splits import Split, Status -from splitio.models.segments import Segment -from splitio.models.telemetry import StreamingEvents, StreamingEventsAsync +from harness_commons.models.segments import Segment +from harness_commons.models.telemetry import StreamingEvents, StreamingEventsAsync from splitio.api.telemetry import TelemetryAPI class TelemetrySynchronizerTests(object): diff --git a/tests/tasks/test_events_sync.py b/tests/tasks/test_events_sync.py index b2ea500d..39b3681a 100644 --- a/tests/tasks/test_events_sync.py +++ b/tests/tasks/test_events_sync.py @@ -7,7 +7,7 @@ from splitio.api.client import HttpResponse from splitio.tasks import events_sync from splitio.storage import EventStorage -from splitio.models.events import Event +from harness_commons.models.events import Event from splitio.api.events import EventsAPI from splitio.sync.event import EventSynchronizer, EventSynchronizerAsync from splitio.optional.loaders import asyncio diff --git a/tests/tasks/test_impressions_sync.py b/tests/tasks/test_impressions_sync.py index 78bbf979..2fccae73 100644 --- a/tests/tasks/test_impressions_sync.py +++ b/tests/tasks/test_impressions_sync.py @@ -7,7 +7,7 @@ from splitio.api.client import HttpResponse from splitio.tasks import impressions_sync from splitio.storage import ImpressionStorage -from splitio.models.impressions import Impression +from harness_commons.models.impressions import Impression from splitio.api.impressions import ImpressionsAPI from splitio.sync.impression import ImpressionSynchronizer, ImpressionsCountSynchronizer, ImpressionSynchronizerAsync, ImpressionsCountSynchronizerAsync from splitio.engine.impressions.manager import Counter diff --git a/tests/tasks/test_segment_sync.py b/tests/tasks/test_segment_sync.py index cc701e52..24befe5d 100644 --- a/tests/tasks/test_segment_sync.py +++ b/tests/tasks/test_segment_sync.py @@ -8,9 +8,9 @@ from splitio.tasks import segment_sync from splitio.storage import SegmentStorage, SplitStorage, RuleBasedSegmentsStorage from splitio.models.splits import Split -from splitio.models.segments import Segment -from splitio.models.grammar.condition import Condition -from splitio.models.grammar.matchers import UserDefinedSegmentMatcher +from harness_commons.models.segments import Segment +from harness_commons.models.grammar.condition import Condition +from harness_commons.models.grammar.matchers import UserDefinedSegmentMatcher from splitio.sync.segment import SegmentSynchronizer, SegmentSynchronizerAsync from splitio.optional.loaders import asyncio diff --git a/tests/util/test_storage_helper.py b/tests/util/test_storage_helper.py index 60e83e8c..feec22d8 100644 --- a/tests/util/test_storage_helper.py +++ b/tests/util/test_storage_helper.py @@ -8,7 +8,8 @@ get_standard_segment_names_in_rbs_storage_async, get_standard_segment_names_in_rbs_storage from splitio.storage.inmemmory import InMemorySplitStorage, InMemoryRuleBasedSegmentStorage, InMemoryRuleBasedSegmentStorageAsync, \ InMemorySplitStorageAsync -from splitio.models import splits, rule_based_segments +from splitio.models import splits +from harness_commons.models import rule_based_segments from splitio.storage import FlagSetsFilter from tests.sync.test_splits_synchronizer import splits_raw as split_sample