Skip to content

Commit cdc2599

Browse files
rashidspaliabbasrizvi
authored andcommitted
feat(DecisionListener): Adds feature variables decision listener. (#158)
1 parent f140be1 commit cdc2599

File tree

2 files changed

+199
-0
lines changed

2 files changed

+199
-0
lines changed

lib/optimizely.rb

+22
Original file line numberDiff line numberDiff line change
@@ -480,16 +480,25 @@ def get_feature_variable_for_type(feature_flag_key, variable_key, variable_type,
480480
# Error message logged in ProjectConfig- get_feature_flag_from_key
481481
return nil if variable.nil?
482482

483+
feature_enabled = false
484+
483485
# Returns nil if type differs
484486
if variable['type'] != variable_type
485487
@logger.log(Logger::WARN,
486488
"Requested variable as type '#{variable_type}' but variable '#{variable_key}' is of type '#{variable['type']}'.")
487489
return nil
488490
else
491+
source_string = Optimizely::DecisionService::DECISION_SOURCE_ROLLOUT
489492
decision = @decision_service.get_variation_for_feature(feature_flag, user_id, attributes)
490493
variable_value = variable['defaultValue']
491494
if decision
492495
variation = decision['variation']
496+
if decision['source'] == Optimizely::DecisionService::DECISION_SOURCE_EXPERIMENT
497+
experiment_key = decision.experiment['key']
498+
variation_key = variation['key']
499+
source_string = Optimizely::DecisionService::DECISION_SOURCE_EXPERIMENT
500+
end
501+
feature_enabled = variation['featureEnabled']
493502
variation_variable_usages = @config.variation_id_to_variable_usage_map[variation['id']]
494503
variable_id = variable['id']
495504
if variation_variable_usages&.key?(variable_id)
@@ -508,6 +517,19 @@ def get_feature_variable_for_type(feature_flag_key, variable_key, variable_type,
508517

509518
variable_value = Helpers::VariableType.cast_value_to_type(variable_value, variable_type, @logger)
510519

520+
@notification_center.send_notifications(
521+
NotificationCenter::NOTIFICATION_TYPES[:DECISION],
522+
Helpers::Constants::DECISION_INFO_TYPES['FEATURE_VARIABLE'], user_id, (attributes || {}),
523+
feature_key: feature_flag_key,
524+
feature_enabled: feature_enabled,
525+
variable_key: variable_key,
526+
variable_type: variable_type,
527+
variable_value: variable_value,
528+
source: source_string.upcase,
529+
source_experiment_key: experiment_key,
530+
source_variation_key: variation_key
531+
)
532+
511533
variable_value
512534
end
513535

spec/project_spec.rb

+177
Original file line numberDiff line numberDiff line change
@@ -2031,6 +2031,183 @@ class InvalidErrorHandler; end
20312031
end
20322032
end
20332033

2034+
describe '#get_feature_variable_for_type listener' do
2035+
user_id = 'test_user'
2036+
user_attributes = {}
2037+
2038+
it 'should call decision listener with correct variable type and value, when user in experiment and feature is not enabled' do
2039+
integer_feature = project_instance.config.feature_flag_key_map['integer_single_variable_feature']
2040+
experiment_to_return = project_instance.config.experiment_id_map[integer_feature['experimentIds'][0]]
2041+
variation_to_return = experiment_to_return['variations'][0]
2042+
variation_to_return['featureEnabled'] = false
2043+
decision_to_return = Optimizely::DecisionService::Decision.new(
2044+
experiment_to_return,
2045+
variation_to_return,
2046+
Optimizely::DecisionService::DECISION_SOURCE_EXPERIMENT
2047+
)
2048+
2049+
allow(project_instance.decision_service).to receive(:get_variation_for_feature).and_return(decision_to_return)
2050+
2051+
# DECISION listener called when the user is in experiment with variation feature off.
2052+
expect(project_instance.notification_center).to receive(:send_notifications).once.with(
2053+
Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION],
2054+
'feature_variable', 'test_user', {},
2055+
feature_key: 'integer_single_variable_feature',
2056+
feature_enabled: false,
2057+
variable_key: 'integer_variable',
2058+
variable_type: 'integer',
2059+
variable_value: 42,
2060+
source: 'EXPERIMENT',
2061+
source_experiment_key: 'test_experiment_integer_feature',
2062+
source_variation_key: 'control'
2063+
)
2064+
2065+
expect(project_instance.send(
2066+
:get_feature_variable_for_type,
2067+
'integer_single_variable_feature',
2068+
'integer_variable',
2069+
'integer',
2070+
user_id,
2071+
nil
2072+
)).to eq(42)
2073+
end
2074+
2075+
it 'should call decision listener with correct variable type and value, when user in experiment and feature is enabled' do
2076+
integer_feature = project_instance.config.feature_flag_key_map['integer_single_variable_feature']
2077+
experiment_to_return = project_instance.config.experiment_id_map[integer_feature['experimentIds'][0]]
2078+
variation_to_return = experiment_to_return['variations'][0]
2079+
variation_to_return['featureEnabled'] = true
2080+
decision_to_return = Optimizely::DecisionService::Decision.new(
2081+
experiment_to_return,
2082+
variation_to_return,
2083+
Optimizely::DecisionService::DECISION_SOURCE_EXPERIMENT
2084+
)
2085+
2086+
allow(project_instance.decision_service).to receive(:get_variation_for_feature).and_return(decision_to_return)
2087+
2088+
# DECISION listener called when the user is in experiment with variation feature on.
2089+
expect(project_instance.notification_center).to receive(:send_notifications).once.with(
2090+
Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION],
2091+
'feature_variable', 'test_user', {'browser_type' => 'firefox'},
2092+
feature_key: 'integer_single_variable_feature',
2093+
feature_enabled: true,
2094+
variable_key: 'integer_variable',
2095+
variable_type: 'integer',
2096+
variable_value: 42,
2097+
source: 'EXPERIMENT',
2098+
source_experiment_key: 'test_experiment_integer_feature',
2099+
source_variation_key: 'control'
2100+
)
2101+
2102+
expect(project_instance.send(
2103+
:get_feature_variable_for_type,
2104+
'integer_single_variable_feature',
2105+
'integer_variable',
2106+
'integer',
2107+
user_id,
2108+
'browser_type' => 'firefox'
2109+
)).to eq(42)
2110+
end
2111+
2112+
it 'should call decision listener with correct variable type and value, when user in rollout and feature is enabled' do
2113+
experiment_to_return = config_body['rollouts'][0]['experiments'][0]
2114+
2115+
variation_to_return = experiment_to_return['variations'][0]
2116+
decision_to_return = Optimizely::DecisionService::Decision.new(
2117+
experiment_to_return,
2118+
variation_to_return,
2119+
Optimizely::DecisionService::DECISION_SOURCE_ROLLOUT
2120+
)
2121+
2122+
allow(project_instance.decision_service).to receive(:get_variation_for_feature).and_return(decision_to_return)
2123+
2124+
# DECISION listener called when the user is in rollout with variation feature on.
2125+
expect(variation_to_return['featureEnabled']).to be true
2126+
expect(project_instance.notification_center).to receive(:send_notifications).once.with(
2127+
Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION],
2128+
'feature_variable', 'test_user', {},
2129+
feature_key: 'boolean_single_variable_feature',
2130+
feature_enabled: true,
2131+
variable_key: 'boolean_variable',
2132+
variable_type: 'boolean',
2133+
variable_value: true,
2134+
source: 'ROLLOUT',
2135+
source_experiment_key: nil,
2136+
source_variation_key: nil
2137+
)
2138+
2139+
expect(project_instance.send(
2140+
:get_feature_variable_for_type,
2141+
'boolean_single_variable_feature',
2142+
'boolean_variable',
2143+
'boolean',
2144+
user_id,
2145+
user_attributes
2146+
)).to eq(true)
2147+
end
2148+
2149+
it 'should call listener with correct variable type and value, when user in rollout and feature is not enabled' do
2150+
experiment_to_return = config_body['rollouts'][0]['experiments'][1]
2151+
variation_to_return = experiment_to_return['variations'][0]
2152+
decision_to_return = Optimizely::DecisionService::Decision.new(
2153+
experiment_to_return,
2154+
variation_to_return,
2155+
Optimizely::DecisionService::DECISION_SOURCE_ROLLOUT
2156+
)
2157+
allow(project_instance.decision_service).to receive(:get_variation_for_feature).and_return(decision_to_return)
2158+
2159+
# DECISION listener called when the user is in rollout with variation feature on.
2160+
expect(variation_to_return['featureEnabled']).to be false
2161+
expect(project_instance.notification_center).to receive(:send_notifications).once.with(
2162+
Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION],
2163+
'feature_variable', 'test_user', {},
2164+
feature_key: 'boolean_single_variable_feature',
2165+
feature_enabled: false,
2166+
variable_key: 'boolean_variable',
2167+
variable_type: 'boolean',
2168+
variable_value: false,
2169+
source: 'ROLLOUT',
2170+
source_experiment_key: nil,
2171+
source_variation_key: nil
2172+
)
2173+
2174+
expect(project_instance.send(
2175+
:get_feature_variable_for_type,
2176+
'boolean_single_variable_feature',
2177+
'boolean_variable',
2178+
'boolean',
2179+
user_id,
2180+
user_attributes
2181+
)).to eq(false)
2182+
end
2183+
2184+
it 'should call listener with correct variable type and value, when user neither in experiment nor in rollout' do
2185+
allow(project_instance.decision_service).to receive(:get_variation_for_feature).and_return(nil)
2186+
2187+
expect(project_instance.notification_center).to receive(:send_notifications).once.with(
2188+
Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION],
2189+
'feature_variable', 'test_user', {},
2190+
feature_key: 'integer_single_variable_feature',
2191+
feature_enabled: false,
2192+
variable_key: 'integer_variable',
2193+
variable_type: 'integer',
2194+
variable_value: 7,
2195+
source: 'ROLLOUT',
2196+
source_experiment_key: nil,
2197+
source_variation_key: nil
2198+
)
2199+
2200+
expect(project_instance.send(
2201+
:get_feature_variable_for_type,
2202+
'integer_single_variable_feature',
2203+
'integer_variable',
2204+
'integer',
2205+
user_id,
2206+
user_attributes
2207+
)).to eq(7)
2208+
end
2209+
end
2210+
20342211
describe 'when forced variation is used' do
20352212
# setForcedVariation on a paused experiment and then call getVariation.
20362213
it 'should return null when getVariation is called on a paused experiment after setForcedVariation' do

0 commit comments

Comments
 (0)