Skip to content

Commit 434dd75

Browse files
authored
Merge branch 'master' into rashid/add-revision
2 parents 662f4cb + 4fbd03b commit 434dd75

File tree

4 files changed

+94
-43
lines changed

4 files changed

+94
-43
lines changed

optimizely/optimizely.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -230,11 +230,7 @@ def _get_feature_variable_for_type(self, feature_key, variable_key, variable_typ
230230
decision = self.decision_service.get_variation_for_feature(feature_flag, user_id, attributes)
231231
if decision.variation:
232232
variable_value = self.config.get_variable_value_for_variation(variable, decision.variation)
233-
self.logger.log(
234-
enums.LogLevels.INFO,
235-
'Value for variable "%s" of feature flag "%s" is %s for user "%s".' % (
236-
variable_key, feature_key, variable_value, user_id
237-
))
233+
238234
else:
239235
variable_value = variable.defaultValue
240236
self.logger.log(
@@ -413,7 +409,7 @@ def get_enabled_features(self, user_id, attributes=None):
413409
attributes: Dict representing user attributes.
414410
415411
Returns:
416-
A sorted list of the keys of the features that are enabled for the user.
412+
A list of the keys of the features that are enabled for the user.
417413
"""
418414

419415
enabled_features = []
@@ -425,7 +421,6 @@ def get_enabled_features(self, user_id, attributes=None):
425421
if self.is_feature_enabled(feature.key, user_id, attributes):
426422
enabled_features.append(feature.key)
427423

428-
enabled_features.sort()
429424
return enabled_features
430425

431426
def get_feature_variable_boolean(self, feature_key, variable_key, user_id, attributes=None):

optimizely/project_config.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2016-2017, Optimizely
1+
# Copyright 2016-2018, Optimizely
22
# Licensed under the Apache License, Version 2.0 (the "License");
33
# you may not use this file except in compliance with the License.
44
# You may obtain a copy of the License at
@@ -90,10 +90,9 @@ def __init__(self, datafile, logger, error_handler):
9090
self.variation_id_map[experiment.key] = {}
9191
for variation in self.variation_key_map.get(experiment.key).values():
9292
self.variation_id_map[experiment.key][variation.id] = variation
93-
if variation.variables:
94-
self.variation_variable_usage_map[variation.id] = self._generate_key_map(
95-
variation.variables, 'id', entities.Variation.VariableUsage
96-
)
93+
self.variation_variable_usage_map[variation.id] = self._generate_key_map(
94+
variation.variables, 'id', entities.Variation.VariableUsage
95+
)
9796

9897
self.feature_key_map = self._generate_key_map(self.feature_flags, 'key', entities.FeatureFlag)
9998
for feature in self.feature_key_map.values():
@@ -439,10 +438,27 @@ def get_variable_value_for_variation(self, variable, variation):
439438
variable_usages = self.variation_variable_usage_map[variation.id]
440439

441440
# Find usage in given variation
442-
variable_usage = variable_usages.get(variable.id)
441+
variable_usage = None
442+
if variable_usages:
443+
variable_usage = variable_usages.get(variable.id)
444+
445+
if variable_usage:
446+
variable_value = variable_usage.value
447+
self.logger.log(
448+
enums.LogLevels.INFO,
449+
'Value for variable "%s" for variation "%s" is "%s".' % (
450+
variable.key, variation.key, variable_value
451+
))
443452

444-
# Return default value in case there is no variable usage for the variable.
445-
return variable_usage.value if variable_usage else variable.defaultValue
453+
else:
454+
variable_value = variable.defaultValue
455+
self.logger.log(
456+
enums.LogLevels.INFO,
457+
'Variable "%s" is not used in variation "%s". Assinging default value "%s".' % (
458+
variable.key, variation.key, variable_value
459+
))
460+
461+
return variable_value
446462

447463
def get_variable_for_feature(self, feature_key, variable_key):
448464
""" Get the variable with the given variable key for the given feature.

tests/test_config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,8 @@ def test_init__with_v4_datafile(self):
616616
'129': entities.Variation.VariableUsage('129', '112'),
617617
'130': entities.Variation.VariableUsage('130', '1.211')
618618
},
619+
'28905': {},
620+
'28906': {},
619621
'211113': {
620622
'131': entities.Variation.VariableUsage('131', '15')
621623
}

tests/test_optimizely.py

Lines changed: 66 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,29 +1398,6 @@ def side_effect(*args, **kwargs):
13981398
mock_is_feature_enabled.assert_any_call('test_feature_in_group', 'user_1', None)
13991399
mock_is_feature_enabled.assert_any_call('test_feature_in_experiment_and_rollout', 'user_1', None)
14001400

1401-
def test_get_enabled_features_returns_a_sorted_list(self):
1402-
""" Test that get_enabled_features returns a sorted list of enabled feature keys. """
1403-
1404-
opt_obj = optimizely.Optimizely(json.dumps(self.config_dict_with_features))
1405-
1406-
with mock.patch('optimizely.optimizely.Optimizely.is_feature_enabled',
1407-
return_value=True) as mock_is_feature_enabled:
1408-
received_features = opt_obj.get_enabled_features('user_1')
1409-
1410-
mock_is_feature_enabled.assert_any_call('test_feature_in_experiment', 'user_1', None)
1411-
mock_is_feature_enabled.assert_any_call('test_feature_in_rollout', 'user_1', None)
1412-
mock_is_feature_enabled.assert_any_call('test_feature_in_group', 'user_1', None)
1413-
mock_is_feature_enabled.assert_any_call('test_feature_in_experiment_and_rollout', 'user_1', None)
1414-
1415-
expected_sorted_features = [
1416-
'test_feature_in_experiment',
1417-
'test_feature_in_experiment_and_rollout',
1418-
'test_feature_in_group',
1419-
'test_feature_in_rollout'
1420-
]
1421-
1422-
self.assertEqual(expected_sorted_features, received_features)
1423-
14241401
def test_get_enabled_features__invalid_object(self):
14251402
""" Test that get_enabled_features returns empty list if Optimizely object is not valid. """
14261403

@@ -1447,7 +1424,7 @@ def test_get_feature_variable_boolean(self):
14471424

14481425
mock_logger.assert_called_once_with(
14491426
enums.LogLevels.INFO,
1450-
'Value for variable "is_working" of feature flag "test_feature_in_experiment" is true for user "test_user".'
1427+
'Value for variable "is_working" for variation "variation" is "true".'
14511428
)
14521429

14531430
def test_get_feature_variable_double(self):
@@ -1465,7 +1442,7 @@ def test_get_feature_variable_double(self):
14651442

14661443
mock_logger.assert_called_once_with(
14671444
enums.LogLevels.INFO,
1468-
'Value for variable "cost" of feature flag "test_feature_in_experiment" is 10.02 for user "test_user".'
1445+
'Value for variable "cost" for variation "variation" is "10.02".'
14691446
)
14701447

14711448
def test_get_feature_variable_integer(self):
@@ -1483,7 +1460,7 @@ def test_get_feature_variable_integer(self):
14831460

14841461
mock_logger.assert_called_once_with(
14851462
enums.LogLevels.INFO,
1486-
'Value for variable "count" of feature flag "test_feature_in_experiment" is 4243 for user "test_user".'
1463+
'Value for variable "count" for variation "variation" is "4243".'
14871464
)
14881465

14891466
def test_get_feature_variable_string(self):
@@ -1502,10 +1479,71 @@ def test_get_feature_variable_string(self):
15021479

15031480
mock_logger.assert_called_once_with(
15041481
enums.LogLevels.INFO,
1505-
'Value for variable "environment" of feature flag "test_feature_in_experiment" is staging for user "test_user".'
1482+
'Value for variable "environment" for variation "variation" is "staging".'
1483+
)
1484+
1485+
def test_get_feature_variable__returns_default_value_if_variable_usage_not_in_variation(self):
1486+
""" Test that get_feature_variable_* returns default value if variable usage not present in variation. """
1487+
1488+
opt_obj = optimizely.Optimizely(json.dumps(self.config_dict_with_features))
1489+
mock_experiment = opt_obj.config.get_experiment_from_key('test_experiment')
1490+
mock_variation = opt_obj.config.get_variation_from_id('test_experiment', '111129')
1491+
1492+
# Empty variable usage map for the mocked variation
1493+
opt_obj.config.variation_variable_usage_map['111129'] = None
1494+
1495+
# Boolean
1496+
with mock.patch('optimizely.decision_service.DecisionService.get_variation_for_feature',
1497+
return_value=decision_service.Decision(mock_experiment, mock_variation,
1498+
decision_service.DECISION_SOURCE_EXPERIMENT)), \
1499+
mock.patch('optimizely.logger.NoOpLogger.log') as mock_logger:
1500+
self.assertTrue(opt_obj.get_feature_variable_boolean('test_feature_in_experiment', 'is_working', 'test_user'))
1501+
1502+
mock_logger.assert_called_once_with(
1503+
enums.LogLevels.INFO,
1504+
'Variable "is_working" is not used in variation "variation". Assinging default value "true".'
1505+
)
1506+
1507+
# Double
1508+
with mock.patch('optimizely.decision_service.DecisionService.get_variation_for_feature',
1509+
return_value=decision_service.Decision(mock_experiment, mock_variation,
1510+
decision_service.DECISION_SOURCE_EXPERIMENT)), \
1511+
mock.patch('optimizely.logger.NoOpLogger.log') as mock_logger:
1512+
self.assertEqual(10.99,
1513+
opt_obj.get_feature_variable_double('test_feature_in_experiment', 'cost', 'test_user'))
1514+
1515+
mock_logger.assert_called_once_with(
1516+
enums.LogLevels.INFO,
1517+
'Variable "cost" is not used in variation "variation". Assinging default value "10.99".'
1518+
)
1519+
1520+
# Integer
1521+
with mock.patch('optimizely.decision_service.DecisionService.get_variation_for_feature',
1522+
return_value=decision_service.Decision(mock_experiment, mock_variation,
1523+
decision_service.DECISION_SOURCE_EXPERIMENT)), \
1524+
mock.patch('optimizely.logger.NoOpLogger.log') as mock_logger:
1525+
self.assertEqual(999,
1526+
opt_obj.get_feature_variable_integer('test_feature_in_experiment', 'count', 'test_user'))
1527+
1528+
mock_logger.assert_called_once_with(
1529+
enums.LogLevels.INFO,
1530+
'Variable "count" is not used in variation "variation". Assinging default value "999".'
1531+
)
1532+
1533+
# String
1534+
with mock.patch('optimizely.decision_service.DecisionService.get_variation_for_feature',
1535+
return_value=decision_service.Decision(mock_experiment, mock_variation,
1536+
decision_service.DECISION_SOURCE_EXPERIMENT)), \
1537+
mock.patch('optimizely.logger.NoOpLogger.log') as mock_logger:
1538+
self.assertEqual('devel',
1539+
opt_obj.get_feature_variable_string('test_feature_in_experiment', 'environment', 'test_user'))
1540+
1541+
mock_logger.assert_called_once_with(
1542+
enums.LogLevels.INFO,
1543+
'Variable "environment" is not used in variation "variation". Assinging default value "devel".'
15061544
)
15071545

1508-
def test_get_feature_variable__returns_default_value(self):
1546+
def test_get_feature_variable__returns_default_value_if_no_variation(self):
15091547
""" Test that get_feature_variable_* returns default value if no variation. """
15101548

15111549
opt_obj = optimizely.Optimizely(json.dumps(self.config_dict_with_features))

0 commit comments

Comments
 (0)