diff --git a/lib/optimizely.rb b/lib/optimizely.rb index 529655f0..129d427f 100644 --- a/lib/optimizely.rb +++ b/lib/optimizely.rb @@ -35,7 +35,7 @@ def initialize(datafile, event_dispatcher = nil, logger = nil, error_handler = n @config = ProjectConfig.new(datafile, @logger, @error_handler) @bucketer = Bucketer.new(@config) - @event_builder = EventBuilder.new(@config, @bucketer) + @event_builder = EventBuilderV1.new(@config, @bucketer) end def activate(experiment_key, user_id, attributes = nil) @@ -70,7 +70,7 @@ def activate(experiment_key, user_id, attributes = nil) @logger.log(Logger::INFO, 'Dispatching impression event to URL %s with params %s.' % [impression_event.url, impression_event.params]) - @event_dispatcher.dispatch_event(impression_event.url, impression_event.params) + @event_dispatcher.dispatch_event(impression_event) @config.get_variation_key_from_id(experiment_key, variation_id) end @@ -136,7 +136,7 @@ def track(event_key, user_id, attributes = nil, event_value = nil) @logger.log(Logger::INFO, 'Dispatching conversion event to URL %s with params %s.' % [conversion_event.url, conversion_event.params]) - @event_dispatcher.dispatch_event(conversion_event.url, conversion_event.params) + @event_dispatcher.dispatch_event(conversion_event) end private diff --git a/lib/optimizely/event_builder.rb b/lib/optimizely/event_builder.rb index 6e25e92a..524e0608 100644 --- a/lib/optimizely/event_builder.rb +++ b/lib/optimizely/event_builder.rb @@ -6,28 +6,23 @@ module Optimizely class Event # Representation of an event which can be sent to the Optimizely logging endpoint. - # Event API format - OFFLINE_API_PATH = 'https://%{project_id}.log.optimizely.com/event' - - # Gets/Sets event params. - attr_accessor :params + attr_reader :http_verb + attr_reader :params + attr_reader :url - def initialize(params) + def initialize(http_verb, url, params) + @http_verb = http_verb + @url = url @params = params end - def url - # URL for sending impression/conversion event. - # - # project_id - ID for the project. - # - # Returns URL for event API. - - sprintf(OFFLINE_API_PATH, project_id: @params[Params::PROJECT_ID]) + # Override equality operator to make two events with the same contents equal for testing purposes + def ==(event) + @http_verb == event.http_verb && @url == event.url && @params == event.params end end - class EventBuilder + class EventBuilderV1 # Class which encapsulates methods to build events for tracking impressions and conversions. # Attribute mapping format @@ -36,8 +31,11 @@ class EventBuilder # Experiment mapping format EXPERIMENT_PARAM_FORMAT = '%{experiment_prefix}%{experiment_id}' - attr_accessor :config - attr_accessor :bucketer + # Event endpoint path + OFFLINE_API_PATH = 'https://%{project_id}.log.optimizely.com/event' + + attr_reader :config + attr_reader :bucketer attr_accessor :params def initialize(config, bucketer) @@ -60,7 +58,7 @@ def create_impression_event(experiment_key, variation_id, user_id, attributes) add_common_params(user_id, attributes) add_impression_goal(experiment_key) add_experiment(experiment_key, variation_id) - Event.new(@params) + Event.new(:get, sprintf(OFFLINE_API_PATH, project_id: @params[Params::PROJECT_ID]), @params) end def create_conversion_event(event_key, user_id, attributes, event_value, experiment_keys) @@ -76,7 +74,7 @@ def create_conversion_event(event_key, user_id, attributes, event_value, experim add_common_params(user_id, attributes) add_conversion_goal(event_key, event_value) add_experiment_variation_params(user_id, experiment_keys) - Event.new(@params) + Event.new(:get, sprintf(OFFLINE_API_PATH, project_id: @params[Params::PROJECT_ID]), @params) end private diff --git a/lib/optimizely/event_dispatcher.rb b/lib/optimizely/event_dispatcher.rb index da57f08e..9a76d8bf 100644 --- a/lib/optimizely/event_dispatcher.rb +++ b/lib/optimizely/event_dispatcher.rb @@ -1,33 +1,28 @@ require 'httparty' module Optimizely - class BaseEventDispatcher - # Class encapsulating event dispatching functionality. - # Override with your own EventDispatcher providing dispatch_event method. - - def dispatch_event(_url, _params) - end - end - - class NoOpEventDispatcher < BaseEventDispatcher + class NoOpEventDispatcher # Class providing dispatch_event method which does nothing. - def dispatch_event(_url, _params) + def dispatch_event(event) end end - class EventDispatcher < BaseEventDispatcher + class EventDispatcher REQUEST_TIMEOUT = 10 - def dispatch_event(url, params) + def dispatch_event(event) # Dispatch the event being represented by the Event object. # - # url - URL to send impression/conversion event to. - # params - Params to be sent to the impression/conversion event. + # event - Event object - HTTParty.get(url, query: params, timeout: REQUEST_TIMEOUT) - rescue Timeout::Error => e - return e + if event.http_verb == :get + begin + HTTParty.get(event.url, query: event.params, timeout: REQUEST_TIMEOUT) + rescue Timeout::Error => e + return e + end + end end end end diff --git a/spec/event_builder_spec.rb b/spec/event_builder_spec.rb index 026d3021..8814d1a1 100644 --- a/spec/event_builder_spec.rb +++ b/spec/event_builder_spec.rb @@ -14,13 +14,9 @@ } @event = Optimizely::Event.new(@params) end - - it 'should return URL when url is called' do - expect(@event.url).to eq('https://111001.log.optimizely.com/event') - end end -describe Optimizely::EventBuilder do +describe Optimizely::EventBuilderV1 do before(:context) do @version = Optimizely::VERSION @config_body = OptimizelySpec::CONFIG_BODY @@ -32,7 +28,7 @@ before(:example) do config = Optimizely::ProjectConfig.new(@config_body_JSON, @logger, @error_handler) bucketer = Optimizely::Bucketer.new(config) - @event_builder = Optimizely::EventBuilder.new(config, bucketer) + @event_builder = Optimizely::EventBuilderV1.new(config, bucketer) end it 'should create Event object with right params when create_impression_event is called' do @@ -52,7 +48,7 @@ expect(@event_builder).to receive(:create_impression_event) .with('test_experiment', 'test_user') - .and_return(Optimizely::Event.new(expected_params)) + .and_return(Optimizely::Event.new(:get, '', expected_params)) impression_event = @event_builder.create_impression_event('test_experiment', 'test_user') expect(impression_event.params).to eq(expected_params) end @@ -75,7 +71,7 @@ expect(@event_builder).to receive(:create_impression_event) .with('test_experiment', 'test_user', {'browser_type' => 'firefox'}) - .and_return(Optimizely::Event.new(expected_params)) + .and_return(Optimizely::Event.new(:get, '', expected_params)) impression_event = @event_builder.create_impression_event('test_experiment', 'test_user', {'browser_type' => 'firefox'}) @@ -99,7 +95,7 @@ expect(@event_builder).to receive(:create_conversion_event) .with('test_event', 'test_user') - .and_return(Optimizely::Event.new(expected_params)) + .and_return(Optimizely::Event.new(:get, '', expected_params)) conversion_event = @event_builder.create_conversion_event('test_event', 'test_user') expect(conversion_event.params).to eq(expected_params) end @@ -122,7 +118,7 @@ expect(@event_builder).to receive(:create_conversion_event) .with('test_event', 'test_user', {'browser_type' => 'firefox'}) - .and_return(Optimizely::Event.new(expected_params)) + .and_return(Optimizely::Event.new(:get, '', expected_params)) conversion_event = @event_builder.create_conversion_event('test_event', 'test_user', {'browser_type' => 'firefox'}) expect(conversion_event.params).to eq(expected_params) end @@ -145,7 +141,7 @@ expect(@event_builder).to receive(:create_conversion_event) .with('test_event', 'test_user', nil, 42) - .and_return(Optimizely::Event.new(expected_params)) + .and_return(Optimizely::Event.new(:get, '', expected_params)) conversion_event = @event_builder.create_conversion_event('test_event', 'test_user', nil, 42) expect(conversion_event.params).to eq(expected_params) end diff --git a/spec/event_dispatcher_spec.rb b/spec/event_dispatcher_spec.rb index 985c40fe..bdd87dca 100644 --- a/spec/event_dispatcher_spec.rb +++ b/spec/event_dispatcher_spec.rb @@ -1,5 +1,6 @@ require 'spec_helper' require 'webmock' +require 'optimizely/event_builder' require 'optimizely/event_dispatcher' describe Optimizely::EventDispatcher do @@ -19,7 +20,8 @@ it 'should fire off GET request with provided URL and params' do stub_request(:get, @url).with(:query => @params) - @event_dispatcher.dispatch_event(@url, @params) + event = Optimizely::Event.new(:get, @url, @params) + @event_dispatcher.dispatch_event(event) expect(a_request(:get, @url).with(:query => @params)).to have_been_made.once end diff --git a/spec/project_spec.rb b/spec/project_spec.rb index 15db0d38..6d4c994c 100644 --- a/spec/project_spec.rb +++ b/spec/project_spec.rb @@ -96,7 +96,7 @@ class InvalidErrorHandler; end } allow(project_instance.bucketer).to receive(:bucket).and_return('111128') - allow(project_instance.event_dispatcher).to receive(:dispatch_event).with(log_url, params) + allow(project_instance.event_dispatcher).to receive(:dispatch_event).with(instance_of(Optimizely::Event)) allow(project_instance.config).to receive(:get_audience_ids_for_experiment) .with('test_experiment') .and_return([]) @@ -104,7 +104,7 @@ class InvalidErrorHandler; end stub_request(:get, log_url).with(:query => params) expect(project_instance.activate('test_experiment', 'test_user')).to eq('control') - expect(project_instance.event_dispatcher).to have_received(:dispatch_event).with(log_url, params).once + expect(project_instance.event_dispatcher).to have_received(:dispatch_event).with(Optimizely::Event.new(:get, log_url, params)).once expect(project_instance.bucketer).to have_received(:bucket).once end @@ -121,11 +121,11 @@ class InvalidErrorHandler; end 'time' => time_now.strftime('%s').to_i } allow(project_instance.bucketer).to receive(:bucket).and_return('122228') - allow(project_instance.event_dispatcher).to receive(:dispatch_event).with(log_url, params) + allow(project_instance.event_dispatcher).to receive(:dispatch_event).with(instance_of(Optimizely::Event)) expect(project_instance.activate('test_experiment_with_audience', 'test_user', 'browser_type' => 'firefox')) .to eq('control_with_audience') - expect(project_instance.event_dispatcher).to have_received(:dispatch_event).with(log_url, params).once + expect(project_instance.event_dispatcher).to have_received(:dispatch_event).with(Optimizely::Event.new(:get, log_url, params)).once expect(project_instance.bucketer).to have_received(:bucket).once end @@ -160,7 +160,7 @@ class InvalidErrorHandler; end } allow(project_instance.bucketer).to receive(:bucket).and_return('111128') - allow(project_instance.event_dispatcher).to receive(:dispatch_event).with(log_url, params) + allow(project_instance.event_dispatcher).to receive(:dispatch_event).with(instance_of(Optimizely::Event)) allow(project_instance.config).to receive(:get_audience_ids_for_experiment) .with('test_experiment') .and_return([]) @@ -192,9 +192,9 @@ class InvalidErrorHandler; end 'time' => time_now.strftime('%s').to_i } - allow(project_instance.event_dispatcher).to receive(:dispatch_event).with(log_url, params) + allow(project_instance.event_dispatcher).to receive(:dispatch_event).with(instance_of(Optimizely::Event)) project_instance.track('test_event', 'test_user') - expect(project_instance.event_dispatcher).to have_received(:dispatch_event).with(log_url, params).once + expect(project_instance.event_dispatcher).to have_received(:dispatch_event).with(Optimizely::Event.new(:get, log_url, params)).once end it 'should properly track an event by calling dispatch_event with right params with revenue provided' do @@ -210,9 +210,9 @@ class InvalidErrorHandler; end 'time' => time_now.strftime('%s').to_i } - allow(project_instance.event_dispatcher).to receive(:dispatch_event).with(log_url, params) + allow(project_instance.event_dispatcher).to receive(:dispatch_event).with(instance_of(Optimizely::Event)) project_instance.track('test_event', 'test_user', nil, 42) - expect(project_instance.event_dispatcher).to have_received(:dispatch_event).with(log_url, params).once + expect(project_instance.event_dispatcher).to have_received(:dispatch_event).with(Optimizely::Event.new(:get, log_url, params)).once end it 'should properly track an event by calling dispatch_event with right params with attributes provided' do @@ -228,9 +228,9 @@ class InvalidErrorHandler; end 'time' => time_now.strftime('%s').to_i } - allow(project_instance.event_dispatcher).to receive(:dispatch_event).with(log_url, params) + allow(project_instance.event_dispatcher).to receive(:dispatch_event).with(instance_of(Optimizely::Event)) project_instance.track('test_event_with_audience', 'test_user', 'browser_type' => 'firefox') - expect(project_instance.event_dispatcher).to have_received(:dispatch_event).with(log_url, params).once + expect(project_instance.event_dispatcher).to have_received(:dispatch_event).with(Optimizely::Event.new(:get, log_url, params)).once end it 'should not call dispatch_event when tracking an event for which audience conditions do not match' do @@ -258,7 +258,7 @@ class InvalidErrorHandler; end 'time' => time_now.strftime('%s').to_i } - allow(project_instance.event_dispatcher).to receive(:dispatch_event).with(log_url, params) + allow(project_instance.event_dispatcher).to receive(:dispatch_event).with(instance_of(Optimizely::Event)) project_instance.track('test_event', 'test_user', nil, 42) expect(spy_logger).to have_received(:log).once.with(Logger::INFO, include("Dispatching conversion event to" \ " URL #{log_url} with params"))