1
1
#
2
- # Copyright 2016, Optimizely and contributors
2
+ # Copyright 2016-2017 , Optimizely and contributors
3
3
#
4
4
# Licensed under the Apache License, Version 2.0 (the "License");
5
5
# you may not use this file except in compliance with the License.
@@ -37,11 +37,6 @@ class Project
37
37
attr_accessor :logger
38
38
attr_accessor :error_handler
39
39
40
- EVENT_BUILDERS_BY_VERSION = {
41
- Optimizely ::V1_CONFIG_VERSION => EventBuilderV1 ,
42
- Optimizely ::V2_CONFIG_VERSION => EventBuilderV2
43
- }
44
-
45
40
def initialize ( datafile , event_dispatcher = nil , logger = nil , error_handler = nil , skip_json_validation = false )
46
41
# Constructor for Projects.
47
42
#
@@ -58,7 +53,7 @@ def initialize(datafile, event_dispatcher = nil, logger = nil, error_handler = n
58
53
@event_dispatcher = event_dispatcher || EventDispatcher . new
59
54
60
55
begin
61
- validate_inputs ( datafile , skip_json_validation )
56
+ validate_instantiation_options ( datafile , skip_json_validation )
62
57
rescue InvalidInputError => e
63
58
@is_valid = false
64
59
logger = SimpleLogger . new
@@ -75,14 +70,15 @@ def initialize(datafile, event_dispatcher = nil, logger = nil, error_handler = n
75
70
return
76
71
end
77
72
78
- begin
79
- @bucketer = Bucketer . new ( @config )
80
- @event_builder = EVENT_BUILDERS_BY_VERSION [ @config . version ] . new ( @config , @bucketer )
81
- rescue
73
+ unless @config . parsing_succeeded?
82
74
@is_valid = false
83
75
logger = SimpleLogger . new
84
- logger . log ( Logger ::ERROR , InvalidDatafileVersionError . new )
76
+ logger . log ( Logger ::ERROR , InvalidDatafileVersionError . new . message )
77
+ return
85
78
end
79
+
80
+ @bucketer = Bucketer . new ( @config )
81
+ @event_builder = EventBuilderV2 . new ( @config )
86
82
end
87
83
88
84
def activate ( experiment_key , user_id , attributes = nil )
@@ -101,24 +97,15 @@ def activate(experiment_key, user_id, attributes = nil)
101
97
return nil
102
98
end
103
99
104
- if attributes && !attributes_valid? ( attributes )
105
- @logger . log ( Logger ::INFO , "Not activating user '#{ user_id } '." )
106
- return nil
107
- end
108
-
109
- unless preconditions_valid? ( experiment_key , user_id , attributes )
110
- @logger . log ( Logger ::INFO , "Not activating user '#{ user_id } '." )
111
- return nil
112
- end
113
-
114
- variation_id = @bucketer . bucket ( experiment_key , user_id )
100
+ variation_key = get_variation ( experiment_key , user_id , attributes )
115
101
116
- if not variation_id
102
+ if variation_key . nil?
117
103
@logger . log ( Logger ::INFO , "Not activating user '#{ user_id } '." )
118
104
return nil
119
105
end
120
106
121
107
# Create and dispatch impression event
108
+ variation_id = @config . get_variation_id_from_key ( experiment_key , variation_key )
122
109
impression_event = @event_builder . create_impression_event ( experiment_key , variation_id , user_id , attributes )
123
110
@logger . log ( Logger ::INFO ,
124
111
'Dispatching impression event to URL %s with params %s.' % [ impression_event . url ,
@@ -129,7 +116,7 @@ def activate(experiment_key, user_id, attributes = nil)
129
116
@logger . log ( Logger ::ERROR , "Unable to dispatch impression event. Error: #{ e } " )
130
117
end
131
118
132
- @config . get_variation_key_from_id ( experiment_key , variation_id )
119
+ variation_key
133
120
end
134
121
135
122
def get_variation ( experiment_key , user_id , attributes = nil )
@@ -148,24 +135,34 @@ def get_variation(experiment_key, user_id, attributes = nil)
148
135
return nil
149
136
end
150
137
151
- if attributes && ! attributes_valid? ( attributes )
138
+ unless preconditions_valid? ( experiment_key , attributes )
152
139
@logger . log ( Logger ::INFO , "Not activating user '#{ user_id } ." )
153
140
return nil
154
141
end
155
142
156
- unless preconditions_valid? ( experiment_key , user_id , attributes )
157
- @logger . log ( Logger ::INFO , "Not activating user '#{ user_id } ." )
143
+ variation_id = @bucketer . get_forced_variation_id ( experiment_key , user_id )
144
+
145
+ unless variation_id . nil?
146
+ return @config . get_variation_key_from_id ( experiment_key , variation_id )
147
+ end
148
+
149
+ unless Audience . user_in_experiment? ( @config , experiment_key , attributes )
150
+ @logger . log ( Logger ::INFO ,
151
+ "User '#{ user_id } ' does not meet the conditions to be in experiment '#{ experiment_key } '." )
158
152
return nil
159
153
end
160
154
161
155
variation_id = @bucketer . bucket ( experiment_key , user_id )
162
- @config . get_variation_key_from_id ( experiment_key , variation_id )
156
+ unless variation_id . nil?
157
+ return @config . get_variation_key_from_id ( experiment_key , variation_id )
158
+ end
159
+ nil
163
160
end
164
161
165
162
def track ( event_key , user_id , attributes = nil , event_tags = nil )
166
163
# Send conversion event to Optimizely.
167
164
#
168
- # event_key - Goal key representing the event which needs to be recorded.
165
+ # event_key - Event key representing the event which needs to be recorded.
169
166
# user_id - String ID for user.
170
167
# attributes - Hash representing visitor attributes and values which need to be recorded.
171
168
# event_tags - Hash representing metadata associated with the event.
@@ -183,34 +180,26 @@ def track(event_key, user_id, attributes = nil, event_tags = nil)
183
180
@logger . log ( Logger ::WARN , 'Event value is deprecated in track call. Use event tags to pass in revenue value instead.' )
184
181
end
185
182
186
- return nil if attributes && !attributes_valid? ( attributes )
187
- return nil if event_tags && !event_tags_valid? ( event_tags )
183
+ return nil unless user_inputs_valid? ( attributes , event_tags )
188
184
189
- experiment_ids = @config . get_experiment_ids_for_goal ( event_key )
185
+ experiment_ids = @config . get_experiment_ids_for_event ( event_key )
190
186
if experiment_ids . empty?
191
187
@config . logger . log ( Logger ::INFO , "Not tracking user '#{ user_id } '." )
192
188
return nil
193
189
end
194
190
195
191
# Filter out experiments that are not running or that do not include the user in audience conditions
196
- valid_experiment_keys = [ ]
197
- experiment_ids . each do |experiment_id |
198
- experiment_key = @config . experiment_id_map [ experiment_id ] [ 'key' ]
199
- unless preconditions_valid? ( experiment_key , user_id , attributes )
200
- @config . logger . log ( Logger ::INFO , "Not tracking user '#{ user_id } ' for experiment '#{ experiment_key } '." )
201
- next
202
- end
203
- valid_experiment_keys . push ( experiment_key )
204
- end
192
+
193
+ experiment_variation_map = get_valid_experiments_for_event ( event_key , user_id , attributes )
205
194
206
195
# Don't track events without valid experiments attached
207
- if valid_experiment_keys . empty?
196
+ if experiment_variation_map . empty?
208
197
@logger . log ( Logger ::INFO , "There are no valid experiments for event '#{ event_key } ' to track." )
209
198
return nil
210
199
end
211
200
212
201
conversion_event = @event_builder . create_conversion_event ( event_key , user_id , attributes ,
213
- event_tags , valid_experiment_keys )
202
+ event_tags , experiment_variation_map )
214
203
@logger . log ( Logger ::INFO ,
215
204
'Dispatching conversion event to URL %s with params %s.' % [ conversion_event . url ,
216
205
conversion_event . params ] )
@@ -223,7 +212,35 @@ def track(event_key, user_id, attributes = nil, event_tags = nil)
223
212
224
213
private
225
214
226
- def preconditions_valid? ( experiment_key , user_id , attributes )
215
+ def get_valid_experiments_for_event ( event_key , user_id , attributes )
216
+ # Get the experiments that we should be tracking for the given event.
217
+ #
218
+ # event_key - Event key representing the event which needs to be recorded.
219
+ # user_id - String ID for user.
220
+ # attributes - Map of attributes of the user.
221
+ #
222
+ # Returns Map where each object contains the ID of the experiment to track and the ID of the variation the user
223
+ # is bucketed into.
224
+
225
+ valid_experiments = { }
226
+ experiment_ids = @config . get_experiment_ids_for_event ( event_key )
227
+ experiment_ids . each do |experiment_id |
228
+ experiment_key = @config . get_experiment_key ( experiment_id )
229
+ variation_key = get_variation ( experiment_key , user_id , attributes )
230
+
231
+ if variation_key . nil?
232
+ @logger . log ( Logger ::INFO , "Not tracking user '#{ user_id } ' for experiment '#{ experiment_key } '." )
233
+ next
234
+ end
235
+
236
+ variation_id = @config . get_variation_id_from_key ( experiment_key , variation_key )
237
+ valid_experiments [ experiment_id ] = variation_id
238
+ end
239
+
240
+ valid_experiments
241
+ end
242
+
243
+ def preconditions_valid? ( experiment_key , attributes = nil , event_tags = nil )
227
244
# Validates preconditions for bucketing a user.
228
245
#
229
246
# experiment_key - String key for an experiment.
@@ -232,18 +249,29 @@ def preconditions_valid?(experiment_key, user_id, attributes)
232
249
#
233
250
# Returns boolean representing whether all preconditions are valid.
234
251
252
+ return false unless user_inputs_valid? ( attributes , event_tags )
253
+
235
254
unless @config . experiment_running? ( experiment_key )
236
255
@logger . log ( Logger ::INFO , "Experiment '#{ experiment_key } ' is not running." )
237
256
return false
238
257
end
239
258
240
- if @config . user_in_forced_variation? ( experiment_key , user_id )
241
- return true
259
+ true
260
+ end
261
+
262
+ def user_inputs_valid? ( attributes = nil , event_tags = nil )
263
+ # Helper method to validate user inputs.
264
+ #
265
+ # attributes - Dict representing user attributes.
266
+ # event_tags - Dict representing metadata associated with an event.
267
+ #
268
+ # Returns boolean True if inputs are valid. False otherwise.
269
+
270
+ if !attributes . nil? && !attributes_valid? ( attributes )
271
+ return false
242
272
end
243
273
244
- unless Audience . user_in_experiment? ( @config , experiment_key , attributes )
245
- @logger . log ( Logger ::INFO ,
246
- "User '#{ user_id } ' does not meet the conditions to be in experiment '#{ experiment_key } '." )
274
+ if !event_tags . nil? && !event_tags_valid? ( event_tags )
247
275
return false
248
276
end
249
277
@@ -268,7 +296,7 @@ def event_tags_valid?(event_tags)
268
296
true
269
297
end
270
298
271
- def validate_inputs ( datafile , skip_json_validation )
299
+ def validate_instantiation_options ( datafile , skip_json_validation )
272
300
unless skip_json_validation
273
301
raise InvalidInputError . new ( 'datafile' ) unless Helpers ::Validator . datafile_valid? ( datafile )
274
302
end
0 commit comments