-
Notifications
You must be signed in to change notification settings - Fork 27
Add method and URL to Event, add method to EventDispatcher #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
cc9eab2
a58a4f2
440e9e3
b8a7dac
4b299b3
a93f803
f28c165
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,28 +6,18 @@ 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 :method | ||
|
||
| attr_reader :params | ||
| attr_reader :url | ||
|
|
||
| def initialize(params) | ||
| def initialize(method, url, params) | ||
| @method = method | ||
| @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]) | ||
| end | ||
| end | ||
|
|
||
| class EventBuilder | ||
| class EventBuilderV1 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of creating two different event builder classes, why don't we have one event builder that can build V1 events and V2 events. That way you don't have to worry about handling the instantiation of two different builder classes.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because when we eventually remove V1 support it will be easier to simply remove the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I disagree because when we rip out V1 support, we would just be changing the internals of this class and the API references. If we keep two classes around there will also be some forking logic throughout the codebase that we need to kill. Providing the event builder as some sort of facade/factory will make it much easier to deal with.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it is way better to pick the right EventBuilder at object invocation and not have logic all over. The model here allows that.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about this, since the |
||
| # Class which encapsulates methods to build events for tracking impressions and conversions. | ||
|
|
||
| # Attribute mapping format | ||
|
|
@@ -36,8 +26,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 +53,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 +69,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 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,33 +1,30 @@ | ||
| 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(_method, _url, _params) | ||
| end | ||
| end | ||
|
|
||
| class EventDispatcher < BaseEventDispatcher | ||
| class EventDispatcher | ||
| REQUEST_TIMEOUT = 10 | ||
|
|
||
| def dispatch_event(url, params) | ||
| def dispatch_event(method, url, params) | ||
|
||
| # Dispatch the event being represented by the Event object. | ||
| # | ||
| # method - HTTP verb with which to send the event. | ||
| # url - URL to send impression/conversion event to. | ||
| # params - Params to be sent to the impression/conversion event. | ||
|
|
||
| HTTParty.get(url, query: params, timeout: REQUEST_TIMEOUT) | ||
| rescue Timeout::Error => e | ||
| return e | ||
| if method == :get | ||
| begin | ||
| HTTParty.get(url, query: params, timeout: REQUEST_TIMEOUT) | ||
| rescue Timeout::Error => e | ||
| return e | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for completeness, we should also have a test case to check that the event is built with the correct method and url |
||
| 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 | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is more of a design thing, but semantically speaking, when you say dispatch_event, it makes more sense to pass in the event object instead of spreading its props and passing them in individually. As we evolve the SDK I can see this method signature becoming too long/unbearable. Might as well make the refactor here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, was going to refactor to pass around
Events in a later PR but will do so here.