Skip to content

Commit 757a100

Browse files
authored
Introduce event tags (#30)
This introduces event tags to the API. Event tags are included as eventFeatures in the event payload.
1 parent a9ce71e commit 757a100

File tree

8 files changed

+468
-39
lines changed

8 files changed

+468
-39
lines changed

lib/optimizely.rb

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,21 +162,29 @@ def get_variation(experiment_key, user_id, attributes = nil)
162162
@config.get_variation_key_from_id(experiment_key, variation_id)
163163
end
164164

165-
def track(event_key, user_id, attributes = nil, event_value = nil)
165+
def track(event_key, user_id, attributes = nil, event_tags = nil)
166166
# Send conversion event to Optimizely.
167167
#
168168
# event_key - Goal key representing the event which needs to be recorded.
169169
# user_id - String ID for user.
170170
# attributes - Hash representing visitor attributes and values which need to be recorded.
171-
# event_value - Value associated with the event. Can be used to represent revenue in cents.
171+
# event_tags - Hash representing metadata associated with the event.
172172

173173
unless @is_valid
174174
logger = SimpleLogger.new
175175
logger.log(Logger::ERROR, InvalidDatafileError.new('track').message)
176176
return nil
177177
end
178178

179+
if event_tags and event_tags.is_a? Numeric
180+
event_tags = {
181+
'revenue' => event_tags
182+
}
183+
@logger.log(Logger::WARN, 'Event value is deprecated in track call. Use event tags to pass in revenue value instead.')
184+
end
185+
179186
return nil if attributes && !attributes_valid?(attributes)
187+
return nil if event_tags && !event_tags_valid?(event_tags)
180188

181189
experiment_ids = @config.get_experiment_ids_for_goal(event_key)
182190
if experiment_ids.empty?
@@ -202,7 +210,7 @@ def track(event_key, user_id, attributes = nil, event_value = nil)
202210
end
203211

204212
conversion_event = @event_builder.create_conversion_event(event_key, user_id, attributes,
205-
event_value, valid_experiment_keys)
213+
event_tags, valid_experiment_keys)
206214
@logger.log(Logger::INFO,
207215
'Dispatching conversion event to URL %s with params %s.' % [conversion_event.url,
208216
conversion_event.params])
@@ -251,6 +259,15 @@ def attributes_valid?(attributes)
251259
true
252260
end
253261

262+
def event_tags_valid?(event_tags)
263+
unless Helpers::Validator.event_tags_valid?(event_tags)
264+
@logger.log(Logger::ERROR, 'Provided event tags are in an invalid format.')
265+
@error_handler.handle_error(InvalidEventTagFormatError)
266+
return false
267+
end
268+
true
269+
end
270+
254271
def validate_inputs(datafile, skip_json_validation)
255272
unless skip_json_validation
256273
raise InvalidInputError.new('datafile') unless Helpers::Validator.datafile_valid?(datafile)

lib/optimizely/event_builder.rb

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
require_relative './audience'
1717
require_relative './params'
1818
require_relative './version'
19+
require_relative '../optimizely/helpers/event_tag_utils'
1920

2021
module Optimizely
2122
class Event
@@ -89,20 +90,21 @@ def create_impression_event(experiment_key, variation_id, user_id, attributes)
8990
Event.new(:post, IMPRESSION_EVENT_ENDPOINT, @params, POST_HEADERS)
9091
end
9192

92-
def create_conversion_event(event_key, user_id, attributes, event_value, experiment_keys)
93+
def create_conversion_event(event_key, user_id, attributes, event_tags, experiment_keys)
9394
# Create conversion Event to be sent to the logging endpoint.
9495
#
9596
# event_key - Event key representing the event which needs to be recorded.
9697
# user_id - ID for user.
9798
# attributes - Hash representing user attributes and values which need to be recorded.
98-
# event_value - Value associated with the event. Can be used to represent revenue in cents.
99+
# event_tags - Hash representing metadata associated with the event.
99100
# experiment_keys - Array of valid experiment keys for the event
100101
#
101102
# Returns event hash encapsulating the conversion event.
102103

103104
@params = {}
104105
add_common_params(user_id, attributes)
105-
add_conversion_event(event_key, event_value)
106+
add_conversion_event(event_key)
107+
add_event_tags(event_tags)
106108
add_layer_states(user_id, experiment_keys)
107109
Event.new(:post, CONVERSION_EVENT_ENDPOINT, @params, POST_HEADERS)
108110
end
@@ -161,26 +163,47 @@ def add_decision(experiment_key, variation_id)
161163
}
162164
end
163165

164-
def add_conversion_event(event_key, event_value)
166+
def add_event_tags(event_tags)
167+
@params['eventFeatures'] ||= []
168+
@params['eventMetrics'] ||= []
169+
170+
return if event_tags.nil?
171+
172+
event_tags.each_pair do |event_tag_key, event_tag_value|
173+
next if event_tag_value.nil?
174+
175+
event_feature = {
176+
'id' => event_tag_key,
177+
'type' => 'custom',
178+
'value' => event_tag_value,
179+
'shouldIndex' => false,
180+
}
181+
@params['eventFeatures'].push(event_feature)
182+
183+
end
184+
185+
event_value = Helpers::EventTagUtils.get_revenue_value(event_tags)
186+
187+
if event_value
188+
event_metric = {
189+
'name' => 'revenue',
190+
'value' => event_value
191+
}
192+
@params['eventMetrics'].push(event_metric)
193+
end
194+
195+
end
196+
197+
def add_conversion_event(event_key)
165198
# Add conversion event information to the event.
166199
#
167200
# event_key - Event key representing the event which needs to be recorded.
168-
# event_value - Value associated with the event. Can be used to represent revenue in cents.
169201

170202
event_id = @config.event_key_map[event_key]['id']
171203
event_name = @config.event_key_map[event_key]['key']
172204

173205
@params['eventEntityId'] = event_id
174-
@params['eventFeatures'] = []
175206
@params['eventName'] = event_name
176-
@params['eventMetrics'] = []
177-
178-
if event_value
179-
@params['eventMetrics'].push({
180-
'name' => 'revenue',
181-
'value' => event_value,
182-
})
183-
end
184207
end
185208

186209
def add_layer_states(user_id, experiment_keys)
@@ -241,18 +264,21 @@ def create_impression_event(experiment_key, variation_id, user_id, attributes)
241264
Event.new(:get, sprintf(OFFLINE_API_PATH, project_id: @params[Params::PROJECT_ID]), @params, {})
242265
end
243266

244-
def create_conversion_event(event_key, user_id, attributes, event_value, experiment_keys)
267+
def create_conversion_event(event_key, user_id, attributes, event_tags, experiment_keys)
245268
# Create conversion Event to be sent to the logging endpoint.
246269
#
247270
# event_key - Goal key representing the event which needs to be recorded.
248271
# user_id - ID for user.
249272
# attributes - Hash representing user attributes and values which need to be recorded.
250-
# event_value - Value associated with the event. Can be used to represent revenue in cents.
273+
# event_tags - Hash representing metadata associated with the event.
251274
# experiment_keys - Array of valid experiment keys for the goal
252275
#
253276
# Returns event hash encapsulating the conversion event.
254277

255278
@params = {}
279+
280+
event_value = Helpers::EventTagUtils.get_revenue_value(event_tags)
281+
256282
add_common_params(user_id, attributes)
257283
add_conversion_goal(event_key, event_value)
258284
add_experiment_variation_params(user_id, experiment_keys)

lib/optimizely/exceptions.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ def initialize(msg = 'Attributes provided are in an invalid format.')
4040
end
4141
end
4242

43+
class InvalidEventTagFormatError < Error
44+
# Raised when attributes are provided in an invalid format (e.g. not a Hash)
45+
46+
def initialize(msg = 'Event tags provided are in an invalid format.')
47+
super
48+
end
49+
end
50+
4351
class InvalidExperimentError < Error
4452
# Raised when an invalid experiment key is provided
4553

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#
2+
# Copyright 2017, Optimizely and contributors
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
require 'optimizely/logger'
17+
require 'optimizely/helpers/validator'
18+
19+
module Optimizely
20+
module Helpers
21+
module EventTagUtils
22+
module_function
23+
24+
def get_revenue_value(event_tags)
25+
# Grab the revenue value from the event tags. "revenue" is a reserved keyword.
26+
#
27+
# event_tags - Hash representing metadata associated with the event.
28+
# Returns revenue value as an integer number
29+
# Returns nil if revenue can't be retrieved from the event tags.
30+
31+
if event_tags.nil? or !Helpers::Validator.attributes_valid?(event_tags)
32+
return nil
33+
end
34+
35+
unless event_tags.has_key?('revenue')
36+
return nil
37+
end
38+
39+
logger = SimpleLogger.new
40+
raw_value = event_tags['revenue']
41+
42+
unless raw_value.is_a? Numeric
43+
logger.log(Logger::WARN, "Failed to parse revenue value #{raw_value} from event tags.")
44+
return nil
45+
end
46+
47+
if raw_value.is_a? Float
48+
logger.log(Logger::WARN, "Failed to parse revenue value #{raw_value} from event tags.")
49+
return nil
50+
end
51+
52+
logger.log(Logger::INFO, "Parsed revenue value #{raw_value} from event tags.")
53+
raw_value
54+
end
55+
end
56+
end
57+
end

lib/optimizely/helpers/validator.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ def attributes_valid?(attributes)
3232
attributes.is_a?(Hash)
3333
end
3434

35+
def event_tags_valid?(event_tags)
36+
# Determines if provided event tags are valid.
37+
#
38+
# event_tags - Event tags to be validated.
39+
#
40+
# Returns boolean depending on validity of event tags.
41+
42+
event_tags.is_a?(Hash)
43+
end
44+
3545
def datafile_valid?(datafile)
3646
# Determines if a given datafile is valid.
3747
#

0 commit comments

Comments
 (0)