diff --git a/lib/optimizely.rb b/lib/optimizely.rb index 28a5b109..02b9e7b2 100644 --- a/lib/optimizely.rb +++ b/lib/optimizely.rb @@ -137,6 +137,9 @@ def get_variation(experiment_key, user_id, attributes = nil) }, @logger, Logger::ERROR ) + experiment = @config.get_experiment_from_key(experiment_key) + return nil if experiment.nil? + unless user_inputs_valid?(attributes) @logger.log(Logger::INFO, "Not activating user '#{user_id}.") return nil @@ -145,10 +148,14 @@ def get_variation(experiment_key, user_id, attributes = nil) variation_id = @decision_service.get_variation(experiment_key, user_id, attributes) variation = @config.get_variation_from_id(experiment_key, variation_id) unless variation_id.nil? variation_key = variation['key'] if variation - + decision_notification_type = if @config.feature_experiment?(experiment['id']) + Helpers::Constants::DECISION_NOTIFICATION_TYPES['FEATURE_TEST'] + else + Helpers::Constants::DECISION_NOTIFICATION_TYPES['AB_TEST'] + end @notification_center.send_notifications( NotificationCenter::NOTIFICATION_TYPES[:DECISION], - Helpers::Constants::DECISION_INFO_TYPES['EXPERIMENT'], user_id, (attributes || {}), + decision_notification_type, user_id, (attributes || {}), experiment_key: experiment_key, variation_key: variation_key ) @@ -264,14 +271,16 @@ def is_feature_enabled(feature_flag_key, user_id, attributes = nil) decision = @decision_service.get_variation_for_feature(feature_flag, user_id, attributes) feature_enabled = false - source_string = Optimizely::DecisionService::DECISION_SOURCE_ROLLOUT + source_string = Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT'] if decision.is_a?(Optimizely::DecisionService::Decision) variation = decision['variation'] feature_enabled = variation['featureEnabled'] - if decision.source == Optimizely::DecisionService::DECISION_SOURCE_EXPERIMENT - source_string = Optimizely::DecisionService::DECISION_SOURCE_EXPERIMENT - experiment_key = decision.experiment['key'] - variation_key = variation['key'] + if decision.source == Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] + source_string = Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] + source_info = { + experiment_key: decision.experiment['key'], + variation_key: variation['key'] + } # Send event if Decision came from an experiment. send_impression(decision.experiment, variation['key'], user_id, attributes) else @@ -282,13 +291,12 @@ def is_feature_enabled(feature_flag_key, user_id, attributes = nil) @notification_center.send_notifications( NotificationCenter::NOTIFICATION_TYPES[:DECISION], - Helpers::Constants::DECISION_INFO_TYPES['FEATURE'], + Helpers::Constants::DECISION_NOTIFICATION_TYPES['FEATURE'], user_id, (attributes || {}), feature_key: feature_flag_key, feature_enabled: feature_enabled, - source: source_string.upcase, - source_experiment_key: experiment_key, - source_variation_key: variation_key + source: source_string, + source_info: source_info || {} ) if feature_enabled == true @@ -481,22 +489,23 @@ def get_feature_variable_for_type(feature_flag_key, variable_key, variable_type, return nil if variable.nil? feature_enabled = false - # Returns nil if type differs if variable['type'] != variable_type @logger.log(Logger::WARN, "Requested variable as type '#{variable_type}' but variable '#{variable_key}' is of type '#{variable['type']}'.") return nil else - source_string = Optimizely::DecisionService::DECISION_SOURCE_ROLLOUT + source_string = Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT'] decision = @decision_service.get_variation_for_feature(feature_flag, user_id, attributes) variable_value = variable['defaultValue'] if decision variation = decision['variation'] - if decision['source'] == Optimizely::DecisionService::DECISION_SOURCE_EXPERIMENT - experiment_key = decision.experiment['key'] - variation_key = variation['key'] - source_string = Optimizely::DecisionService::DECISION_SOURCE_EXPERIMENT + if decision['source'] == Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] + source_info = { + experiment_key: decision.experiment['key'], + variation_key: variation['key'] + } + source_string = Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] end feature_enabled = variation['featureEnabled'] if feature_enabled == true @@ -524,15 +533,14 @@ def get_feature_variable_for_type(feature_flag_key, variable_key, variable_type, @notification_center.send_notifications( NotificationCenter::NOTIFICATION_TYPES[:DECISION], - Helpers::Constants::DECISION_INFO_TYPES['FEATURE_VARIABLE'], user_id, (attributes || {}), + Helpers::Constants::DECISION_NOTIFICATION_TYPES['FEATURE_VARIABLE'], user_id, (attributes || {}), feature_key: feature_flag_key, feature_enabled: feature_enabled, + source: source_string, variable_key: variable_key, variable_type: variable_type, variable_value: variable_value, - source: source_string.upcase, - source_experiment_key: experiment_key, - source_variation_key: variation_key + source_info: source_info || {} ) variable_value diff --git a/lib/optimizely/decision_service.rb b/lib/optimizely/decision_service.rb index 4f0a242d..9b71dfbd 100644 --- a/lib/optimizely/decision_service.rb +++ b/lib/optimizely/decision_service.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2017-2018, Optimizely and contributors +# Copyright 2017-2019, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -35,8 +35,11 @@ class DecisionService attr_reader :config Decision = Struct.new(:experiment, :variation, :source) - DECISION_SOURCE_EXPERIMENT = 'experiment' - DECISION_SOURCE_ROLLOUT = 'rollout' + + DECISION_SOURCES = { + 'FEATURE_TEST' => 'feature-test', + 'ROLLOUT' => 'rollout' + }.freeze def initialize(config, user_profile_service = nil) @config = config @@ -172,7 +175,7 @@ def get_variation_for_feature_experiment(feature_flag, user_id, attributes = nil Logger::INFO, "The user '#{user_id}' is bucketed into experiment '#{experiment_key}' of feature '#{feature_flag_key}'." ) - return Decision.new(experiment, variation, DECISION_SOURCE_EXPERIMENT) + return Decision.new(experiment, variation, DECISION_SOURCES['FEATURE_TEST']) end @config.logger.log( @@ -236,7 +239,7 @@ def get_variation_for_feature_rollout(feature_flag, user_id, attributes = nil) # Evaluate if user satisfies the traffic allocation for this rollout rule variation = @bucketer.bucket(rollout_rule, bucketing_id, user_id) - return Decision.new(rollout_rule, variation, DECISION_SOURCE_ROLLOUT) unless variation.nil? + return Decision.new(rollout_rule, variation, DECISION_SOURCES['ROLLOUT']) unless variation.nil? break end @@ -255,7 +258,7 @@ def get_variation_for_feature_rollout(feature_flag, user_id, attributes = nil) return nil end variation = @bucketer.bucket(everyone_else_experiment, bucketing_id, user_id) - return Decision.new(everyone_else_experiment, variation, DECISION_SOURCE_ROLLOUT) unless variation.nil? + return Decision.new(everyone_else_experiment, variation, DECISION_SOURCES['ROLLOUT']) unless variation.nil? nil end diff --git a/lib/optimizely/helpers/constants.rb b/lib/optimizely/helpers/constants.rb index f760d3b0..c3068ede 100644 --- a/lib/optimizely/helpers/constants.rb +++ b/lib/optimizely/helpers/constants.rb @@ -353,10 +353,11 @@ module Constants 'to upgrade to a newer release of the Optimizely SDK.' }.freeze - DECISION_INFO_TYPES = { - 'EXPERIMENT' => 'experiment', + DECISION_NOTIFICATION_TYPES = { + 'AB_TEST' => 'ab-test', 'FEATURE' => 'feature', - 'FEATURE_VARIABLE' => 'feature_variable' + 'FEATURE_TEST' => 'feature-test', + 'FEATURE_VARIABLE' => 'feature-variable' }.freeze end end diff --git a/lib/optimizely/project_config.rb b/lib/optimizely/project_config.rb index 11dba235..742d71df 100644 --- a/lib/optimizely/project_config.rb +++ b/lib/optimizely/project_config.rb @@ -47,6 +47,7 @@ class ProjectConfig attr_reader :attribute_key_map attr_reader :audience_id_map attr_reader :event_key_map + attr_reader :experiment_feature_map attr_reader :experiment_id_map attr_reader :experiment_key_map attr_reader :feature_flag_key_map @@ -140,9 +141,13 @@ def initialize(datafile, logger, error_handler) @variation_key_map[key] = generate_key_map(variations, 'key') end @feature_flag_key_map = generate_key_map(@feature_flags, 'key') + @experiment_feature_map = {} @feature_variable_key_map = {} @feature_flag_key_map.each do |key, feature_flag| @feature_variable_key_map[key] = generate_key_map(feature_flag['variables'], 'key') + feature_flag['experimentIds'].each do |experiment_id| + @experiment_feature_map[experiment_id] = [feature_flag['id']] + end end end @@ -451,6 +456,16 @@ def get_rollout_from_id(rollout_id) nil end + def feature_experiment?(experiment_id) + # Determines if given experiment is a feature test. + # + # experiment_id - String experiment ID + # + # Returns true if experiment belongs to any feature, + # false otherwise. + @experiment_feature_map.key?(experiment_id) + end + private def generate_key_map(array, key) diff --git a/spec/decision_service_spec.rb b/spec/decision_service_spec.rb index 701720df..a97f3f9d 100644 --- a/spec/decision_service_spec.rb +++ b/spec/decision_service_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2017-2018, Optimizely and contributors +# Copyright 2017-2019, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -388,7 +388,7 @@ expected_decision = Optimizely::DecisionService::Decision.new( config.experiment_key_map['test_experiment_multivariate'], config.variation_id_map['test_experiment_multivariate']['122231'], - Optimizely::DecisionService::DECISION_SOURCE_EXPERIMENT + Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] ) expect(decision_service.get_variation_for_feature_experiment(feature_flag, 'user_1', user_attributes)).to eq(expected_decision) @@ -408,18 +408,18 @@ expected_decision = Optimizely::DecisionService::Decision.new( mutex_exp, variation, - Optimizely::DecisionService::DECISION_SOURCE_EXPERIMENT + Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] ) allow(decision_service).to receive(:get_variation) .and_return(variation['id']) end it 'should return the variation the user is bucketed into' do - feature_flag = config.feature_flag_key_map['boolean_feature'] + feature_flag = config.feature_flag_key_map['mutex_group_feature'] expect(decision_service.get_variation_for_feature_experiment(feature_flag, user_id, user_attributes)).to eq(expected_decision) expect(spy_logger).to have_received(:log).once - .with(Logger::INFO, "The user 'user_1' is bucketed into experiment 'group1_exp1' of feature 'boolean_feature'.") + .with(Logger::INFO, "The user 'user_1' is bucketed into experiment 'group1_exp1' of feature 'mutex_group_feature'.") end end @@ -436,11 +436,11 @@ end it 'should return nil and log a message' do - feature_flag = config.feature_flag_key_map['boolean_feature'] + feature_flag = config.feature_flag_key_map['mutex_group_feature'] expect(decision_service.get_variation_for_feature_experiment(feature_flag, user_id, user_attributes)).to eq(nil) expect(spy_logger).to have_received(:log).once - .with(Logger::INFO, "The user 'user_1' is not bucketed into any of the experiments on the feature 'boolean_feature'.") + .with(Logger::INFO, "The user 'user_1' is not bucketed into any of the experiments on the feature 'mutex_group_feature'.") end end end @@ -487,7 +487,7 @@ feature_flag = config.feature_flag_key_map['boolean_single_variable_feature'] rollout_experiment = config.rollout_id_map[feature_flag['rolloutId']]['experiments'][0] variation = rollout_experiment['variations'][0] - expected_decision = Optimizely::DecisionService::Decision.new(rollout_experiment, variation, Optimizely::DecisionService::DECISION_SOURCE_ROLLOUT) + expected_decision = Optimizely::DecisionService::Decision.new(rollout_experiment, variation, Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT']) allow(Optimizely::Audience).to receive(:user_in_experiment?).and_return(true) allow(decision_service.bucketer).to receive(:bucket) .with(rollout_experiment, user_id, user_id) @@ -527,7 +527,7 @@ rollout = config.rollout_id_map[feature_flag['rolloutId']] everyone_else_experiment = rollout['experiments'][2] variation = everyone_else_experiment['variations'][0] - expected_decision = Optimizely::DecisionService::Decision.new(everyone_else_experiment, variation, Optimizely::DecisionService::DECISION_SOURCE_ROLLOUT) + expected_decision = Optimizely::DecisionService::Decision.new(everyone_else_experiment, variation, Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT']) allow(Optimizely::Audience).to receive(:user_in_experiment?).and_return(true) allow(decision_service.bucketer).to receive(:bucket) .with(rollout['experiments'][0], user_id, user_id) @@ -554,7 +554,7 @@ rollout = config.rollout_id_map[feature_flag['rolloutId']] everyone_else_experiment = rollout['experiments'][2] variation = everyone_else_experiment['variations'][0] - expected_decision = Optimizely::DecisionService::Decision.new(everyone_else_experiment, variation, Optimizely::DecisionService::DECISION_SOURCE_ROLLOUT) + expected_decision = Optimizely::DecisionService::Decision.new(everyone_else_experiment, variation, Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT']) allow(Optimizely::Audience).to receive(:user_in_experiment?).and_return(false) allow(Optimizely::Audience).to receive(:user_in_experiment?) @@ -652,7 +652,7 @@ expected_decision = Optimizely::DecisionService::Decision.new( nil, variation, - Optimizely::DecisionService::DECISION_SOURCE_ROLLOUT + Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT'] ) allow(decision_service).to receive(:get_variation_for_feature_experiment).and_return(nil) allow(decision_service).to receive(:get_variation_for_feature_rollout).and_return(expected_decision) diff --git a/spec/project_config_spec.rb b/spec/project_config_spec.rb index 3aa8ffb1..fc4d3769 100644 --- a/spec/project_config_spec.rb +++ b/spec/project_config_spec.rb @@ -59,6 +59,16 @@ 'test_event_not_running' => config_body['events'][3] } + expected_experiment_feature_map = { + '122227' => [config_body['featureFlags'][0]['id']], + '133331' => [config_body['featureFlags'][6]['id']], + '133332' => [config_body['featureFlags'][6]['id']], + '122238' => [config_body['featureFlags'][1]['id']], + '122241' => [config_body['featureFlags'][2]['id']], + '122235' => [config_body['featureFlags'][4]['id']], + '122230' => [config_body['featureFlags'][5]['id']] + } + expected_experiment_key_map = { 'test_experiment' => config_body['experiments'][0], 'test_experiment_not_started' => config_body['experiments'][1], @@ -650,6 +660,7 @@ expect(project_config.attribute_key_map).to eq(expected_attribute_key_map) expect(project_config.audience_id_map).to eq(expected_audience_id_map) expect(project_config.event_key_map).to eq(expected_event_key_map) + expect(project_config.experiment_feature_map).to eq(expected_experiment_feature_map) expect(project_config.experiment_key_map).to eq(expected_experiment_key_map) expect(project_config.feature_flag_key_map).to eq(expected_feature_flag_key_map) expect(project_config.feature_variable_key_map).to eq(expected_feature_variable_key_map) @@ -1069,4 +1080,18 @@ expect(config.get_attribute_id('$opt_user_agent')).to eq('$opt_user_agent') end end + + describe '#feature_experiment' do + let(:config) { Optimizely::ProjectConfig.new(config_body_JSON, logger, error_handler) } + + it 'should return true if the experiment is a feature test' do + experiment = config.get_experiment_from_key('test_experiment_double_feature') + expect(config.feature_experiment?(experiment['id'])).to eq(true) + end + + it 'should return false if the experiment is not a feature test' do + experiment = config.get_experiment_from_key('test_experiment') + expect(config.feature_experiment?(experiment['id'])).to eq(false) + end + end end diff --git a/spec/project_spec.rb b/spec/project_spec.rb index ba633269..8f6f0bdc 100644 --- a/spec/project_spec.rb +++ b/spec/project_spec.rb @@ -660,7 +660,7 @@ class InvalidErrorHandler; end it 'should call decision listener when user not in experiment' do expect(project_instance.notification_center).to receive(:send_notifications).with( Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION], - 'experiment', 'test_user', {}, + 'feature-test', 'test_user', {}, experiment_key: 'test_experiment_with_audience', variation_key: nil ) @@ -675,7 +675,7 @@ class InvalidErrorHandler; end # Decision listener expect(project_instance.notification_center).to receive(:send_notifications).with( Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION], - 'experiment', 'test_user', {}, + 'ab-test', 'test_user', {}, experiment_key: 'test_experiment', variation_key: 'control' ).ordered @@ -1107,21 +1107,31 @@ class InvalidErrorHandler; end it 'should call decision listener when user in experiment' do expect(project_instance.notification_center).to receive(:send_notifications).with( Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION], - 'experiment', 'test_user', {'browser_type' => 'firefox'}, - experiment_key: 'test_experiment_with_audience', variation_key: 'control_with_audience' + 'ab-test', 'test_user', {'browser_type' => 'firefox'}, + experiment_key: 'test_experiment', variation_key: 'control' ) - project_instance.get_variation('test_experiment_with_audience', 'test_user', 'browser_type' => 'firefox') + project_instance.get_variation('test_experiment', 'test_user', 'browser_type' => 'firefox') end it 'should call decision listener when user not in experiment' do expect(project_instance.notification_center).to receive(:send_notifications).with( Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION], - 'experiment', 'test_user', {'browser_type' => 'chrome'}, - experiment_key: 'test_experiment_with_audience', variation_key: nil + 'ab-test', 'test_user', {'browser_type' => 'chrome'}, + experiment_key: 'test_experiment', variation_key: 'control' ) - project_instance.get_variation('test_experiment_with_audience', 'test_user', 'browser_type' => 'chrome') + project_instance.get_variation('test_experiment', 'test_user', 'browser_type' => 'chrome') + end + + it 'should call decision listener with type feature-test when get_variation returns feature experiment variation' do + expect(project_instance.notification_center).to receive(:send_notifications).with( + Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION], + 'feature-test', 'test_user', {'browser_type' => 'chrome'}, + experiment_key: 'test_experiment_double_feature', variation_key: 'control' + ) + + project_instance.get_variation('test_experiment_double_feature', 'test_user', 'browser_type' => 'chrome') end end end @@ -1226,7 +1236,7 @@ class InvalidErrorHandler; end decision_to_return = Optimizely::DecisionService::Decision.new( experiment_to_return, variation_to_return, - Optimizely::DecisionService::DECISION_SOURCE_ROLLOUT + Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT'] ) allow(project_instance.decision_service).to receive(:get_variation_for_feature).and_return(decision_to_return) @@ -1241,7 +1251,7 @@ class InvalidErrorHandler; end decision_to_return = Optimizely::DecisionService::Decision.new( experiment_to_return, variation_to_return, - Optimizely::DecisionService::DECISION_SOURCE_ROLLOUT + Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT'] ) allow(project_instance.decision_service).to receive(:get_variation_for_feature).and_return(decision_to_return) expect(variation_to_return['featureEnabled']).to be false @@ -1256,7 +1266,7 @@ class InvalidErrorHandler; end decision_to_return = Optimizely::DecisionService::Decision.new( experiment_to_return, variation_to_return, - Optimizely::DecisionService::DECISION_SOURCE_ROLLOUT + Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT'] ) allow(project_instance.decision_service).to receive(:get_variation_for_feature).and_return(decision_to_return) expect(variation_to_return['featureEnabled']).to be true @@ -1325,7 +1335,7 @@ class InvalidErrorHandler; end decision_to_return = Optimizely::DecisionService::Decision.new( experiment_to_return, variation_to_return, - Optimizely::DecisionService::DECISION_SOURCE_EXPERIMENT + Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] ) expect(project_instance.notification_center).to receive(:send_notifications) @@ -1353,7 +1363,7 @@ class InvalidErrorHandler; end decision_to_return = Optimizely::DecisionService::Decision.new( experiment_to_return, variation_to_return, - Optimizely::DecisionService::DECISION_SOURCE_EXPERIMENT + Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] ) expect(variation_to_return['featureEnabled']).to be false allow(project_instance.decision_service).to receive(:get_variation_for_feature).and_return(decision_to_return) @@ -1371,7 +1381,7 @@ class InvalidErrorHandler; end decision_to_return = Optimizely::DecisionService::Decision.new( experiment_to_return, variation_to_return, - Optimizely::DecisionService::DECISION_SOURCE_EXPERIMENT + Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] ) allow(project_instance.decision_service).to receive(:get_variation_for_feature).and_return(decision_to_return) @@ -1386,9 +1396,11 @@ class InvalidErrorHandler; end 'feature', 'test_user', {}, feature_enabled: true, feature_key: 'multi_variate_feature', - source: 'EXPERIMENT', - source_experiment_key: 'test_experiment_multivariate', - source_variation_key: 'Fred' + source: 'feature-test', + source_info: { + experiment_key: 'test_experiment_multivariate', + variation_key: 'Fred' + } ).ordered project_instance.is_feature_enabled('multi_variate_feature', 'test_user') @@ -1401,7 +1413,7 @@ class InvalidErrorHandler; end decision_to_return = Optimizely::DecisionService::Decision.new( experiment_to_return, variation_to_return, - Optimizely::DecisionService::DECISION_SOURCE_EXPERIMENT + Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] ) allow(project_instance.decision_service).to receive(:get_variation_for_feature).and_return(decision_to_return) @@ -1415,9 +1427,11 @@ class InvalidErrorHandler; end 'feature', 'test_user', {'browser_type' => 'chrome'}, feature_enabled: false, feature_key: 'multi_variate_feature', - source: 'EXPERIMENT', - source_experiment_key: 'test_experiment_multivariate', - source_variation_key: 'Feorge' + source: 'feature-test', + source_info: { + experiment_key: 'test_experiment_multivariate', + variation_key: 'Feorge' + } ) project_instance.is_feature_enabled('multi_variate_feature', 'test_user', 'browser_type' => 'chrome') @@ -1429,7 +1443,7 @@ class InvalidErrorHandler; end decision_to_return = Optimizely::DecisionService::Decision.new( experiment_to_return, variation_to_return, - Optimizely::DecisionService::DECISION_SOURCE_ROLLOUT + Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT'] ) allow(project_instance.decision_service).to receive(:get_variation_for_feature).and_return(decision_to_return) @@ -1440,9 +1454,8 @@ class InvalidErrorHandler; end 'feature', 'test_user', {'browser_type' => 'firefox'}, feature_enabled: true, feature_key: 'boolean_single_variable_feature', - source: 'ROLLOUT', - source_experiment_key: nil, - source_variation_key: nil + source: 'rollout', + source_info: {} ) project_instance.is_feature_enabled('boolean_single_variable_feature', 'test_user', 'browser_type' => 'firefox') @@ -1457,9 +1470,8 @@ class InvalidErrorHandler; end 'feature', 'test_user', {}, feature_enabled: false, feature_key: 'boolean_single_variable_feature', - source: 'ROLLOUT', - source_experiment_key: nil, - source_variation_key: nil + source: 'rollout', + source_info: {} ) project_instance.is_feature_enabled('boolean_single_variable_feature', 'test_user') @@ -1473,9 +1485,8 @@ class InvalidErrorHandler; end 'feature', 'test_user', {'browser_type' => 'firefox'}, feature_enabled: false, feature_key: 'multi_variate_feature', - source: 'ROLLOUT', - source_experiment_key: nil, - source_variation_key: nil + source: 'rollout', + source_info: {} ) project_instance.is_feature_enabled('multi_variate_feature', 'test_user', 'browser_type' => 'firefox') @@ -1560,18 +1571,18 @@ class InvalidErrorHandler; end Optimizely::DecisionService::Decision.new( experiment_to_return, experiment_to_return['variations'][0], - Optimizely::DecisionService::DECISION_SOURCE_EXPERIMENT + Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] ), nil, Optimizely::DecisionService::Decision.new( rollout_to_return, rollout_to_return['variations'][0], - Optimizely::DecisionService::DECISION_SOURCE_ROLLOUT + Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT'] ), Optimizely::DecisionService::Decision.new( experiment_to_return, experiment_to_return['variations'][1], - Optimizely::DecisionService::DECISION_SOURCE_EXPERIMENT + Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] ), nil, nil, @@ -1588,9 +1599,11 @@ class InvalidErrorHandler; end 'feature', 'test_user', {'browser_type' => 'firefox'}, feature_enabled: true, feature_key: 'boolean_feature', - source: 'EXPERIMENT', - source_experiment_key: 'test_experiment_multivariate', - source_variation_key: 'Fred' + source: 'feature-test', + source_info: { + experiment_key: 'test_experiment_multivariate', + variation_key: 'Fred' + } ).ordered expect(project_instance.notification_center).to receive(:send_notifications).once.with( @@ -1598,9 +1611,8 @@ class InvalidErrorHandler; end 'feature', 'test_user', {'browser_type' => 'firefox'}, feature_enabled: false, feature_key: 'double_single_variable_feature', - source: 'ROLLOUT', - source_experiment_key: nil, - source_variation_key: nil + source: 'rollout', + source_info: {} ).ordered expect(project_instance.notification_center).to receive(:send_notifications).once.with( @@ -1608,9 +1620,8 @@ class InvalidErrorHandler; end 'feature', 'test_user', {'browser_type' => 'firefox'}, feature_enabled: true, feature_key: 'integer_single_variable_feature', - source: 'ROLLOUT', - source_experiment_key: nil, - source_variation_key: nil + source: 'rollout', + source_info: {} ).ordered expect(project_instance.notification_center).to receive(:send_notifications).once.with( @@ -1618,9 +1629,11 @@ class InvalidErrorHandler; end 'feature', 'test_user', {'browser_type' => 'firefox'}, feature_enabled: false, feature_key: 'boolean_single_variable_feature', - source: 'EXPERIMENT', - source_experiment_key: 'test_experiment_multivariate', - source_variation_key: 'Feorge' + source: 'feature-test', + source_info: { + experiment_key: 'test_experiment_multivariate', + variation_key: 'Feorge' + } ).ordered expect(project_instance.notification_center).to receive(:send_notifications).once.with( @@ -1628,9 +1641,8 @@ class InvalidErrorHandler; end 'feature', 'test_user', {'browser_type' => 'firefox'}, feature_enabled: false, feature_key: 'string_single_variable_feature', - source: 'ROLLOUT', - source_experiment_key: nil, - source_variation_key: nil + source: 'rollout', + source_info: {} ).ordered expect(project_instance.notification_center).to receive(:send_notifications).once.with( @@ -1638,9 +1650,8 @@ class InvalidErrorHandler; end 'feature', 'test_user', {'browser_type' => 'firefox'}, feature_enabled: false, feature_key: 'multi_variate_feature', - source: 'ROLLOUT', - source_experiment_key: nil, - source_variation_key: nil + source: 'rollout', + source_info: {} ).ordered expect(project_instance.notification_center).to receive(:send_notifications).once.with( @@ -1648,9 +1659,8 @@ class InvalidErrorHandler; end 'feature', 'test_user', {'browser_type' => 'firefox'}, feature_enabled: false, feature_key: 'mutex_group_feature', - source: 'ROLLOUT', - source_experiment_key: nil, - source_variation_key: nil + source: 'rollout', + source_info: {} ).ordered expect(project_instance.notification_center).to receive(:send_notifications).once.with( @@ -1658,9 +1668,8 @@ class InvalidErrorHandler; end 'feature', 'test_user', {'browser_type' => 'firefox'}, feature_enabled: false, feature_key: 'empty_feature', - source: 'ROLLOUT', - source_experiment_key: nil, - source_variation_key: nil + source: 'rollout', + source_info: {} ).ordered expect(project_instance.get_enabled_features('test_user', 'browser_type' => 'firefox')).to eq(enabled_features) @@ -2043,7 +2052,7 @@ class InvalidErrorHandler; end decision_to_return = Optimizely::DecisionService::Decision.new( experiment_to_return, variation_to_return, - Optimizely::DecisionService::DECISION_SOURCE_EXPERIMENT + Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] ) allow(project_instance.decision_service).to receive(:get_variation_for_feature).and_return(decision_to_return) @@ -2051,15 +2060,17 @@ class InvalidErrorHandler; end # DECISION listener called when the user is in experiment with variation feature off. expect(project_instance.notification_center).to receive(:send_notifications).once.with( Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION], - 'feature_variable', 'test_user', {}, + 'feature-variable', 'test_user', {}, feature_key: 'integer_single_variable_feature', feature_enabled: false, + source: 'feature-test', variable_key: 'integer_variable', variable_type: 'integer', variable_value: 7, - source: 'EXPERIMENT', - source_experiment_key: 'test_experiment_integer_feature', - source_variation_key: 'control' + source_info: { + experiment_key: 'test_experiment_integer_feature', + variation_key: 'control' + } ) expect(project_instance.send( @@ -2085,7 +2096,7 @@ class InvalidErrorHandler; end decision_to_return = Optimizely::DecisionService::Decision.new( experiment_to_return, variation_to_return, - Optimizely::DecisionService::DECISION_SOURCE_EXPERIMENT + Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] ) allow(project_instance.decision_service).to receive(:get_variation_for_feature).and_return(decision_to_return) @@ -2093,15 +2104,17 @@ class InvalidErrorHandler; end # DECISION listener called when the user is in experiment with variation feature on. expect(project_instance.notification_center).to receive(:send_notifications).once.with( Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION], - 'feature_variable', 'test_user', {'browser_type' => 'firefox'}, + 'feature-variable', 'test_user', {'browser_type' => 'firefox'}, feature_key: 'integer_single_variable_feature', feature_enabled: true, + source: 'feature-test', variable_key: 'integer_variable', variable_type: 'integer', variable_value: 42, - source: 'EXPERIMENT', - source_experiment_key: 'test_experiment_integer_feature', - source_variation_key: 'control' + source_info: { + experiment_key: 'test_experiment_integer_feature', + variation_key: 'control' + } ) expect(project_instance.send( @@ -2121,7 +2134,7 @@ class InvalidErrorHandler; end decision_to_return = Optimizely::DecisionService::Decision.new( experiment_to_return, variation_to_return, - Optimizely::DecisionService::DECISION_SOURCE_ROLLOUT + Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT'] ) allow(project_instance.decision_service).to receive(:get_variation_for_feature).and_return(decision_to_return) @@ -2130,15 +2143,14 @@ class InvalidErrorHandler; end expect(variation_to_return['featureEnabled']).to be true expect(project_instance.notification_center).to receive(:send_notifications).once.with( Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION], - 'feature_variable', 'test_user', {}, + 'feature-variable', 'test_user', {}, feature_key: 'boolean_single_variable_feature', feature_enabled: true, + source: 'rollout', variable_key: 'boolean_variable', variable_type: 'boolean', variable_value: true, - source: 'ROLLOUT', - source_experiment_key: nil, - source_variation_key: nil + source_info: {} ) expect(project_instance.send( @@ -2157,7 +2169,7 @@ class InvalidErrorHandler; end decision_to_return = Optimizely::DecisionService::Decision.new( experiment_to_return, variation_to_return, - Optimizely::DecisionService::DECISION_SOURCE_ROLLOUT + Optimizely::DecisionService::DECISION_SOURCES['ROLLOUT'] ) allow(project_instance.decision_service).to receive(:get_variation_for_feature).and_return(decision_to_return) @@ -2165,15 +2177,14 @@ class InvalidErrorHandler; end expect(variation_to_return['featureEnabled']).to be false expect(project_instance.notification_center).to receive(:send_notifications).once.with( Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION], - 'feature_variable', 'test_user', {}, + 'feature-variable', 'test_user', {}, feature_key: 'boolean_single_variable_feature', feature_enabled: false, + source: 'rollout', variable_key: 'boolean_variable', variable_type: 'boolean', variable_value: true, - source: 'ROLLOUT', - source_experiment_key: nil, - source_variation_key: nil + source_info: {} ) expect(project_instance.send( @@ -2196,15 +2207,14 @@ class InvalidErrorHandler; end expect(project_instance.notification_center).to receive(:send_notifications).once.with( Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION], - 'feature_variable', 'test_user', {}, + 'feature-variable', 'test_user', {}, feature_key: 'integer_single_variable_feature', feature_enabled: false, + source: 'rollout', variable_key: 'integer_variable', variable_type: 'integer', variable_value: 7, - source: 'ROLLOUT', - source_experiment_key: nil, - source_variation_key: nil + source_info: {} ) expect(project_instance.send( diff --git a/spec/spec_params.rb b/spec/spec_params.rb index 34e814bb..c7957ac0 100644 --- a/spec/spec_params.rb +++ b/spec/spec_params.rb @@ -466,7 +466,7 @@ module OptimizelySpec 'id' => '155549', 'key' => 'boolean_feature', 'rolloutId' => '', - 'experimentIds' => %w[133331 133332], + 'experimentIds' => ['122227'], 'variables' => [] }, { 'id' => '155550',