|
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 @@ def destroyed(self): |
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 @@ def _make_evaluation(self, key, feature_flag, attributes, method_name, metric_na |
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 @@ def _make_evaluations(self, key, feature_flags, attributes, method_name, metric_ |
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 @@ def _make_evaluations(self, key, feature_flags, attributes, method_name, metric_ |
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 @@ def get_treatments(self, key, feature_flags, attributes=None): |
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, |
|
0 commit comments