Skip to content

Commit eb65834

Browse files
Merge branch 'master' into rashid/feature-variables-decision-listener
2 parents 1cc4dec + f140be1 commit eb65834

File tree

2 files changed

+273
-18
lines changed

2 files changed

+273
-18
lines changed

lib/optimizely.rb

+31-17
Original file line numberDiff line numberDiff line change
@@ -262,30 +262,44 @@ def is_feature_enabled(feature_flag_key, user_id, attributes = nil)
262262
end
263263

264264
decision = @decision_service.get_variation_for_feature(feature_flag, user_id, attributes)
265-
if decision.nil?
266-
@logger.log(Logger::INFO,
267-
"Feature '#{feature_flag_key}' is not enabled for user '#{user_id}'.")
268-
return false
269-
end
270265

271-
variation = decision['variation']
272-
if decision.source == Optimizely::DecisionService::DECISION_SOURCE_EXPERIMENT
273-
# Send event if Decision came from an experiment.
274-
send_impression(decision.experiment, variation['key'], user_id, attributes)
275-
else
276-
@logger.log(Logger::DEBUG,
277-
"The user '#{user_id}' is not being experimented on in feature '#{feature_flag_key}'.")
266+
feature_enabled = false
267+
source_string = Optimizely::DecisionService::DECISION_SOURCE_ROLLOUT
268+
if decision.is_a?(Optimizely::DecisionService::Decision)
269+
variation = decision['variation']
270+
feature_enabled = variation['featureEnabled']
271+
if decision.source == Optimizely::DecisionService::DECISION_SOURCE_EXPERIMENT
272+
source_string = Optimizely::DecisionService::DECISION_SOURCE_EXPERIMENT
273+
experiment_key = decision.experiment['key']
274+
variation_key = variation['key']
275+
# Send event if Decision came from an experiment.
276+
send_impression(decision.experiment, variation['key'], user_id, attributes)
277+
else
278+
@logger.log(Logger::DEBUG,
279+
"The user '#{user_id}' is not being experimented on in feature '#{feature_flag_key}'.")
280+
end
278281
end
279282

280-
if variation['featureEnabled'] == true
283+
@notification_center.send_notifications(
284+
NotificationCenter::NOTIFICATION_TYPES[:DECISION],
285+
Helpers::Constants::DECISION_INFO_TYPES['FEATURE'],
286+
user_id, (attributes || {}),
287+
feature_key: feature_flag_key,
288+
feature_enabled: feature_enabled,
289+
source: source_string.upcase,
290+
source_experiment_key: experiment_key,
291+
source_variation_key: variation_key
292+
)
293+
294+
if feature_enabled == true
281295
@logger.log(Logger::INFO,
282296
"Feature '#{feature_flag_key}' is enabled for user '#{user_id}'.")
283297
return true
284-
else
285-
@logger.log(Logger::INFO,
286-
"Feature '#{feature_flag_key}' is not enabled for user '#{user_id}'.")
287-
return false
288298
end
299+
300+
@logger.log(Logger::INFO,
301+
"Feature '#{feature_flag_key}' is not enabled for user '#{user_id}'.")
302+
false
289303
end
290304

291305
# Gets keys of all feature flags which are enabled for the user.

spec/project_spec.rb

+242-1
Original file line numberDiff line numberDiff line change
@@ -1333,7 +1333,9 @@ class InvalidErrorHandler; end
13331333
Optimizely::NotificationCenter::NOTIFICATION_TYPES[:ACTIVATE],
13341334
experiment_to_return, 'test_user', nil, variation_to_return,
13351335
instance_of(Optimizely::Event)
1336-
)
1336+
).ordered
1337+
1338+
expect(project_instance.notification_center).to receive(:send_notifications).ordered
13371339

13381340
allow(project_instance.decision_service).to receive(:get_variation_for_feature).and_return(decision_to_return)
13391341

@@ -1360,6 +1362,125 @@ class InvalidErrorHandler; end
13601362
expect(project_instance.event_dispatcher).to have_received(:dispatch_event).with(instance_of(Optimizely::Event)).once
13611363
expect(spy_logger).to have_received(:log).once.with(Logger::INFO, "Feature 'multi_variate_feature' is not enabled for user 'test_user'.")
13621364
end
1365+
1366+
describe '.decision listener' do
1367+
it 'should call decision listener when user is bucketed into a feature experiment with featureEnabled property is true' do
1368+
allow(project_instance.event_dispatcher).to receive(:dispatch_event).with(instance_of(Optimizely::Event))
1369+
experiment_to_return = config_body['experiments'][3]
1370+
variation_to_return = experiment_to_return['variations'][0]
1371+
decision_to_return = Optimizely::DecisionService::Decision.new(
1372+
experiment_to_return,
1373+
variation_to_return,
1374+
Optimizely::DecisionService::DECISION_SOURCE_EXPERIMENT
1375+
)
1376+
1377+
allow(project_instance.decision_service).to receive(:get_variation_for_feature).and_return(decision_to_return)
1378+
1379+
# Activate listener
1380+
expect(project_instance.notification_center).to receive(:send_notifications).ordered
1381+
1382+
# Decision listener called when the user is in experiment with variation feature on.
1383+
expect(variation_to_return['featureEnabled']).to be true
1384+
expect(project_instance.notification_center).to receive(:send_notifications).once.with(
1385+
Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION],
1386+
'feature', 'test_user', {},
1387+
feature_enabled: true,
1388+
feature_key: 'multi_variate_feature',
1389+
source: 'EXPERIMENT',
1390+
source_experiment_key: 'test_experiment_multivariate',
1391+
source_variation_key: 'Fred'
1392+
).ordered
1393+
1394+
project_instance.is_feature_enabled('multi_variate_feature', 'test_user')
1395+
end
1396+
1397+
it 'should call decision listener when user is bucketed into a feature experiment with featureEnabled property is false' do
1398+
allow(project_instance.event_dispatcher).to receive(:dispatch_event).with(instance_of(Optimizely::Event))
1399+
experiment_to_return = config_body['experiments'][3]
1400+
variation_to_return = experiment_to_return['variations'][1]
1401+
decision_to_return = Optimizely::DecisionService::Decision.new(
1402+
experiment_to_return,
1403+
variation_to_return,
1404+
Optimizely::DecisionService::DECISION_SOURCE_EXPERIMENT
1405+
)
1406+
1407+
allow(project_instance.decision_service).to receive(:get_variation_for_feature).and_return(decision_to_return)
1408+
1409+
expect(project_instance.notification_center).to receive(:send_notifications).ordered
1410+
1411+
# DECISION listener called when the user is in experiment with variation feature off.
1412+
expect(variation_to_return['featureEnabled']).to be false
1413+
expect(project_instance.notification_center).to receive(:send_notifications).once.with(
1414+
Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION],
1415+
'feature', 'test_user', {'browser_type' => 'chrome'},
1416+
feature_enabled: false,
1417+
feature_key: 'multi_variate_feature',
1418+
source: 'EXPERIMENT',
1419+
source_experiment_key: 'test_experiment_multivariate',
1420+
source_variation_key: 'Feorge'
1421+
)
1422+
1423+
project_instance.is_feature_enabled('multi_variate_feature', 'test_user', 'browser_type' => 'chrome')
1424+
end
1425+
1426+
it 'should call decision listener when user is bucketed into rollout with featureEnabled property is true' do
1427+
experiment_to_return = config_body['rollouts'][0]['experiments'][0]
1428+
variation_to_return = experiment_to_return['variations'][0]
1429+
decision_to_return = Optimizely::DecisionService::Decision.new(
1430+
experiment_to_return,
1431+
variation_to_return,
1432+
Optimizely::DecisionService::DECISION_SOURCE_ROLLOUT
1433+
)
1434+
allow(project_instance.decision_service).to receive(:get_variation_for_feature).and_return(decision_to_return)
1435+
1436+
# DECISION listener called when the user is in rollout with variation feature true.
1437+
expect(variation_to_return['featureEnabled']).to be true
1438+
expect(project_instance.notification_center).to receive(:send_notifications).once.with(
1439+
Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION],
1440+
'feature', 'test_user', {'browser_type' => 'firefox'},
1441+
feature_enabled: true,
1442+
feature_key: 'boolean_single_variable_feature',
1443+
source: 'ROLLOUT',
1444+
source_experiment_key: nil,
1445+
source_variation_key: nil
1446+
)
1447+
1448+
project_instance.is_feature_enabled('boolean_single_variable_feature', 'test_user', 'browser_type' => 'firefox')
1449+
end
1450+
1451+
it 'should call decision listener when user is bucketed into rollout with featureEnabled property is false' do
1452+
allow(project_instance.decision_service).to receive(:get_variation_for_feature).and_return(Optimizely::DecisionService::Decision)
1453+
1454+
# DECISION listener called when the user is in rollout with variation feature off.
1455+
expect(project_instance.notification_center).to receive(:send_notifications).once.with(
1456+
Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION],
1457+
'feature', 'test_user', {},
1458+
feature_enabled: false,
1459+
feature_key: 'boolean_single_variable_feature',
1460+
source: 'ROLLOUT',
1461+
source_experiment_key: nil,
1462+
source_variation_key: nil
1463+
)
1464+
1465+
project_instance.is_feature_enabled('boolean_single_variable_feature', 'test_user')
1466+
end
1467+
1468+
it 'call decision listener when the user is not bucketed into any experiment or rollout' do
1469+
allow(project_instance.decision_service).to receive(:get_variation_for_feature).and_return(nil)
1470+
1471+
expect(project_instance.notification_center).to receive(:send_notifications).once.with(
1472+
Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION],
1473+
'feature', 'test_user', {'browser_type' => 'firefox'},
1474+
feature_enabled: false,
1475+
feature_key: 'multi_variate_feature',
1476+
source: 'ROLLOUT',
1477+
source_experiment_key: nil,
1478+
source_variation_key: nil
1479+
)
1480+
1481+
project_instance.is_feature_enabled('multi_variate_feature', 'test_user', 'browser_type' => 'firefox')
1482+
end
1483+
end
13631484
end
13641485

13651486
describe '#get_enabled_features' do
@@ -1425,6 +1546,126 @@ class InvalidErrorHandler; end
14251546
# Checks prevented features should not return
14261547
expect(project_instance.get_enabled_features('test_user', 'browser_type' => 'chrome')).not_to include(*disabled_features)
14271548
end
1549+
1550+
describe '.decision listener' do
1551+
it 'should return enabled features and call decision listener for all features' do
1552+
allow(project_instance.event_dispatcher).to receive(:dispatch_event).with(instance_of(Optimizely::Event))
1553+
1554+
enabled_features = %w[boolean_feature integer_single_variable_feature]
1555+
1556+
experiment_to_return = config_body['experiments'][3]
1557+
rollout_to_return = config_body['rollouts'][0]['experiments'][0]
1558+
1559+
allow(project_instance.decision_service).to receive(:get_variation_for_feature).and_return(
1560+
Optimizely::DecisionService::Decision.new(
1561+
experiment_to_return,
1562+
experiment_to_return['variations'][0],
1563+
Optimizely::DecisionService::DECISION_SOURCE_EXPERIMENT
1564+
),
1565+
nil,
1566+
Optimizely::DecisionService::Decision.new(
1567+
rollout_to_return,
1568+
rollout_to_return['variations'][0],
1569+
Optimizely::DecisionService::DECISION_SOURCE_ROLLOUT
1570+
),
1571+
Optimizely::DecisionService::Decision.new(
1572+
experiment_to_return,
1573+
experiment_to_return['variations'][1],
1574+
Optimizely::DecisionService::DECISION_SOURCE_EXPERIMENT
1575+
),
1576+
nil,
1577+
nil,
1578+
Optimizely::DecisionService::Decision,
1579+
nil
1580+
)
1581+
1582+
expect(project_instance.notification_center).to receive(:send_notifications).twice.with(
1583+
Optimizely::NotificationCenter::NOTIFICATION_TYPES[:ACTIVATE], any_args
1584+
)
1585+
1586+
expect(project_instance.notification_center).to receive(:send_notifications).once.with(
1587+
Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION],
1588+
'feature', 'test_user', {'browser_type' => 'firefox'},
1589+
feature_enabled: true,
1590+
feature_key: 'boolean_feature',
1591+
source: 'EXPERIMENT',
1592+
source_experiment_key: 'test_experiment_multivariate',
1593+
source_variation_key: 'Fred'
1594+
).ordered
1595+
1596+
expect(project_instance.notification_center).to receive(:send_notifications).once.with(
1597+
Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION],
1598+
'feature', 'test_user', {'browser_type' => 'firefox'},
1599+
feature_enabled: false,
1600+
feature_key: 'double_single_variable_feature',
1601+
source: 'ROLLOUT',
1602+
source_experiment_key: nil,
1603+
source_variation_key: nil
1604+
).ordered
1605+
1606+
expect(project_instance.notification_center).to receive(:send_notifications).once.with(
1607+
Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION],
1608+
'feature', 'test_user', {'browser_type' => 'firefox'},
1609+
feature_enabled: true,
1610+
feature_key: 'integer_single_variable_feature',
1611+
source: 'ROLLOUT',
1612+
source_experiment_key: nil,
1613+
source_variation_key: nil
1614+
).ordered
1615+
1616+
expect(project_instance.notification_center).to receive(:send_notifications).once.with(
1617+
Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION],
1618+
'feature', 'test_user', {'browser_type' => 'firefox'},
1619+
feature_enabled: false,
1620+
feature_key: 'boolean_single_variable_feature',
1621+
source: 'EXPERIMENT',
1622+
source_experiment_key: 'test_experiment_multivariate',
1623+
source_variation_key: 'Feorge'
1624+
).ordered
1625+
1626+
expect(project_instance.notification_center).to receive(:send_notifications).once.with(
1627+
Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION],
1628+
'feature', 'test_user', {'browser_type' => 'firefox'},
1629+
feature_enabled: false,
1630+
feature_key: 'string_single_variable_feature',
1631+
source: 'ROLLOUT',
1632+
source_experiment_key: nil,
1633+
source_variation_key: nil
1634+
).ordered
1635+
1636+
expect(project_instance.notification_center).to receive(:send_notifications).once.with(
1637+
Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION],
1638+
'feature', 'test_user', {'browser_type' => 'firefox'},
1639+
feature_enabled: false,
1640+
feature_key: 'multi_variate_feature',
1641+
source: 'ROLLOUT',
1642+
source_experiment_key: nil,
1643+
source_variation_key: nil
1644+
).ordered
1645+
1646+
expect(project_instance.notification_center).to receive(:send_notifications).once.with(
1647+
Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION],
1648+
'feature', 'test_user', {'browser_type' => 'firefox'},
1649+
feature_enabled: false,
1650+
feature_key: 'mutex_group_feature',
1651+
source: 'ROLLOUT',
1652+
source_experiment_key: nil,
1653+
source_variation_key: nil
1654+
).ordered
1655+
1656+
expect(project_instance.notification_center).to receive(:send_notifications).once.with(
1657+
Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION],
1658+
'feature', 'test_user', {'browser_type' => 'firefox'},
1659+
feature_enabled: false,
1660+
feature_key: 'empty_feature',
1661+
source: 'ROLLOUT',
1662+
source_experiment_key: nil,
1663+
source_variation_key: nil
1664+
).ordered
1665+
1666+
expect(project_instance.get_enabled_features('test_user', 'browser_type' => 'firefox')).to eq(enabled_features)
1667+
end
1668+
end
14281669
end
14291670

14301671
describe '#get_feature_variable_string' do

0 commit comments

Comments
 (0)