splitio-client 9.5.1__tar.gz → 9.6.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {splitio_client-9.5.1 → splitio_client-9.6.1}/PKG-INFO +3 -3
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/api/commons.py +14 -1
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/api/splits.py +2 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/api/telemetry.py +0 -1
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/client/client.py +133 -5
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/client/config.py +10 -4
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/client/factory.py +21 -11
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/client/input_validator.py +52 -20
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/engine/telemetry.py +30 -12
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/models/splits.py +18 -5
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/models/telemetry.py +97 -48
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/models/token.py +12 -21
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/push/manager.py +2 -1
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/push/splitworker.py +7 -12
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/storage/__init__.py +48 -28
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/storage/adapters/redis.py +8 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/storage/inmemmory.py +175 -20
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/storage/pluggable.py +116 -50
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/storage/redis.py +110 -84
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/sync/split.py +31 -26
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/sync/synchronizer.py +6 -2
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/tasks/util/asynctask.py +2 -2
- splitio_client-9.6.1/splitio/util/storage_helper.py +71 -0
- splitio_client-9.6.1/splitio/version.py +1 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio_client.egg-info/PKG-INFO +3 -3
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio_client.egg-info/SOURCES.txt +2 -30
- splitio_client-9.5.1/splitio/version.py +0 -1
- splitio_client-9.5.1/tests/__init__.py +0 -0
- splitio_client-9.5.1/tests/helpers/__init__.py +0 -9
- splitio_client-9.5.1/tests/helpers/mockserver.py +0 -249
- splitio_client-9.5.1/tests/integration/__init__.py +0 -42
- splitio_client-9.5.1/tests/integration/test_client_e2e.py +0 -1878
- splitio_client-9.5.1/tests/integration/test_pluggable_integration.py +0 -247
- splitio_client-9.5.1/tests/integration/test_redis_integration.py +0 -244
- splitio_client-9.5.1/tests/integration/test_streaming_e2e.py +0 -1478
- splitio_client-9.5.1/tests/push/__init__.py +0 -0
- splitio_client-9.5.1/tests/push/test_manager.py +0 -230
- splitio_client-9.5.1/tests/push/test_parser.py +0 -116
- splitio_client-9.5.1/tests/push/test_processor.py +0 -58
- splitio_client-9.5.1/tests/push/test_segment_worker.py +0 -60
- splitio_client-9.5.1/tests/push/test_split_worker.py +0 -206
- splitio_client-9.5.1/tests/push/test_splitsse.py +0 -126
- splitio_client-9.5.1/tests/push/test_sse.py +0 -125
- splitio_client-9.5.1/tests/push/test_status_tracker.py +0 -195
- splitio_client-9.5.1/tests/recorder/__init__.py +0 -0
- splitio_client-9.5.1/tests/recorder/test_recorder.py +0 -79
- splitio_client-9.5.1/tests/sync/__init__.py +0 -0
- splitio_client-9.5.1/tests/sync/test_events_synchronizer.py +0 -68
- splitio_client-9.5.1/tests/sync/test_impressions_count_synchronizer.py +0 -38
- splitio_client-9.5.1/tests/sync/test_impressions_synchronizer.py +0 -68
- splitio_client-9.5.1/tests/sync/test_manager.py +0 -123
- splitio_client-9.5.1/tests/sync/test_segments_synchronizer.py +0 -358
- splitio_client-9.5.1/tests/sync/test_splits_synchronizer.py +0 -524
- splitio_client-9.5.1/tests/sync/test_synchronizer.py +0 -400
- splitio_client-9.5.1/tests/sync/test_telemetry.py +0 -140
- splitio_client-9.5.1/tests/sync/test_unique_keys_sync.py +0 -54
- {splitio_client-9.5.1 → splitio_client-9.6.1}/README.md +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/setup.cfg +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/setup.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/__init__.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/api/__init__.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/api/auth.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/api/client.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/api/events.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/api/impressions.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/api/segments.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/client/__init__.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/client/key.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/client/listener.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/client/localhost.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/client/manager.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/client/util.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/engine/__init__.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/engine/cache/__init__.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/engine/cache/lru.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/engine/evaluator.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/engine/filters.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/engine/hashfns/__init__.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/engine/hashfns/legacy.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/engine/hashfns/murmur3py.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/engine/impressions/__init__.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/engine/impressions/adapters.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/engine/impressions/impressions.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/engine/impressions/manager.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/engine/impressions/strategies.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/engine/impressions/unique_keys_tracker.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/engine/splitters.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/exceptions.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/factories.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/impressions.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/key.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/models/__init__.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/models/datatypes.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/models/events.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/models/grammar/__init__.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/models/grammar/condition.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/models/grammar/matchers/__init__.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/models/grammar/matchers/base.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/models/grammar/matchers/keys.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/models/grammar/matchers/misc.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/models/grammar/matchers/numeric.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/models/grammar/matchers/sets.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/models/grammar/matchers/string.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/models/grammar/partitions.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/models/impressions.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/models/notification.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/models/segments.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/push/__init__.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/push/parser.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/push/processor.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/push/segmentworker.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/push/splitsse.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/push/sse.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/push/status_tracker.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/recorder/__init__.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/recorder/recorder.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/storage/adapters/__init__.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/storage/adapters/cache_trait.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/storage/adapters/util.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/sync/__init__.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/sync/event.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/sync/impression.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/sync/manager.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/sync/segment.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/sync/telemetry.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/sync/unique_keys.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/sync/util.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/tasks/__init__.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/tasks/events_sync.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/tasks/impressions_sync.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/tasks/segment_sync.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/tasks/split_sync.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/tasks/telemetry_sync.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/tasks/unique_keys_sync.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/tasks/util/__init__.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/tasks/util/workerpool.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/util/__init__.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/util/backoff.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/util/decorators.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/util/threadutil.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio/util/time.py +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio_client.egg-info/dependency_links.txt +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio_client.egg-info/requires.txt +0 -0
- {splitio_client-9.5.1 → splitio_client-9.6.1}/splitio_client.egg-info/top_level.txt +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: splitio_client
|
|
3
|
-
Version: 9.
|
|
3
|
+
Version: 9.6.1
|
|
4
4
|
Summary: Split.io Python Client
|
|
5
5
|
Home-page: https://github.com/splitio/python-client
|
|
6
6
|
Author: Patricio Echague, Sebastian Arrubia
|
|
7
7
|
Author-email: pato@split.io, sebastian@split.io
|
|
8
8
|
License: Apache License 2.0
|
|
9
|
-
Download-URL: https://github.com/splitio/python-client/tarball/9.
|
|
9
|
+
Download-URL: https://github.com/splitio/python-client/tarball/9.6.1
|
|
10
10
|
Description: UNKNOWN
|
|
11
11
|
Platform: UNKNOWN
|
|
12
12
|
Classifier: Environment :: Console
|
|
@@ -15,7 +15,7 @@ Classifier: Programming Language :: Python
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 2
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
|
17
17
|
Classifier: Topic :: Software Development :: Libraries
|
|
18
|
+
Provides-Extra: test
|
|
18
19
|
Provides-Extra: redis
|
|
19
20
|
Provides-Extra: uwsgi
|
|
20
|
-
Provides-Extra: test
|
|
21
21
|
Provides-Extra: cpphash
|
|
@@ -57,7 +57,7 @@ def record_telemetry(status_code, elapsed, metric_name, telemetry_runtime_produc
|
|
|
57
57
|
class FetchOptions(object):
|
|
58
58
|
"""Fetch Options object."""
|
|
59
59
|
|
|
60
|
-
def __init__(self, cache_control_headers=False, change_number=None):
|
|
60
|
+
def __init__(self, cache_control_headers=False, change_number=None, sets=None):
|
|
61
61
|
"""
|
|
62
62
|
Class constructor.
|
|
63
63
|
|
|
@@ -66,9 +66,13 @@ class FetchOptions(object):
|
|
|
66
66
|
|
|
67
67
|
:param change_number: ChangeNumber to use for bypassing CDN in request.
|
|
68
68
|
:type change_number: int
|
|
69
|
+
|
|
70
|
+
:param sets: list of flag sets
|
|
71
|
+
:type sets: list
|
|
69
72
|
"""
|
|
70
73
|
self._cache_control_headers = cache_control_headers
|
|
71
74
|
self._change_number = change_number
|
|
75
|
+
self._sets = sets
|
|
72
76
|
|
|
73
77
|
@property
|
|
74
78
|
def cache_control_headers(self):
|
|
@@ -80,12 +84,19 @@ class FetchOptions(object):
|
|
|
80
84
|
"""Return change number."""
|
|
81
85
|
return self._change_number
|
|
82
86
|
|
|
87
|
+
@property
|
|
88
|
+
def sets(self):
|
|
89
|
+
"""Return sets."""
|
|
90
|
+
return self._sets
|
|
91
|
+
|
|
83
92
|
def __eq__(self, other):
|
|
84
93
|
"""Match between other options."""
|
|
85
94
|
if self._cache_control_headers != other._cache_control_headers:
|
|
86
95
|
return False
|
|
87
96
|
if self._change_number != other._change_number:
|
|
88
97
|
return False
|
|
98
|
+
if self._sets != other._sets:
|
|
99
|
+
return False
|
|
89
100
|
return True
|
|
90
101
|
|
|
91
102
|
|
|
@@ -113,4 +124,6 @@ def build_fetch(change_number, fetch_options, metadata):
|
|
|
113
124
|
extra_headers[_CACHE_CONTROL] = _CACHE_CONTROL_NO_CACHE
|
|
114
125
|
if fetch_options.change_number is not None:
|
|
115
126
|
query['till'] = fetch_options.change_number
|
|
127
|
+
if fetch_options.sets is not None:
|
|
128
|
+
query['sets'] = fetch_options.sets
|
|
116
129
|
return query, extra_headers
|
|
@@ -59,6 +59,8 @@ class SplitsAPI(object): # pylint: disable=too-few-public-methods
|
|
|
59
59
|
if 200 <= response.status_code < 300:
|
|
60
60
|
return json.loads(response.body)
|
|
61
61
|
else:
|
|
62
|
+
if response.status_code == 414:
|
|
63
|
+
_LOGGER.error('Error fetching feature flags; the amount of flag sets provided are too big, causing uri length error.')
|
|
62
64
|
raise APIException(response.body, response.status_code)
|
|
63
65
|
except HttpClientException as exc:
|
|
64
66
|
_LOGGER.error('Error fetching feature flags because an exception was raised by the HTTPClient')
|
|
@@ -76,7 +76,6 @@ class TelemetryAPI(object): # pylint: disable=too-few-public-methods
|
|
|
76
76
|
'Error posting init config because an exception was raised by the HTTPClient'
|
|
77
77
|
)
|
|
78
78
|
_LOGGER.debug('Error: ', exc_info=True)
|
|
79
|
-
raise APIException('Init config data not flushed properly.') from exc
|
|
80
79
|
|
|
81
80
|
def record_stats(self, stats):
|
|
82
81
|
"""
|
|
@@ -6,7 +6,7 @@ from splitio.engine.splitters import Splitter
|
|
|
6
6
|
from splitio.models.impressions import Impression, Label
|
|
7
7
|
from splitio.models.events import Event, EventWrapper
|
|
8
8
|
from splitio.models.telemetry import get_latency_bucket_index, MethodExceptionsAndLatencies
|
|
9
|
-
from splitio.client import input_validator
|
|
9
|
+
from splitio.client import input_validator, config
|
|
10
10
|
from splitio.util.time import get_current_epoch_time_ms, utctime_ms
|
|
11
11
|
|
|
12
12
|
_LOGGER = logging.getLogger(__name__)
|
|
@@ -59,8 +59,9 @@ class Client(object): # pylint: disable=too-many-instance-attributes
|
|
|
59
59
|
"""Return whether the factory holding this client has been destroyed."""
|
|
60
60
|
return self._factory.destroyed
|
|
61
61
|
|
|
62
|
-
def _evaluate_if_ready(self, matching_key, bucketing_key, feature, attributes=None):
|
|
62
|
+
def _evaluate_if_ready(self, matching_key, bucketing_key, feature, method, attributes=None):
|
|
63
63
|
if not self.ready:
|
|
64
|
+
_LOGGER.warning("%s: The SDK is not ready, results may be incorrect for feature flag %s. Make sure to wait for SDK readiness before using this method", method, feature)
|
|
64
65
|
self._telemetry_init_producer.record_not_ready_usage()
|
|
65
66
|
return {
|
|
66
67
|
'treatment': CONTROL,
|
|
@@ -102,7 +103,7 @@ class Client(object): # pylint: disable=too-many-instance-attributes
|
|
|
102
103
|
or not input_validator.validate_attributes(attributes, method_name):
|
|
103
104
|
return CONTROL, None
|
|
104
105
|
|
|
105
|
-
result = self._evaluate_if_ready(matching_key, bucketing_key, feature_flag, attributes)
|
|
106
|
+
result = self._evaluate_if_ready(matching_key, bucketing_key, feature_flag, method_name, attributes)
|
|
106
107
|
|
|
107
108
|
impression = self._build_impression(
|
|
108
109
|
matching_key,
|
|
@@ -167,7 +168,7 @@ class Client(object): # pylint: disable=too-many-instance-attributes
|
|
|
167
168
|
|
|
168
169
|
try:
|
|
169
170
|
evaluations = self._evaluate_features_if_ready(matching_key, bucketing_key,
|
|
170
|
-
list(feature_flags), attributes)
|
|
171
|
+
list(feature_flags), method_name, attributes)
|
|
171
172
|
|
|
172
173
|
for feature_flag in feature_flags:
|
|
173
174
|
try:
|
|
@@ -212,8 +213,9 @@ class Client(object): # pylint: disable=too-many-instance-attributes
|
|
|
212
213
|
_LOGGER.debug('Error: ', exc_info=True)
|
|
213
214
|
return input_validator.generate_control_treatments(list(feature_flags), method_name)
|
|
214
215
|
|
|
215
|
-
def _evaluate_features_if_ready(self, matching_key, bucketing_key, feature_flags, attributes=None):
|
|
216
|
+
def _evaluate_features_if_ready(self, matching_key, bucketing_key, feature_flags, method, attributes=None):
|
|
216
217
|
if not self.ready:
|
|
218
|
+
_LOGGER.warning("%s: The SDK is not ready, results may be incorrect for feature flags %s. Make sure to wait for SDK readiness before using this method", method, ', '.join([feature for feature in feature_flags]))
|
|
217
219
|
self._telemetry_init_producer.record_not_ready_usage()
|
|
218
220
|
return {
|
|
219
221
|
feature_flag: {
|
|
@@ -309,6 +311,132 @@ class Client(object): # pylint: disable=too-many-instance-attributes
|
|
|
309
311
|
MethodExceptionsAndLatencies.TREATMENTS)
|
|
310
312
|
return {feature_flag: result[0] for (feature_flag, result) in with_config.items()}
|
|
311
313
|
|
|
314
|
+
def get_treatments_by_flag_set(self, key, flag_set, attributes=None):
|
|
315
|
+
"""
|
|
316
|
+
Get treatments for feature flags that contain given flag set.
|
|
317
|
+
|
|
318
|
+
This method never raises an exception. If there's a problem, the appropriate log message
|
|
319
|
+
will be generated and the method will return the CONTROL treatment.
|
|
320
|
+
|
|
321
|
+
:param key: The key for which to get the treatment
|
|
322
|
+
:type key: str
|
|
323
|
+
:param flag_set: flag set
|
|
324
|
+
:type flag_sets: str
|
|
325
|
+
:param attributes: An optional dictionary of attributes
|
|
326
|
+
:type attributes: dict
|
|
327
|
+
|
|
328
|
+
:return: Dictionary with the result of all the feature flags provided
|
|
329
|
+
:rtype: dict
|
|
330
|
+
"""
|
|
331
|
+
return self._get_treatments_by_flag_sets( key, [flag_set], MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SET, attributes)
|
|
332
|
+
|
|
333
|
+
def get_treatments_by_flag_sets(self, key, flag_sets, attributes=None):
|
|
334
|
+
"""
|
|
335
|
+
Get treatments for feature flags that contain given flag sets.
|
|
336
|
+
|
|
337
|
+
This method never raises an exception. If there's a problem, the appropriate log message
|
|
338
|
+
will be generated and the method will return the CONTROL treatment.
|
|
339
|
+
|
|
340
|
+
:param key: The key for which to get the treatment
|
|
341
|
+
:type key: str
|
|
342
|
+
:param flag_sets: list of flag sets
|
|
343
|
+
:type flag_sets: list
|
|
344
|
+
:param attributes: An optional dictionary of attributes
|
|
345
|
+
:type attributes: dict
|
|
346
|
+
|
|
347
|
+
:return: Dictionary with the result of all the feature flags provided
|
|
348
|
+
:rtype: dict
|
|
349
|
+
"""
|
|
350
|
+
return self._get_treatments_by_flag_sets( key, flag_sets, MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SETS, attributes)
|
|
351
|
+
|
|
352
|
+
def get_treatments_with_config_by_flag_set(self, key, flag_set, attributes=None):
|
|
353
|
+
"""
|
|
354
|
+
Get treatments for feature flags that contain given flag set.
|
|
355
|
+
|
|
356
|
+
This method never raises an exception. If there's a problem, the appropriate log message
|
|
357
|
+
will be generated and the method will return the CONTROL treatment.
|
|
358
|
+
|
|
359
|
+
:param key: The key for which to get the treatment
|
|
360
|
+
:type key: str
|
|
361
|
+
:param flag_set: flag set
|
|
362
|
+
:type flag_sets: str
|
|
363
|
+
:param attributes: An optional dictionary of attributes
|
|
364
|
+
:type attributes: dict
|
|
365
|
+
|
|
366
|
+
:return: Dictionary with the result of all the feature flags provided
|
|
367
|
+
:rtype: dict
|
|
368
|
+
"""
|
|
369
|
+
return self._get_treatments_by_flag_sets( key, [flag_set], MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SET, attributes)
|
|
370
|
+
|
|
371
|
+
def get_treatments_with_config_by_flag_sets(self, key, flag_sets, attributes=None):
|
|
372
|
+
"""
|
|
373
|
+
Get treatments for feature flags that contain given flag set.
|
|
374
|
+
|
|
375
|
+
This method never raises an exception. If there's a problem, the appropriate log message
|
|
376
|
+
will be generated and the method will return the CONTROL treatment.
|
|
377
|
+
|
|
378
|
+
:param key: The key for which to get the treatment
|
|
379
|
+
:type key: str
|
|
380
|
+
:param flag_set: flag set
|
|
381
|
+
:type flag_sets: str
|
|
382
|
+
:param attributes: An optional dictionary of attributes
|
|
383
|
+
:type attributes: dict
|
|
384
|
+
|
|
385
|
+
:return: Dictionary with the result of all the feature flags provided
|
|
386
|
+
:rtype: dict
|
|
387
|
+
"""
|
|
388
|
+
return self._get_treatments_by_flag_sets( key, flag_sets, MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SETS, attributes)
|
|
389
|
+
|
|
390
|
+
def _get_treatments_by_flag_sets(self, key, flag_sets, method, attributes=None):
|
|
391
|
+
"""
|
|
392
|
+
Get treatments for feature flags that contain given flag sets.
|
|
393
|
+
|
|
394
|
+
This method never raises an exception. If there's a problem, the appropriate log message
|
|
395
|
+
will be generated and the method will return the CONTROL treatment.
|
|
396
|
+
|
|
397
|
+
:param key: The key for which to get the treatment
|
|
398
|
+
:type key: str
|
|
399
|
+
:param flag_sets: list of flag sets
|
|
400
|
+
:type flag_sets: list
|
|
401
|
+
:param method: Treatment by flag set method flavor
|
|
402
|
+
:type method: splitio.models.telemetry.MethodExceptionsAndLatencies
|
|
403
|
+
:param attributes: An optional dictionary of attributes
|
|
404
|
+
:type attributes: dict
|
|
405
|
+
|
|
406
|
+
:return: Dictionary with the result of all the feature flags provided
|
|
407
|
+
:rtype: dict
|
|
408
|
+
"""
|
|
409
|
+
feature_flags_names = self._get_feature_flag_names_by_flag_sets(flag_sets, method.value)
|
|
410
|
+
if feature_flags_names == []:
|
|
411
|
+
_LOGGER.warning("%s: No valid Flag set or no feature flags found for evaluating treatments" % (method.value))
|
|
412
|
+
return {}
|
|
413
|
+
|
|
414
|
+
if 'config' in method.value:
|
|
415
|
+
return self._make_evaluations(key, feature_flags_names, attributes, method.value,
|
|
416
|
+
method)
|
|
417
|
+
|
|
418
|
+
with_config = self._make_evaluations(key, feature_flags_names, attributes, method.value,
|
|
419
|
+
method)
|
|
420
|
+
return {feature_flag: result[0] for (feature_flag, result) in with_config.items()}
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def _get_feature_flag_names_by_flag_sets(self, flag_sets, method_name):
|
|
424
|
+
"""
|
|
425
|
+
Sanitize given flag sets and return list of feature flag names associated with them
|
|
426
|
+
|
|
427
|
+
:param flag_sets: list of flag sets
|
|
428
|
+
:type flag_sets: list
|
|
429
|
+
|
|
430
|
+
:return: list of feature flag names
|
|
431
|
+
:rtype: list
|
|
432
|
+
"""
|
|
433
|
+
sanitized_flag_sets = input_validator.validate_flag_sets(flag_sets, method_name)
|
|
434
|
+
feature_flags_by_set = self._split_storage.get_feature_flags_by_sets(sanitized_flag_sets)
|
|
435
|
+
if feature_flags_by_set is None:
|
|
436
|
+
_LOGGER.warning("Fetching feature flags for flag set %s encountered an error, skipping this flag set." % (flag_sets))
|
|
437
|
+
return []
|
|
438
|
+
return feature_flags_by_set
|
|
439
|
+
|
|
312
440
|
def _build_impression( # pylint: disable=too-many-arguments
|
|
313
441
|
self,
|
|
314
442
|
matching_key,
|
|
@@ -3,12 +3,12 @@ import os.path
|
|
|
3
3
|
import logging
|
|
4
4
|
|
|
5
5
|
from splitio.engine.impressions import ImpressionsMode
|
|
6
|
+
from splitio.client.input_validator import validate_flag_sets
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
_LOGGER = logging.getLogger(__name__)
|
|
9
10
|
DEFAULT_DATA_SAMPLING = 1
|
|
10
11
|
|
|
11
|
-
|
|
12
12
|
DEFAULT_CONFIG = {
|
|
13
13
|
'operationMode': 'standalone',
|
|
14
14
|
'connectionTimeout': 1500,
|
|
@@ -31,6 +31,7 @@ DEFAULT_CONFIG = {
|
|
|
31
31
|
'redisHost': 'localhost',
|
|
32
32
|
'redisPort': 6379,
|
|
33
33
|
'redisDb': 0,
|
|
34
|
+
'redisUsername': None,
|
|
34
35
|
'redisPassword': None,
|
|
35
36
|
'redisSocketTimeout': None,
|
|
36
37
|
'redisSocketConnectTimeout': None,
|
|
@@ -58,10 +59,10 @@ DEFAULT_CONFIG = {
|
|
|
58
59
|
'dataSampling': DEFAULT_DATA_SAMPLING,
|
|
59
60
|
'storageWrapper': None,
|
|
60
61
|
'storagePrefix': None,
|
|
61
|
-
'storageType': None
|
|
62
|
+
'storageType': None,
|
|
63
|
+
'flagSetsFilter': None
|
|
62
64
|
}
|
|
63
65
|
|
|
64
|
-
|
|
65
66
|
def _parse_operation_mode(sdk_key, config):
|
|
66
67
|
"""
|
|
67
68
|
Process incoming config to determine operation mode and storage type
|
|
@@ -118,7 +119,6 @@ def _sanitize_impressions_mode(storage_type, mode, refresh_rate=None):
|
|
|
118
119
|
|
|
119
120
|
return mode, refresh_rate
|
|
120
121
|
|
|
121
|
-
|
|
122
122
|
def sanitize(sdk_key, config):
|
|
123
123
|
"""
|
|
124
124
|
Look for inconsistencies or ill-formed configs and tune it accordingly.
|
|
@@ -143,4 +143,10 @@ def sanitize(sdk_key, config):
|
|
|
143
143
|
_LOGGER.warning('metricRefreshRate parameter minimum value is 60 seconds, defaulting to 3600 seconds.')
|
|
144
144
|
processed['metricsRefreshRate'] = 3600
|
|
145
145
|
|
|
146
|
+
if config['operationMode'] == 'consumer' and config.get('flagSetsFilter') is not None:
|
|
147
|
+
processed['flagSetsFilter'] = None
|
|
148
|
+
_LOGGER.warning('config: FlagSets filter is not applicable for Consumer modes where the SDK does keep rollout data in sync. FlagSet filter was discarded.')
|
|
149
|
+
else:
|
|
150
|
+
processed['flagSetsFilter'] = sorted(validate_flag_sets(processed['flagSetsFilter'], 'SDK Config')) if processed['flagSetsFilter'] is not None else None
|
|
151
|
+
|
|
146
152
|
return processed
|
|
@@ -312,7 +312,8 @@ def _wrap_impression_listener(listener, metadata):
|
|
|
312
312
|
|
|
313
313
|
|
|
314
314
|
def _build_in_memory_factory(api_key, cfg, sdk_url=None, events_url=None, # pylint:disable=too-many-arguments,too-many-locals
|
|
315
|
-
auth_api_base_url=None, streaming_api_base_url=None, telemetry_api_base_url=None
|
|
315
|
+
auth_api_base_url=None, streaming_api_base_url=None, telemetry_api_base_url=None,
|
|
316
|
+
total_flag_sets=0, invalid_flag_sets=0):
|
|
316
317
|
"""Build and return a split factory tailored to the supplied config."""
|
|
317
318
|
if not input_validator.validate_factory_instantiation(api_key):
|
|
318
319
|
return None
|
|
@@ -350,7 +351,7 @@ def _build_in_memory_factory(api_key, cfg, sdk_url=None, events_url=None, # pyl
|
|
|
350
351
|
}
|
|
351
352
|
|
|
352
353
|
storages = {
|
|
353
|
-
'splits': InMemorySplitStorage(),
|
|
354
|
+
'splits': InMemorySplitStorage(cfg['flagSetsFilter'] if cfg['flagSetsFilter'] is not None else []),
|
|
354
355
|
'segments': InMemorySegmentStorage(),
|
|
355
356
|
'impressions': InMemoryImpressionStorage(cfg['impressionsQueueSize'], telemetry_runtime_producer),
|
|
356
357
|
'events': InMemoryEventStorage(cfg['eventsQueueSize'], telemetry_runtime_producer),
|
|
@@ -416,7 +417,7 @@ def _build_in_memory_factory(api_key, cfg, sdk_url=None, events_url=None, # pyl
|
|
|
416
417
|
telemetry_evaluation_producer
|
|
417
418
|
)
|
|
418
419
|
|
|
419
|
-
telemetry_init_producer.record_config(cfg, extra_cfg)
|
|
420
|
+
telemetry_init_producer.record_config(cfg, extra_cfg, total_flag_sets, invalid_flag_sets)
|
|
420
421
|
|
|
421
422
|
if preforked_initialization:
|
|
422
423
|
synchronizer.sync_all(max_retry_attempts=_MAX_RETRY_SYNC_ALL)
|
|
@@ -440,7 +441,7 @@ def _build_redis_factory(api_key, cfg):
|
|
|
440
441
|
cache_enabled = cfg.get('redisLocalCacheEnabled', False)
|
|
441
442
|
cache_ttl = cfg.get('redisLocalCacheTTL', 5)
|
|
442
443
|
storages = {
|
|
443
|
-
'splits': RedisSplitStorage(redis_adapter, cache_enabled, cache_ttl),
|
|
444
|
+
'splits': RedisSplitStorage(redis_adapter, cache_enabled, cache_ttl, []),
|
|
444
445
|
'segments': RedisSegmentStorage(redis_adapter),
|
|
445
446
|
'impressions': RedisImpressionsStorage(redis_adapter, sdk_metadata),
|
|
446
447
|
'events': RedisEventsStorage(redis_adapter, sdk_metadata),
|
|
@@ -495,7 +496,7 @@ def _build_redis_factory(api_key, cfg):
|
|
|
495
496
|
initialization_thread = threading.Thread(target=manager.start, name="SDKInitializer", daemon=True)
|
|
496
497
|
initialization_thread.start()
|
|
497
498
|
|
|
498
|
-
telemetry_init_producer.record_config(cfg, {})
|
|
499
|
+
telemetry_init_producer.record_config(cfg, {}, 0, 0)
|
|
499
500
|
|
|
500
501
|
split_factory = SplitFactory(
|
|
501
502
|
api_key,
|
|
@@ -523,7 +524,7 @@ def _build_pluggable_factory(api_key, cfg):
|
|
|
523
524
|
pluggable_adapter = cfg.get('storageWrapper')
|
|
524
525
|
storage_prefix = cfg.get('storagePrefix')
|
|
525
526
|
storages = {
|
|
526
|
-
'splits': PluggableSplitStorage(pluggable_adapter, storage_prefix),
|
|
527
|
+
'splits': PluggableSplitStorage(pluggable_adapter, storage_prefix, []),
|
|
527
528
|
'segments': PluggableSegmentStorage(pluggable_adapter, storage_prefix),
|
|
528
529
|
'impressions': PluggableImpressionsStorage(pluggable_adapter, sdk_metadata, storage_prefix),
|
|
529
530
|
'events': PluggableEventsStorage(pluggable_adapter, sdk_metadata, storage_prefix),
|
|
@@ -573,7 +574,7 @@ def _build_pluggable_factory(api_key, cfg):
|
|
|
573
574
|
initialization_thread = threading.Thread(target=manager.start, name="SDKInitializer", daemon=True)
|
|
574
575
|
initialization_thread.start()
|
|
575
576
|
|
|
576
|
-
telemetry_init_producer.record_config(cfg, {})
|
|
577
|
+
telemetry_init_producer.record_config(cfg, {}, 0, 0)
|
|
577
578
|
|
|
578
579
|
split_factory = SplitFactory(
|
|
579
580
|
api_key,
|
|
@@ -600,7 +601,7 @@ def _build_localhost_factory(cfg):
|
|
|
600
601
|
telemetry_evaluation_producer = telemetry_producer.get_telemetry_evaluation_producer()
|
|
601
602
|
|
|
602
603
|
storages = {
|
|
603
|
-
'splits': InMemorySplitStorage(),
|
|
604
|
+
'splits': InMemorySplitStorage(cfg['flagSetsFilter'] if cfg['flagSetsFilter'] is not None else []),
|
|
604
605
|
'segments': InMemorySegmentStorage(), # not used, just to avoid possible future errors.
|
|
605
606
|
'impressions': LocalhostImpressionsStorage(),
|
|
606
607
|
'events': LocalhostEventsStorage(),
|
|
@@ -684,7 +685,14 @@ def get_factory(api_key, **kwargs):
|
|
|
684
685
|
_INSTANTIATED_FACTORIES.update([api_key])
|
|
685
686
|
_INSTANTIATED_FACTORIES_LOCK.release()
|
|
686
687
|
|
|
687
|
-
|
|
688
|
+
config_raw = kwargs.get('config', {})
|
|
689
|
+
total_flag_sets = 0
|
|
690
|
+
invalid_flag_sets = 0
|
|
691
|
+
if config_raw.get('flagSetsFilter') is not None and isinstance(config_raw.get('flagSetsFilter'), list):
|
|
692
|
+
total_flag_sets = len(config_raw.get('flagSetsFilter'))
|
|
693
|
+
invalid_flag_sets = total_flag_sets - len(input_validator.validate_flag_sets(config_raw.get('flagSetsFilter'), 'Telemetry Init'))
|
|
694
|
+
|
|
695
|
+
config = sanitize_config(api_key, config_raw)
|
|
688
696
|
|
|
689
697
|
if config['operationMode'] == 'localhost':
|
|
690
698
|
split_factory = _build_localhost_factory(config)
|
|
@@ -700,7 +708,9 @@ def get_factory(api_key, **kwargs):
|
|
|
700
708
|
kwargs.get('events_api_base_url'),
|
|
701
709
|
kwargs.get('auth_api_base_url'),
|
|
702
710
|
kwargs.get('streaming_api_base_url'),
|
|
703
|
-
kwargs.get('telemetry_api_base_url')
|
|
711
|
+
kwargs.get('telemetry_api_base_url'),
|
|
712
|
+
total_flag_sets,
|
|
713
|
+
invalid_flag_sets)
|
|
704
714
|
|
|
705
715
|
return split_factory
|
|
706
716
|
|
|
@@ -712,4 +722,4 @@ def _get_active_and_redundant_count():
|
|
|
712
722
|
redundant_factory_count += _INSTANTIATED_FACTORIES[item] - 1
|
|
713
723
|
active_factory_count += _INSTANTIATED_FACTORIES[item]
|
|
714
724
|
_INSTANTIATED_FACTORIES_LOCK.release()
|
|
715
|
-
return redundant_factory_count, active_factory_count
|
|
725
|
+
return redundant_factory_count, active_factory_count
|
|
@@ -15,6 +15,7 @@ _LOGGER = logging.getLogger(__name__)
|
|
|
15
15
|
MAX_LENGTH = 250
|
|
16
16
|
EVENT_TYPE_PATTERN = r'^[a-zA-Z0-9][-_.:a-zA-Z0-9]{0,79}$'
|
|
17
17
|
MAX_PROPERTIES_LENGTH_BYTES = 32768
|
|
18
|
+
_FLAG_SETS_REGEX = '^[a-z0-9][_a-z0-9]{0,49}$'
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
def _check_not_null(value, name, operation):
|
|
@@ -79,7 +80,8 @@ def _check_string_not_empty(value, name, operation):
|
|
|
79
80
|
return True
|
|
80
81
|
|
|
81
82
|
|
|
82
|
-
|
|
83
|
+
|
|
84
|
+
def _check_string_matches(value, operation, pattern, name, length):
|
|
83
85
|
"""
|
|
84
86
|
Check if value is adhere to a regular expression passed.
|
|
85
87
|
|
|
@@ -92,14 +94,14 @@ def _check_string_matches(value, operation, pattern):
|
|
|
92
94
|
:return: The result of validation
|
|
93
95
|
:rtype: True|False
|
|
94
96
|
"""
|
|
95
|
-
if
|
|
97
|
+
if re.search(pattern, value) is None or re.search(pattern, value).group() != value:
|
|
96
98
|
_LOGGER.error(
|
|
97
99
|
'%s: you passed %s, event_type must ' +
|
|
98
100
|
'adhere to the regular expression %s. ' +
|
|
99
|
-
'This means
|
|
100
|
-
'than
|
|
101
|
+
'This means %s must be alphanumeric, cannot be more ' +
|
|
102
|
+
'than %s characters long, and can only include a dash, underscore, ' +
|
|
101
103
|
'period, or colon as separators of alphanumeric characters.',
|
|
102
|
-
operation, value, pattern
|
|
104
|
+
operation, value, pattern, name, length
|
|
103
105
|
)
|
|
104
106
|
return False
|
|
105
107
|
return True
|
|
@@ -165,10 +167,7 @@ def _check_valid_object_key(key, name, operation):
|
|
|
165
167
|
:return: The result of validation
|
|
166
168
|
:rtype: str|None
|
|
167
169
|
"""
|
|
168
|
-
if key
|
|
169
|
-
_LOGGER.error(
|
|
170
|
-
'%s: you passed a null %s, %s must be a non-empty string.',
|
|
171
|
-
operation, name, name)
|
|
170
|
+
if not _check_not_null(key, 'key', operation):
|
|
172
171
|
return None
|
|
173
172
|
if isinstance(key, str):
|
|
174
173
|
if not _check_string_not_empty(key, name, operation):
|
|
@@ -179,7 +178,7 @@ def _check_valid_object_key(key, name, operation):
|
|
|
179
178
|
return key_str
|
|
180
179
|
|
|
181
180
|
|
|
182
|
-
def _remove_empty_spaces(value, operation):
|
|
181
|
+
def _remove_empty_spaces(value, name, operation):
|
|
183
182
|
"""
|
|
184
183
|
Check if an string has whitespaces.
|
|
185
184
|
|
|
@@ -192,10 +191,17 @@ def _remove_empty_spaces(value, operation):
|
|
|
192
191
|
"""
|
|
193
192
|
strip_value = value.strip()
|
|
194
193
|
if value != strip_value:
|
|
195
|
-
_LOGGER.warning("%s:
|
|
194
|
+
_LOGGER.warning("%s: %s '%s' has extra whitespace, trimming.", operation, name, value)
|
|
196
195
|
return strip_value
|
|
197
196
|
|
|
198
197
|
|
|
198
|
+
def _convert_str_to_lower(value, name, operation):
|
|
199
|
+
lower_value = value.lower()
|
|
200
|
+
if value != lower_value:
|
|
201
|
+
_LOGGER.warning("%s: %s '%s' should be all lowercase - converting string to lowercase" % (operation, name, value))
|
|
202
|
+
return lower_value
|
|
203
|
+
|
|
204
|
+
|
|
199
205
|
def validate_key(key, method_name):
|
|
200
206
|
"""
|
|
201
207
|
Validate Key parameter for get_treatment/s.
|
|
@@ -211,8 +217,7 @@ def validate_key(key, method_name):
|
|
|
211
217
|
"""
|
|
212
218
|
matching_key_result = None
|
|
213
219
|
bucketing_key_result = None
|
|
214
|
-
if key
|
|
215
|
-
_LOGGER.error('%s: you passed a null key, key must be a non-empty string.', method_name)
|
|
220
|
+
if not _check_not_null(key, 'key', method_name):
|
|
216
221
|
return None, None
|
|
217
222
|
|
|
218
223
|
if isinstance(key, Key):
|
|
@@ -255,7 +260,7 @@ def validate_feature_flag_name(feature_flag_name, should_validate_existance, fea
|
|
|
255
260
|
)
|
|
256
261
|
return None
|
|
257
262
|
|
|
258
|
-
return _remove_empty_spaces(feature_flag_name, method_name)
|
|
263
|
+
return _remove_empty_spaces(feature_flag_name, 'feature flag name', method_name)
|
|
259
264
|
|
|
260
265
|
|
|
261
266
|
def validate_track_key(key):
|
|
@@ -294,10 +299,7 @@ def validate_traffic_type(traffic_type, should_validate_existance, feature_flag_
|
|
|
294
299
|
(not _check_is_string(traffic_type, 'traffic_type', 'track')) or \
|
|
295
300
|
(not _check_string_not_empty(traffic_type, 'traffic_type', 'track')):
|
|
296
301
|
return None
|
|
297
|
-
|
|
298
|
-
_LOGGER.warning('track: %s should be all lowercase - converting string to lowercase.',
|
|
299
|
-
traffic_type)
|
|
300
|
-
traffic_type = traffic_type.lower()
|
|
302
|
+
traffic_type = _convert_str_to_lower(traffic_type, 'traffic type', 'track')
|
|
301
303
|
|
|
302
304
|
if should_validate_existance and not feature_flag_storage.is_valid_traffic_type(traffic_type):
|
|
303
305
|
_LOGGER.warning(
|
|
@@ -322,7 +324,7 @@ def validate_event_type(event_type):
|
|
|
322
324
|
if (not _check_not_null(event_type, 'event_type', 'track')) or \
|
|
323
325
|
(not _check_is_string(event_type, 'event_type', 'track')) or \
|
|
324
326
|
(not _check_string_not_empty(event_type, 'event_type', 'track')) or \
|
|
325
|
-
(not _check_string_matches(event_type, 'track', EVENT_TYPE_PATTERN)):
|
|
327
|
+
(not _check_string_matches(event_type, 'track', EVENT_TYPE_PATTERN, 'an event name', 80)):
|
|
326
328
|
return None
|
|
327
329
|
return event_type
|
|
328
330
|
|
|
@@ -390,7 +392,7 @@ def validate_feature_flags_get_treatments( # pylint: disable=invalid-name
|
|
|
390
392
|
_LOGGER.error("%s: feature flag names must be a non-empty array.", method_name)
|
|
391
393
|
return None, None
|
|
392
394
|
filtered_feature_flags = set(
|
|
393
|
-
_remove_empty_spaces(feature_flag, method_name) for feature_flag in feature_flags
|
|
395
|
+
_remove_empty_spaces(feature_flag, 'feature flag name', method_name) for feature_flag in feature_flags
|
|
394
396
|
if feature_flag is not None and
|
|
395
397
|
_check_is_string(feature_flag, 'feature flag name', method_name) and
|
|
396
398
|
_check_string_not_empty(feature_flag, 'feature flag name', method_name)
|
|
@@ -566,3 +568,33 @@ def validate_pluggable_adapter(config):
|
|
|
566
568
|
_LOGGER.error("Pluggable adapter method %s has less than required arguments count: %s : " % (exp_method, len(get_method_args)))
|
|
567
569
|
return False
|
|
568
570
|
return True
|
|
571
|
+
|
|
572
|
+
def validate_flag_sets(flag_sets, method_name):
|
|
573
|
+
"""
|
|
574
|
+
Validate flag sets list
|
|
575
|
+
|
|
576
|
+
:param flag_set: list of flag sets
|
|
577
|
+
:type flag_set: list[str]
|
|
578
|
+
|
|
579
|
+
:returns: Sanitized and sorted flag sets
|
|
580
|
+
:rtype: list[str]
|
|
581
|
+
"""
|
|
582
|
+
if not isinstance(flag_sets, list):
|
|
583
|
+
_LOGGER.warning("%s: flag sets parameter type should be list object, parameter is discarded" % (method_name))
|
|
584
|
+
return []
|
|
585
|
+
|
|
586
|
+
sanitized_flag_sets = set()
|
|
587
|
+
for flag_set in flag_sets:
|
|
588
|
+
if not _check_not_null(flag_set, 'flag set', method_name):
|
|
589
|
+
continue
|
|
590
|
+
if not _check_is_string(flag_set, 'flag set', method_name):
|
|
591
|
+
continue
|
|
592
|
+
flag_set = _remove_empty_spaces(flag_set, 'flag set', method_name)
|
|
593
|
+
flag_set = _convert_str_to_lower(flag_set, 'flag set', method_name)
|
|
594
|
+
|
|
595
|
+
if not _check_string_matches(flag_set, method_name, _FLAG_SETS_REGEX, 'a flag set', 50):
|
|
596
|
+
continue
|
|
597
|
+
|
|
598
|
+
sanitized_flag_sets.add(flag_set)
|
|
599
|
+
|
|
600
|
+
return list(sanitized_flag_sets)
|
|
@@ -36,9 +36,9 @@ class TelemetryInitProducer(object):
|
|
|
36
36
|
"""Constructor."""
|
|
37
37
|
self._telemetry_storage = telemetry_storage
|
|
38
38
|
|
|
39
|
-
def record_config(self, config, extra_config):
|
|
39
|
+
def record_config(self, config, extra_config, total_flag_sets=0, invalid_flag_sets=0):
|
|
40
40
|
"""Record configurations."""
|
|
41
|
-
self._telemetry_storage.record_config(config, extra_config)
|
|
41
|
+
self._telemetry_storage.record_config(config, extra_config, total_flag_sets, invalid_flag_sets)
|
|
42
42
|
current_app, app_worker_id = self._get_app_worker_id()
|
|
43
43
|
if current_app is not None:
|
|
44
44
|
self.add_config_tag("initilization:" + current_app)
|
|
@@ -48,6 +48,14 @@ class TelemetryInitProducer(object):
|
|
|
48
48
|
"""Record ready time."""
|
|
49
49
|
self._telemetry_storage.record_ready_time(ready_time)
|
|
50
50
|
|
|
51
|
+
def record_flag_sets(self, flag_sets):
|
|
52
|
+
"""Record flag sets."""
|
|
53
|
+
self._telemetry_storage.record_flag_sets(flag_sets)
|
|
54
|
+
|
|
55
|
+
def record_invalid_flag_sets(self, flag_sets):
|
|
56
|
+
"""Record invalid flag sets."""
|
|
57
|
+
self._telemetry_storage.record_invalid_flag_sets(flag_sets)
|
|
58
|
+
|
|
51
59
|
def record_bur_time_out(self):
|
|
52
60
|
"""Record block until ready timeout."""
|
|
53
61
|
self._telemetry_storage.record_bur_time_out()
|
|
@@ -218,17 +226,27 @@ class TelemetryEvaluationConsumer(object):
|
|
|
218
226
|
exceptions = self.pop_exceptions()['methodExceptions']
|
|
219
227
|
latencies = self.pop_latencies()['methodLatencies']
|
|
220
228
|
return {
|
|
221
|
-
'mE': {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
229
|
+
'mE': {
|
|
230
|
+
't': exceptions['treatment'],
|
|
231
|
+
'ts': exceptions['treatments'],
|
|
232
|
+
'tc': exceptions['treatment_with_config'],
|
|
233
|
+
'tcs': exceptions['treatments_with_config'],
|
|
234
|
+
'tf': exceptions['treatments_by_flag_set'],
|
|
235
|
+
'tfs': exceptions['treatments_by_flag_sets'],
|
|
236
|
+
'tcf': exceptions['treatments_with_config_by_flag_set'],
|
|
237
|
+
'tcfs': exceptions['treatments_with_config_by_flag_sets'],
|
|
238
|
+
'tr': exceptions['track']
|
|
226
239
|
},
|
|
227
|
-
'mL': {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
240
|
+
'mL': {
|
|
241
|
+
't': latencies['treatment'],
|
|
242
|
+
'ts': latencies['treatments'],
|
|
243
|
+
'tc': latencies['treatment_with_config'],
|
|
244
|
+
'tcs': latencies['treatments_with_config'],
|
|
245
|
+
'tf': latencies['treatments_by_flag_set'],
|
|
246
|
+
'tfs': latencies['treatments_by_flag_sets'],
|
|
247
|
+
'tcf': latencies['treatments_with_config_by_flag_set'],
|
|
248
|
+
'tcfs': latencies['treatments_with_config_by_flag_sets'],
|
|
249
|
+
'tr': latencies['track']
|
|
232
250
|
},
|
|
233
251
|
}
|
|
234
252
|
|