diff --git a/lib/optimizely.rb b/lib/optimizely.rb index 204625f7..08bec7f6 100644 --- a/lib/optimizely.rb +++ b/lib/optimizely.rb @@ -143,12 +143,17 @@ def get_variation(experiment_key, user_id, attributes = nil) end 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 - unless variation_id.nil? - variation = @config.get_variation_from_id(experiment_key, variation_id) - return variation['key'] if variation - end - nil + @notification_center.send_notifications( + NotificationCenter::NOTIFICATION_TYPES[:DECISION], + Helpers::Constants::DECISION_INFO_TYPES['EXPERIMENT'], user_id, (attributes || {}), + experiment_key: experiment_key, + variation_key: variation_key + ) + + variation_key end # Force a user into a variation for a given experiment. diff --git a/lib/optimizely/helpers/constants.rb b/lib/optimizely/helpers/constants.rb index 73e70b81..f760d3b0 100644 --- a/lib/optimizely/helpers/constants.rb +++ b/lib/optimizely/helpers/constants.rb @@ -352,6 +352,12 @@ module Constants 'UNKNOWN_MATCH_TYPE' => 'Audience condition %s uses an unknown match type. You may need ' \ 'to upgrade to a newer release of the Optimizely SDK.' }.freeze + + DECISION_INFO_TYPES = { + 'EXPERIMENT' => 'experiment', + 'FEATURE' => 'feature', + 'FEATURE_VARIABLE' => 'feature_variable' + }.freeze end end end diff --git a/lib/optimizely/notification_center.rb b/lib/optimizely/notification_center.rb index c30b74c0..c3c90a8c 100644 --- a/lib/optimizely/notification_center.rb +++ b/lib/optimizely/notification_center.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. @@ -22,6 +22,7 @@ class NotificationCenter NOTIFICATION_TYPES = { ACTIVATE: 'ACTIVATE: experiment, user_id, attributes, variation, event', + DECISION: 'DECISION: type, user_id, attributes, decision_info', TRACK: 'TRACK: event_key, user_id, attributes, event_tags, event' }.freeze diff --git a/spec/project_spec.rb b/spec/project_spec.rb index ae27ef5b..efbad984 100644 --- a/spec/project_spec.rb +++ b/spec/project_spec.rb @@ -592,11 +592,17 @@ class InvalidErrorHandler; end .with('test_experiment') .and_return([]) experiment = project_instance.config.get_experiment_from_key('test_experiment') + + # Decision listener + expect(project_instance.notification_center).to receive(:send_notifications).ordered + + # Activate listener expect(project_instance.notification_center).to receive(:send_notifications).with( Optimizely::NotificationCenter::NOTIFICATION_TYPES[:ACTIVATE], experiment, 'test_user', nil, variation_to_return, instance_of(Optimizely::Event) - ) + ).ordered + project_instance.activate('test_experiment', 'test_user') expect(spy_logger).to have_received(:log).once.with(Logger::INFO, include('Dispatching impression event to' \ @@ -649,6 +655,36 @@ class InvalidErrorHandler; end invalid_project = Optimizely::Project.new('invalid') invalid_project.activate('test_exp', 'test_user') end + + describe '.decision listener' do + 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', {}, + experiment_key: 'test_experiment_with_audience', variation_key: nil + ) + + project_instance.activate('test_experiment_with_audience', 'test_user') + end + + it 'should call decision listener when user in experiment' do + variation_to_return = project_instance.config.get_variation_from_id('test_experiment', '111128') + allow(project_instance.decision_service.bucketer).to receive(:bucket).and_return(variation_to_return) + expect(project_instance.event_dispatcher).to receive(:dispatch_event).with(instance_of(Optimizely::Event)) + + # Decision listener + expect(project_instance.notification_center).to receive(:send_notifications).with( + Optimizely::NotificationCenter::NOTIFICATION_TYPES[:DECISION], + 'experiment', 'test_user', {}, + experiment_key: 'test_experiment', variation_key: 'control' + ).ordered + + # Activate listener + expect(project_instance.notification_center).to receive(:send_notifications).ordered + + project_instance.activate('test_experiment', 'test_user') + end + end end describe '#track' do @@ -1066,6 +1102,28 @@ class InvalidErrorHandler; end invalid_project = Optimizely::Project.new('invalid') invalid_project.get_variation('test_exp', 'test_user') end + + describe '.decision listener' do + 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' + ) + + project_instance.get_variation('test_experiment_with_audience', '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 + ) + + project_instance.get_variation('test_experiment_with_audience', 'test_user', 'browser_type' => 'chrome') + end + end end describe '#is_feature_enabled' do