Skip to content

Commit 52f4530

Browse files
authored
Merge branch 'Feature/FlagSets' into flagsets-test-integration
2 parents 7c6b1eb + b0e92bf commit 52f4530

File tree

13 files changed

+256
-104
lines changed

13 files changed

+256
-104
lines changed

splitio/client/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ def _get_treatments_by_flag_sets(self, key, flag_sets, method, attributes=None):
406406
"""
407407
feature_flags_names = self._get_feature_flag_names_by_flag_sets(flag_sets, method)
408408
if feature_flags_names == []:
409-
_LOGGER.warning("No valid Flag set or no feature flags found for evaluating treatments")
409+
_LOGGER.warning("%s: No valid Flag set or no feature flags found for evaluating treatments" % (method))
410410
return {}
411411

412412
if 'config' in method.value:

splitio/client/config.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
_LOGGER = logging.getLogger(__name__)
1111
DEFAULT_DATA_SAMPLING = 1
12-
_FLAG_SETS_REGEX = '^[a-z0-9][_a-z0-9]{0,49}$'
1312

1413
DEFAULT_CONFIG = {
1514
'operationMode': 'standalone',
@@ -120,7 +119,6 @@ def _sanitize_impressions_mode(storage_type, mode, refresh_rate=None):
120119

121120
return mode, refresh_rate
122121

123-
124122
def sanitize(sdk_key, config):
125123
"""
126124
Look for inconsistencies or ill-formed configs and tune it accordingly.

splitio/client/input_validator.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ def _check_string_not_empty(value, name, operation):
8080
return True
8181

8282

83-
def _check_string_matches(value, operation, pattern, name):
83+
84+
def _check_string_matches(value, operation, pattern, name, length):
8485
"""
8586
Check if value is adhere to a regular expression passed.
8687
@@ -98,9 +99,9 @@ def _check_string_matches(value, operation, pattern, name):
9899
'%s: you passed %s, event_type must ' +
99100
'adhere to the regular expression %s. ' +
100101
'This means %s must be alphanumeric, cannot be more ' +
101-
'than 80 characters long, and can only include a dash, underscore, ' +
102+
'than %s characters long, and can only include a dash, underscore, ' +
102103
'period, or colon as separators of alphanumeric characters.',
103-
operation, value, pattern, name
104+
operation, value, pattern, name, length
104105
)
105106
return False
106107
return True
@@ -323,7 +324,7 @@ def validate_event_type(event_type):
323324
if (not _check_not_null(event_type, 'event_type', 'track')) or \
324325
(not _check_is_string(event_type, 'event_type', 'track')) or \
325326
(not _check_string_not_empty(event_type, 'event_type', 'track')) or \
326-
(not _check_string_matches(event_type, 'track', EVENT_TYPE_PATTERN, 'an event name')):
327+
(not _check_string_matches(event_type, 'track', EVENT_TYPE_PATTERN, 'an event name', 80)):
327328
return None
328329
return event_type
329330

@@ -591,7 +592,7 @@ def validate_flag_sets(flag_sets, method_name):
591592
flag_set = _remove_empty_spaces(flag_set, 'flag set', method_name)
592593
flag_set = _convert_str_to_lower(flag_set, 'flag set', method_name)
593594

594-
if not _check_string_matches(flag_set, method_name, _FLAG_SETS_REGEX, 'a flag set'):
595+
if not _check_string_matches(flag_set, method_name, _FLAG_SETS_REGEX, 'a flag set', 50):
595596
continue
596597

597598
sanitized_flag_sets.add(flag_set)

splitio/models/flag_sets.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
"""Flagsets classes."""
2+
import threading
3+
4+
class FlagSetsFilter(object):
5+
"""Config Flagsets Filter storage."""
6+
7+
def __init__(self, flag_sets=[]):
8+
"""Constructor."""
9+
self.flag_sets = set(flag_sets)
10+
self.should_filter = any(flag_sets)
11+
12+
def set_exist(self, flag_set):
13+
"""
14+
Check if a flagset exist in flagset filter
15+
16+
:param flag_set: set name
17+
:type flag_set: str
18+
19+
:rtype: bool
20+
"""
21+
if not self.should_filter:
22+
return True
23+
if not isinstance(flag_set, str) or flag_set == '':
24+
return False
25+
26+
return any(self.flag_sets.intersection(set([flag_set])))
27+
28+
def intersect(self, flag_sets):
29+
"""
30+
Check if a set exist in config flagset filter
31+
32+
:param flag_set: set of flagsets
33+
:type flag_set: set
34+
35+
:rtype: bool
36+
"""
37+
if not self.should_filter:
38+
return True
39+
if not isinstance(flag_sets, set) or len(flag_sets) == 0:
40+
return False
41+
return any(self.flag_sets.intersection(flag_sets))
42+
43+
44+
class FlagSets(object):
45+
"""InMemory Flagsets storage."""
46+
47+
def __init__(self, flag_sets=[]):
48+
"""Constructor."""
49+
self._lock = threading.RLock()
50+
self.sets_feature_flag_map = {}
51+
for flag_set in flag_sets:
52+
self.sets_feature_flag_map[flag_set] = set()
53+
54+
def flag_set_exist(self, flag_set):
55+
"""
56+
Check if a flagset exist in stored flagset
57+
58+
:param flag_set: set name
59+
:type flag_set: str
60+
61+
:rtype: bool
62+
"""
63+
with self._lock:
64+
return flag_set in self.sets_feature_flag_map.keys()
65+
66+
def get_flag_set(self, flag_set):
67+
"""
68+
fetch feature flags stored in a flag set
69+
70+
:param flag_set: set name
71+
:type flag_set: str
72+
73+
:rtype: list(str)
74+
"""
75+
with self._lock:
76+
return self.sets_feature_flag_map.get(flag_set)
77+
78+
def add_flag_set(self, flag_set):
79+
"""
80+
Add new flag set to storage
81+
82+
:param flag_set: set name
83+
:type flag_set: str
84+
"""
85+
with self._lock:
86+
if not self.flag_set_exist(flag_set):
87+
self.sets_feature_flag_map[flag_set] = set()
88+
89+
def remove_flag_set(self, flag_set):
90+
"""
91+
Remove existing flag set from storage
92+
93+
:param flag_set: set name
94+
:type flag_set: str
95+
"""
96+
with self._lock:
97+
if self.flag_set_exist(flag_set):
98+
del self.sets_feature_flag_map[flag_set]
99+
100+
def add_feature_flag_to_flag_set(self, flag_set, feature_flag):
101+
"""
102+
Add a feature flag to existing flag set
103+
104+
:param flag_set: set name
105+
:type flag_set: str
106+
:param feature_flag: feature flag name
107+
:type feature_flag: str
108+
"""
109+
with self._lock:
110+
if self.flag_set_exist(flag_set):
111+
self.sets_feature_flag_map[flag_set].add(feature_flag)
112+
113+
def remove_feature_flag_to_flag_set(self, flag_set, feature_flag):
114+
"""
115+
Remove a feature flag from existing flag set
116+
117+
:param flag_set: set name
118+
:type flag_set: str
119+
:param feature_flag: feature flag name
120+
:type feature_flag: str
121+
"""
122+
with self._lock:
123+
if self.flag_set_exist(flag_set):
124+
self.sets_feature_flag_map[flag_set].remove(feature_flag)

splitio/push/splitworker.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,6 @@ def _run(self):
9090
try:
9191
new_feature_flag = from_raw(json.loads(self._get_feature_flag_definition(event)))
9292
segment_list = update_feature_flag_storage(self._feature_flag_storage, [new_feature_flag], event.change_number)
93-
'''
94-
if new_split.status == Status.ACTIVE:
95-
self._feature_flag_storage.put(new_split)
96-
_LOGGER.debug('Feature flag %s is updated', new_split.name)
97-
else:
98-
self._feature_flag_storage.remove(new_split.name)
99-
self._feature_flag_storage.set_change_number(event.change_number)
100-
'''
10193
for segment_name in segment_list:
10294
if self._segment_storage.get(segment_name) is None:
10395
_LOGGER.debug('Fetching new segment %s', segment_name)

splitio/storage/inmemmory.py

Lines changed: 0 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -14,89 +14,6 @@
1414

1515
_LOGGER = logging.getLogger(__name__)
1616

17-
class FlagSets(object):
18-
"""InMemory Flagsets storage."""
19-
20-
def __init__(self, flag_sets=[]):
21-
"""Constructor."""
22-
self._lock = threading.RLock()
23-
self.sets_feature_flag_map = {}
24-
for flag_set in flag_sets:
25-
self.sets_feature_flag_map[flag_set] = set()
26-
27-
def flag_set_exist(self, flag_set):
28-
"""
29-
Check if a flagset exist in stored flagset
30-
31-
:param flag_set: set name
32-
:type flag_set: str
33-
34-
:rtype: bool
35-
"""
36-
with self._lock:
37-
return flag_set in self.sets_feature_flag_map.keys()
38-
39-
def get_flag_set(self, flag_set):
40-
"""
41-
fetch feature flags stored in a flag set
42-
43-
:param flag_set: set name
44-
:type flag_set: str
45-
46-
:rtype: list(str)
47-
"""
48-
with self._lock:
49-
return self.sets_feature_flag_map.get(flag_set)
50-
51-
def add_flag_set(self, flag_set):
52-
"""
53-
Add new flag set to storage
54-
55-
:param flag_set: set name
56-
:type flag_set: str
57-
"""
58-
with self._lock:
59-
if not self.flag_set_exist(flag_set):
60-
self.sets_feature_flag_map[flag_set] = set()
61-
62-
def remove_flag_set(self, flag_set):
63-
"""
64-
Remove existing flag set from storage
65-
66-
:param flag_set: set name
67-
:type flag_set: str
68-
"""
69-
with self._lock:
70-
if self.flag_set_exist(flag_set):
71-
del self.sets_feature_flag_map[flag_set]
72-
73-
def add_feature_flag_to_flag_set(self, flag_set, feature_flag):
74-
"""
75-
Add a feature flag to existing flag set
76-
77-
:param flag_set: set name
78-
:type flag_set: str
79-
:param feature_flag: feature flag name
80-
:type feature_flag: str
81-
"""
82-
with self._lock:
83-
if self.flag_set_exist(flag_set):
84-
self.sets_feature_flag_map[flag_set].add(feature_flag)
85-
86-
def remove_feature_flag_to_flag_set(self, flag_set, feature_flag):
87-
"""
88-
Remove a feature flag from existing flag set
89-
90-
:param flag_set: set name
91-
:type flag_set: str
92-
:param feature_flag: feature flag name
93-
:type feature_flag: str
94-
"""
95-
with self._lock:
96-
if self.flag_set_exist(flag_set):
97-
self.sets_feature_flag_map[flag_set].remove(feature_flag)
98-
99-
10017
class InMemorySplitStorage(SplitStorage):
10118
"""InMemory implementation of a feature flag storage."""
10219

splitio/storage/pluggable.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,30 @@ def fetch_many(self, feature_flag_names):
7676
_LOGGER.debug('Error: ', exc_info=True)
7777
return None
7878

79+
def get_feature_flags_by_sets(self, flag_sets):
80+
"""
81+
Retrieve feature flags by flag set.
82+
83+
:param flag_sets: List of flag sets to fetch.
84+
:type flag_sets: list(str)
85+
86+
:return: Feature flag names that are tagged with the flag set
87+
:rtype: listt(str)
88+
"""
89+
try:
90+
sets_to_fetch = get_valid_flag_sets(flag_sets, self.flag_set_filter)
91+
if sets_to_fetch == []:
92+
return []
93+
94+
keys = [self._flag_set_prefix.format(flag_set=flag_set) for flag_set in sets_to_fetch]
95+
result_sets = []
96+
[result_sets.append(set(key)) for key in self._pluggable_adapter.get_many(keys)]
97+
return list(combine_valid_flag_sets(result_sets))
98+
except Exception:
99+
_LOGGER.error('Error fetching feature flag from storage')
100+
_LOGGER.debug('Error: ', exc_info=True)
101+
return None
102+
79103
def get_feature_flags_by_sets(self, flag_sets):
80104
"""
81105
Retrieve feature flags by flag set.

splitio/storage/redis.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
from splitio.storage.adapters.cache_trait import decorate as add_cache, DEFAULT_MAX_AGE
1414
from splitio.util.storage_helper import get_valid_flag_sets, combine_valid_flag_sets
1515

16-
1716
_LOGGER = logging.getLogger(__name__)
1817
MAX_TAGS = 10
1918

splitio/util/storage_helper.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,40 @@ def combine_valid_flag_sets(result_sets):
6969
if isinstance(result_set, set) and len(result_set) > 0:
7070
to_return.update(result_set)
7171
return to_return
72+
73+
def _check_flag_sets(feature_flag_storage, feature_flag):
74+
"""
75+
Check each flag set in given array, return it if exist in a given config flag set array, if config array is empty return all
76+
77+
:param flag_sets: Flag sets array
78+
:type flag_sets: list(str)
79+
:param config_flag_sets: Config flag sets array
80+
:type config_flag_sets: list(str)
81+
82+
:return: array of flag sets
83+
:rtype: list(str)
84+
"""
85+
sets_to_fetch = []
86+
for flag_set in flag_sets:
87+
if not flag_set_filter.set_exist(flag_set) and flag_set_filter.should_filter:
88+
_LOGGER.warning("Flag set %s is not part of the configured flag set list, ignoring the request." % (flag_set))
89+
continue
90+
sets_to_fetch.append(flag_set)
91+
92+
return sets_to_fetch
93+
94+
def combine_valid_flag_sets(result_sets):
95+
"""
96+
Check each flag set in given array of sets, combine all flag sets in one unique set
97+
98+
:param result_sets: Flag sets set
99+
:type flag_sets: list(set)
100+
101+
:return: flag sets set
102+
:rtype: set
103+
"""
104+
to_return = set()
105+
for result_set in result_sets:
106+
if isinstance(result_set, set) and len(result_set) > 0:
107+
to_return.update(result_set)
108+
return to_return

tests/client/test_config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,4 @@ def test_sanitize(self):
6969
processed = config.sanitize('some', configs)
7070

7171
assert processed['redisLocalCacheEnabled'] # check default is True
72-
assert processed['flagSetsFilter'] is None
72+
assert processed['flagSetsFilter'] is None

0 commit comments

Comments
 (0)