Skip to content

Commit 1ee5866

Browse files
authored
Merge pull request #456 from splitio/flagsets-client-client
Added flagset support in client, updated client.config and models.tel…
2 parents 874f205 + 6743839 commit 1ee5866

File tree

6 files changed

+624
-25
lines changed

6 files changed

+624
-25
lines changed

splitio/client/client.py

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from splitio.models.impressions import Impression, Label
77
from splitio.models.events import Event, EventWrapper
88
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
1010
from splitio.util.time import get_current_epoch_time_ms, utctime_ms
1111

1212
_LOGGER = logging.getLogger(__name__)
@@ -309,6 +309,132 @@ def get_treatments(self, key, feature_flags, attributes=None):
309309
MethodExceptionsAndLatencies.TREATMENTS)
310310
return {feature_flag: result[0] for (feature_flag, result) in with_config.items()}
311311

312+
def get_treatments_by_flag_set(self, key, flag_set, attributes=None):
313+
"""
314+
Get treatments for feature flags that contain given flag set.
315+
316+
This method never raises an exception. If there's a problem, the appropriate log message
317+
will be generated and the method will return the CONTROL treatment.
318+
319+
:param key: The key for which to get the treatment
320+
:type key: str
321+
:param flag_set: flag set
322+
:type flag_sets: str
323+
:param attributes: An optional dictionary of attributes
324+
:type attributes: dict
325+
326+
:return: Dictionary with the result of all the feature flags provided
327+
:rtype: dict
328+
"""
329+
return self._get_treatments_by_flag_sets( key, [flag_set], MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SET, attributes)
330+
331+
def get_treatments_by_flag_sets(self, key, flag_sets, attributes=None):
332+
"""
333+
Get treatments for feature flags that contain given flag sets.
334+
335+
This method never raises an exception. If there's a problem, the appropriate log message
336+
will be generated and the method will return the CONTROL treatment.
337+
338+
:param key: The key for which to get the treatment
339+
:type key: str
340+
:param flag_sets: list of flag sets
341+
:type flag_sets: list
342+
:param attributes: An optional dictionary of attributes
343+
:type attributes: dict
344+
345+
:return: Dictionary with the result of all the feature flags provided
346+
:rtype: dict
347+
"""
348+
return self._get_treatments_by_flag_sets( key, flag_sets, MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SETS, attributes)
349+
350+
def get_treatments_with_config_by_flag_set(self, key, flag_set, attributes=None):
351+
"""
352+
Get treatments for feature flags that contain given flag set.
353+
354+
This method never raises an exception. If there's a problem, the appropriate log message
355+
will be generated and the method will return the CONTROL treatment.
356+
357+
:param key: The key for which to get the treatment
358+
:type key: str
359+
:param flag_set: flag set
360+
:type flag_sets: str
361+
:param attributes: An optional dictionary of attributes
362+
:type attributes: dict
363+
364+
:return: Dictionary with the result of all the feature flags provided
365+
:rtype: dict
366+
"""
367+
return self._get_treatments_by_flag_sets( key, [flag_set], MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SET, attributes)
368+
369+
def get_treatments_with_config_by_flag_sets(self, key, flag_sets, attributes=None):
370+
"""
371+
Get treatments for feature flags that contain given flag set.
372+
373+
This method never raises an exception. If there's a problem, the appropriate log message
374+
will be generated and the method will return the CONTROL treatment.
375+
376+
:param key: The key for which to get the treatment
377+
:type key: str
378+
:param flag_set: flag set
379+
:type flag_sets: str
380+
:param attributes: An optional dictionary of attributes
381+
:type attributes: dict
382+
383+
:return: Dictionary with the result of all the feature flags provided
384+
:rtype: dict
385+
"""
386+
return self._get_treatments_by_flag_sets( key, flag_sets, MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SETS, attributes)
387+
388+
def _get_treatments_by_flag_sets(self, key, flag_sets, method, attributes=None):
389+
"""
390+
Get treatments for feature flags that contain given flag sets.
391+
392+
This method never raises an exception. If there's a problem, the appropriate log message
393+
will be generated and the method will return the CONTROL treatment.
394+
395+
:param key: The key for which to get the treatment
396+
:type key: str
397+
:param flag_sets: list of flag sets
398+
:type flag_sets: list
399+
:param method: Treatment by flag set method flavor
400+
:type method: splitio.models.telemetry.MethodExceptionsAndLatencies
401+
:param attributes: An optional dictionary of attributes
402+
:type attributes: dict
403+
404+
:return: Dictionary with the result of all the feature flags provided
405+
:rtype: dict
406+
"""
407+
feature_flags_names = self._get_feature_flag_names_by_flag_sets(flag_sets)
408+
if feature_flags_names == []:
409+
_LOGGER.warning("No valid Flag set or no feature flags found for evaluating treatments")
410+
return {}
411+
412+
if 'config' in method.value:
413+
return self._make_evaluations(key, feature_flags_names, attributes, method.value,
414+
method)
415+
416+
with_config = self._make_evaluations(key, feature_flags_names, attributes, method.value,
417+
method)
418+
return {feature_flag: result[0] for (feature_flag, result) in with_config.items()}
419+
420+
421+
def _get_feature_flag_names_by_flag_sets(self, flag_sets):
422+
"""
423+
Sanitize given flag sets and return list of feature flag names associated with them
424+
425+
:param flag_sets: list of flag sets
426+
:type flag_sets: list
427+
428+
:return: list of feature flag names
429+
:rtype: list
430+
"""
431+
sanitized_flag_sets = config.sanitize_flag_sets(flag_sets)
432+
feature_flags = []
433+
[feature_flags.extend(self._split_storage.get_feature_flags_by_set(flag_set)) for flag_set in sanitized_flag_sets]
434+
feature_flags_names = []
435+
[feature_flags_names.append(feature_flag) for feature_flag in feature_flags]
436+
return feature_flags_names
437+
312438
def _build_impression( # pylint: disable=too-many-arguments
313439
self,
314440
matching_key,

splitio/client/config.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ def _sanitize_impressions_mode(storage_type, mode, refresh_rate=None):
120120
return mode, refresh_rate
121121

122122

123-
def _sanitize_flag_sets(flag_sets):
123+
def sanitize_flag_sets(flag_sets):
124124
"""
125125
Check supplied flag sets list
126126
@@ -130,8 +130,15 @@ def _sanitize_flag_sets(flag_sets):
130130
:returns: Sanitized and sorted flag sets
131131
:rtype: list[str]
132132
"""
133+
if not isinstance(flag_sets, list):
134+
_LOGGER.warning("SDK config: FlagSets config parameters type should be list object, parameter is discarded")
135+
return []
136+
133137
sanitized_flag_sets = set()
134138
for flag_set in flag_sets:
139+
if not isinstance(flag_set, str):
140+
_LOGGER.warning("SDK config: Flag Set name %s should be str object, this flag set is discarded" % (flag_set))
141+
continue
135142
if flag_set != flag_set.strip():
136143
_LOGGER.warning("SDK config: Flag Set name %s has extra whitespace, trimming" % (flag_set))
137144
flag_set = flag_set.strip()

splitio/models/telemetry.py

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ class MethodExceptionsAndLatencies(Enum):
8282
TREATMENTS = 'treatments'
8383
TREATMENT_WITH_CONFIG = 'treatment_with_config'
8484
TREATMENTS_WITH_CONFIG = 'treatments_with_config'
85+
TREATMENTS_BY_FLAG_SET = 'treatments_by_flag_set'
86+
TREATMENTS_BY_FLAG_SETS = 'treatments_by_flag_sets'
87+
TREATMENTS_WITH_CONFIG_BY_FLAG_SET = 'treatments_with_config_by_flag_set'
88+
TREATMENTS_WITH_CONFIG_BY_FLAG_SETS = 'treatments_with_config_by_flag_sets'
8589
TRACK = 'track'
8690

8791
class LastSynchronizationConstants(Enum):
@@ -166,6 +170,10 @@ def _reset_all(self):
166170
self._treatments = [0] * MAX_LATENCY_BUCKET_COUNT
167171
self._treatment_with_config = [0] * MAX_LATENCY_BUCKET_COUNT
168172
self._treatments_with_config = [0] * MAX_LATENCY_BUCKET_COUNT
173+
self._treatments_by_flag_set = [0] * MAX_LATENCY_BUCKET_COUNT
174+
self._treatments_by_flag_sets = [0] * MAX_LATENCY_BUCKET_COUNT
175+
self._treatments_with_config_by_flag_set = [0] * MAX_LATENCY_BUCKET_COUNT
176+
self._treatments_with_config_by_flag_sets = [0] * MAX_LATENCY_BUCKET_COUNT
169177
self._track = [0] * MAX_LATENCY_BUCKET_COUNT
170178

171179
def add_latency(self, method, latency):
@@ -187,6 +195,14 @@ def add_latency(self, method, latency):
187195
self._treatment_with_config[latency_bucket] += 1
188196
elif method == MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG:
189197
self._treatments_with_config[latency_bucket] += 1
198+
elif method == MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SET:
199+
self._treatments_by_flag_set[latency_bucket] += 1
200+
elif method == MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SETS:
201+
self._treatments_by_flag_sets[latency_bucket] += 1
202+
elif method == MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SET:
203+
self._treatments_with_config_by_flag_set[latency_bucket] += 1
204+
elif method == MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SETS:
205+
self._treatments_with_config_by_flag_sets[latency_bucket] += 1
190206
elif method == MethodExceptionsAndLatencies.TRACK:
191207
self._track[latency_bucket] += 1
192208
else:
@@ -200,10 +216,18 @@ def pop_all(self):
200216
:rtype: dict
201217
"""
202218
with self._lock:
203-
latencies = {MethodExceptionsAndLatencies.METHOD_LATENCIES.value: {MethodExceptionsAndLatencies.TREATMENT.value: self._treatment, MethodExceptionsAndLatencies.TREATMENTS.value: self._treatments,
204-
MethodExceptionsAndLatencies.TREATMENT_WITH_CONFIG.value: self._treatment_with_config, MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG.value: self._treatments_with_config,
205-
MethodExceptionsAndLatencies.TRACK.value: self._track}
219+
latencies = {MethodExceptionsAndLatencies.METHOD_LATENCIES.value: {
220+
MethodExceptionsAndLatencies.TREATMENT.value: self._treatment,
221+
MethodExceptionsAndLatencies.TREATMENTS.value: self._treatments,
222+
MethodExceptionsAndLatencies.TREATMENT_WITH_CONFIG.value: self._treatment_with_config,
223+
MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG.value: self._treatments_with_config,
224+
MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SET.value: self._treatments_by_flag_set,
225+
MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SETS.value: self._treatments_by_flag_sets,
226+
MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SET.value: self._treatments_with_config_by_flag_set,
227+
MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SETS.value: self._treatments_with_config_by_flag_sets,
228+
MethodExceptionsAndLatencies.TRACK.value: self._track
206229
}
230+
}
207231
self._reset_all()
208232
return latencies
209233

@@ -288,6 +312,10 @@ def _reset_all(self):
288312
self._treatments = 0
289313
self._treatment_with_config = 0
290314
self._treatments_with_config = 0
315+
self._treatments_by_flag_set = 0
316+
self._treatments_by_flag_sets = 0
317+
self._treatments_with_config_by_flag_set = 0
318+
self._treatments_with_config_by_flag_sets = 0
291319
self._track = 0
292320

293321
def add_exception(self, method):
@@ -306,6 +334,14 @@ def add_exception(self, method):
306334
self._treatment_with_config += 1
307335
elif method == MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG:
308336
self._treatments_with_config += 1
337+
elif method == MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SET:
338+
self._treatments_by_flag_set += 1
339+
elif method == MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SETS:
340+
self._treatments_by_flag_sets += 1
341+
elif method == MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SET:
342+
self._treatments_with_config_by_flag_set += 1
343+
elif method == MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SETS:
344+
self._treatments_with_config_by_flag_sets += 1
309345
elif method == MethodExceptionsAndLatencies.TRACK:
310346
self._track += 1
311347
else:
@@ -319,10 +355,18 @@ def pop_all(self):
319355
:rtype: dict
320356
"""
321357
with self._lock:
322-
exceptions = {MethodExceptionsAndLatencies.METHOD_EXCEPTIONS.value: {MethodExceptionsAndLatencies.TREATMENT.value: self._treatment, MethodExceptionsAndLatencies.TREATMENTS.value: self._treatments,
323-
MethodExceptionsAndLatencies.TREATMENT_WITH_CONFIG.value: self._treatment_with_config, MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG.value: self._treatments_with_config,
324-
MethodExceptionsAndLatencies.TRACK.value: self._track}
358+
exceptions = {MethodExceptionsAndLatencies.METHOD_EXCEPTIONS.value: {
359+
MethodExceptionsAndLatencies.TREATMENT.value: self._treatment,
360+
MethodExceptionsAndLatencies.TREATMENTS.value: self._treatments,
361+
MethodExceptionsAndLatencies.TREATMENT_WITH_CONFIG.value: self._treatment_with_config,
362+
MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG.value: self._treatments_with_config,
363+
MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SET.value: self._treatments_by_flag_set,
364+
MethodExceptionsAndLatencies.TREATMENTS_BY_FLAG_SETS.value: self._treatments_by_flag_sets,
365+
MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SET.value: self._treatments_with_config_by_flag_set,
366+
MethodExceptionsAndLatencies.TREATMENTS_WITH_CONFIG_BY_FLAG_SETS.value: self._treatments_with_config_by_flag_sets,
367+
MethodExceptionsAndLatencies.TRACK.value: self._track
325368
}
369+
}
326370
self._reset_all()
327371
return exceptions
328372

0 commit comments

Comments
 (0)