optimizely-sdk 1.1.2 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c74599de62f5c8f5f5dee2484384c5dc7c3de7e8
4
- data.tar.gz: 53319f3eb1ab45bab9b609f8e901d5a78ecfb009
3
+ metadata.gz: c8eff682f4c4c99808f246f2828a4e34e276e54b
4
+ data.tar.gz: 1c4de8ae37ce421199a1d66ea67c5e9ba78e1c41
5
5
  SHA512:
6
- metadata.gz: b90f7312495293123be649f29d14fdcf6f4497e3094b160ae7d424bd1e3807a0775971d47473e0cf9a835b48cd7a65ac9b7a22e10670e6c72ae82751e8e1043a
7
- data.tar.gz: 478ce61817912292b29d2d9b08e6de617e30f400ef8d5e44f707036692f7d00acb96d409bc52dd0a6d323d15ba6d0c5cd056baab7ad93e439574bec8058b7709
6
+ metadata.gz: 32ad714501f8b55a48201653de322c2f47e33634d323bb653909307cfc289ceff833ba44508b823ecfe0a4ade94cae2aac8ecca5bd2d6ee350d1ec82977a2a69
7
+ data.tar.gz: 96297561d1ee0a395d35f8b9878ac71b2c097583442bf7327b01db773ef13b32a5aa223c288beeb10f1da75ff4d713baf511286a150bf5a38cd5ea5e5bf2c1cd
data/lib/optimizely.rb CHANGED
@@ -1,5 +1,5 @@
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 @@ module Optimizely
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 @@ module Optimizely
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 @@ module Optimizely
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 @@ module Optimizely
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 @@ module Optimizely
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 @@ module Optimizely
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 @@ module Optimizely
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 @@ module Optimizely
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 @@ module Optimizely
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 @@ module Optimizely
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
@@ -1,5 +1,5 @@
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.
@@ -43,23 +43,6 @@ module Optimizely
43
43
  #
44
44
  # Returns String variation ID in which visitor with ID user_id has been placed. Nil if no variation.
45
45
 
46
- # handle forced variations if applicable
47
- forced_variations = @config.get_forced_variations(experiment_key)
48
- forced_variation_key = forced_variations[user_id]
49
- if forced_variation_key
50
- forced_variation_id = @config.get_variation_id_from_key(experiment_key, forced_variation_key)
51
- if forced_variation_id
52
- @config.logger.log(Logger::INFO, "User '#{user_id}' is forced in variation '#{forced_variation_key}'.")
53
- return forced_variation_id
54
- else
55
- @config.logger.log(
56
- Logger::INFO,
57
- "Variation key '#{forced_variation_key}' is not in datafile. Not activating user '#{user_id}'."
58
- )
59
- return nil
60
- end
61
- end
62
-
63
46
  # check if experiment is in a group; if so, check if user is bucketed into specified experiment
64
47
  experiment_id = @config.get_experiment_id(experiment_key)
65
48
  group_id = @config.get_experiment_group_id(experiment_key)
@@ -118,6 +101,36 @@ module Optimizely
118
101
  nil
119
102
  end
120
103
 
104
+ def get_forced_variation_id(experiment_key, user_id)
105
+ # Determine if a user is forced into a variation for the given experiment and return the id of that variation.
106
+ #
107
+ # experiment_key - Key representing the experiment for which user is to be bucketed.
108
+ # user_id - ID for the user.
109
+ #
110
+ # Returns variation ID in which the user with ID user_id is forced into. Nil if no variation.
111
+
112
+ forced_variations = @config.get_forced_variations(experiment_key)
113
+
114
+ return nil unless forced_variations
115
+
116
+ forced_variation_key = forced_variations[user_id]
117
+
118
+ return nil unless forced_variation_key
119
+
120
+ forced_variation_id = @config.get_variation_id_from_key(experiment_key, forced_variation_key)
121
+
122
+ unless forced_variation_id
123
+ @config.logger.log(
124
+ Logger::INFO,
125
+ "Variation key '#{forced_variation_key}' is not in datafile. Not activating user '#{user_id}'."
126
+ )
127
+ return nil
128
+ end
129
+
130
+ @config.logger.log(Logger::INFO, "User '#{user_id}' is forced in variation '#{forced_variation_key}'.")
131
+ forced_variation_id
132
+ end
133
+
121
134
  private
122
135
 
123
136
  def find_bucket(bucket_value, traffic_allocations)
@@ -42,12 +42,10 @@ module Optimizely
42
42
 
43
43
  class BaseEventBuilder
44
44
  attr_reader :config
45
- attr_reader :bucketer
46
45
  attr_accessor :params
47
46
 
48
- def initialize(config, bucketer)
47
+ def initialize(config)
49
48
  @config = config
50
- @bucketer = bucketer
51
49
  @params = {}
52
50
  end
53
51
 
@@ -90,14 +88,14 @@ module Optimizely
90
88
  Event.new(:post, IMPRESSION_EVENT_ENDPOINT, @params, POST_HEADERS)
91
89
  end
92
90
 
93
- def create_conversion_event(event_key, user_id, attributes, event_tags, experiment_keys)
91
+ def create_conversion_event(event_key, user_id, attributes, event_tags, experiment_variation_map)
94
92
  # Create conversion Event to be sent to the logging endpoint.
95
93
  #
96
94
  # event_key - Event key representing the event which needs to be recorded.
97
95
  # user_id - ID for user.
98
96
  # attributes - Hash representing user attributes and values which need to be recorded.
99
97
  # event_tags - Hash representing metadata associated with the event.
100
- # experiment_keys - Array of valid experiment keys for the event
98
+ # experiment_variation_map - Map of experiment ID to the ID of the variation that the user is bucketed into.
101
99
  #
102
100
  # Returns event hash encapsulating the conversion event.
103
101
 
@@ -105,7 +103,7 @@ module Optimizely
105
103
  add_common_params(user_id, attributes)
106
104
  add_conversion_event(event_key)
107
105
  add_event_tags(event_tags)
108
- add_layer_states(user_id, experiment_keys)
106
+ add_layer_states(experiment_variation_map)
109
107
  Event.new(:post, CONVERSION_EVENT_ENDPOINT, @params, POST_HEADERS)
110
108
  end
111
109
 
@@ -206,14 +204,16 @@ module Optimizely
206
204
  @params['eventName'] = event_name
207
205
  end
208
206
 
209
- def add_layer_states(user_id, experiment_keys)
207
+ def add_layer_states(experiments_map)
208
+ # Add layer states information to the event.
209
+ #
210
+ # experiments_map - Hash with experiment ID as a key and variation ID as a value.
211
+
210
212
  @params['layerStates'] = []
211
213
 
212
- experiment_keys.each do |experiment_key|
213
- variation_id = @bucketer.bucket(experiment_key, user_id)
214
- experiment_id = @config.experiment_key_map[experiment_key]['id']
214
+ experiments_map.each do |experiment_id, variation_id|
215
215
  layer_state = {
216
- 'layerId' => @config.experiment_key_map[experiment_key]['layerId'],
216
+ 'layerId' => @config.experiment_id_map[experiment_id]['layerId'],
217
217
  'decision' => {
218
218
  'variationId' => variation_id,
219
219
  'experimentId' => experiment_id,
@@ -234,162 +234,4 @@ module Optimizely
234
234
  @params['timestamp'] = (Time.now.to_f * 1000).to_i
235
235
  end
236
236
  end
237
-
238
- class EventBuilderV1 < BaseEventBuilder
239
- # Class which encapsulates methods to build events for tracking impressions and conversions.
240
-
241
- # Attribute mapping format
242
- ATTRIBUTE_PARAM_FORMAT = '%{segment_prefix}%{segment_id}'
243
-
244
- # Experiment mapping format
245
- EXPERIMENT_PARAM_FORMAT = '%{experiment_prefix}%{experiment_id}'
246
-
247
- # Event endpoint path
248
- OFFLINE_API_PATH = 'https://%{project_id}.log.optimizely.com/event'
249
-
250
- def create_impression_event(experiment_key, variation_id, user_id, attributes)
251
- # Create conversion Event to be sent to the logging endpoint.
252
- #
253
- # experiment_key - Experiment for which impression needs to be recorded.
254
- # variation_id - ID for variation which would be presented to user.
255
- # user_id - ID for user.
256
- # attributes - Hash representing user attributes and values which need to be recorded.
257
- #
258
- # Returns event hash encapsulating the impression event.
259
-
260
- @params = {}
261
- add_common_params(user_id, attributes)
262
- add_impression_goal(experiment_key)
263
- add_experiment(experiment_key, variation_id)
264
- Event.new(:get, sprintf(OFFLINE_API_PATH, project_id: @params[Params::PROJECT_ID]), @params, {})
265
- end
266
-
267
- def create_conversion_event(event_key, user_id, attributes, event_tags, experiment_keys)
268
- # Create conversion Event to be sent to the logging endpoint.
269
- #
270
- # event_key - Goal key representing the event which needs to be recorded.
271
- # user_id - ID for user.
272
- # attributes - Hash representing user attributes and values which need to be recorded.
273
- # event_tags - Hash representing metadata associated with the event.
274
- # experiment_keys - Array of valid experiment keys for the goal
275
- #
276
- # Returns event hash encapsulating the conversion event.
277
-
278
- @params = {}
279
-
280
- event_value = Helpers::EventTagUtils.get_revenue_value(event_tags)
281
-
282
- add_common_params(user_id, attributes)
283
- add_conversion_goal(event_key, event_value)
284
- add_experiment_variation_params(user_id, experiment_keys)
285
- Event.new(:get, sprintf(OFFLINE_API_PATH, project_id: @params[Params::PROJECT_ID]), @params, {})
286
- end
287
-
288
- private
289
-
290
- def add_project_id
291
- # Add project ID to the event.
292
-
293
- @params[Params::PROJECT_ID] = @config.project_id
294
- end
295
-
296
- def add_account_id
297
- # Add account ID to the event.
298
-
299
- @params[Params::ACCOUNT_ID] = @config.account_id
300
- end
301
-
302
- def add_user_id(user_id)
303
- # Add user ID to the event.
304
-
305
- @params[Params::END_USER_ID] = user_id
306
- end
307
-
308
- def add_attributes(attributes)
309
- # Add attribute(s) information to the event.
310
- #
311
- # attributes - Hash representing user attributes and values which need to be recorded.
312
-
313
- return if attributes.nil?
314
-
315
- attributes.keys.each do |attribute_key|
316
- attribute_value = attributes[attribute_key]
317
- next unless attribute_value
318
-
319
- # Skip attributes not in the datafile
320
- segment_id = @config.get_segment_id(attribute_key)
321
- next unless segment_id
322
- segment_param = sprintf(ATTRIBUTE_PARAM_FORMAT,
323
- segment_prefix: Params::SEGMENT_PREFIX, segment_id: segment_id)
324
- params[segment_param] = attribute_value
325
- end
326
- end
327
-
328
- def add_source
329
- # Add source information to the event.
330
-
331
- @params[Params::SOURCE] = sprintf('ruby-sdk-%{version}', version: VERSION)
332
- end
333
-
334
- def add_time
335
- # Add time information to the event.
336
-
337
- @params[Params::TIME] = Time.now.strftime('%s').to_i
338
- end
339
-
340
- def add_impression_goal(experiment_key)
341
- # Add impression goal information to the event.
342
- #
343
- # experiment_key - Experiment which is being activated.
344
-
345
- # For tracking impressions, goal ID is set equal to experiment ID of experiment being activated.
346
- @params[Params::GOAL_ID] = @config.get_experiment_id(experiment_key)
347
- @params[Params::GOAL_NAME] = 'visitor-event'
348
- end
349
-
350
- def add_experiment(experiment_key, variation_id)
351
- # Add experiment to variation mapping to the impression event.
352
- #
353
- # experiment_key - Experiment which is being activated.
354
- # variation_id - ID for variation which would be presented to user.
355
-
356
- experiment_id = @config.get_experiment_id(experiment_key)
357
- experiment_param = sprintf(EXPERIMENT_PARAM_FORMAT,
358
- experiment_prefix: Params::EXPERIMENT_PREFIX, experiment_id: experiment_id)
359
- @params[experiment_param] = variation_id
360
- end
361
-
362
- def add_experiment_variation_params(user_id, experiment_keys)
363
- # Maps experiment and corresponding variation as parameters to be used in the event tracking call.
364
- #
365
- # user_id - ID for user.
366
- # experiment_keys - Array of valid experiment keys for the goal
367
-
368
- experiment_keys.each do |experiment_key|
369
- variation_id = @bucketer.bucket(experiment_key, user_id)
370
- experiment_id = @config.experiment_key_map[experiment_key]['id']
371
- experiment_param = sprintf(EXPERIMENT_PARAM_FORMAT,
372
- experiment_prefix: Params::EXPERIMENT_PREFIX, experiment_id: experiment_id)
373
- @params[experiment_param] = variation_id
374
- end
375
- end
376
-
377
- def add_conversion_goal(event_key, event_value)
378
- # Add conversion goal information to the event.
379
- #
380
- # event_key - Goal key representing the event which needs to be recorded.
381
- # event_value - Value associated with the event. Can be used to represent revenue in cents.
382
-
383
- goal_id = @config.event_key_map[event_key]['id']
384
- event_ids = goal_id
385
-
386
- if event_value
387
- event_ids = sprintf('%{goal_id},%{revenue_id}', goal_id: goal_id, revenue_id: @config.get_revenue_goal_id)
388
- @params[Params::EVENT_VALUE] = event_value
389
- end
390
-
391
- @params[Params::GOAL_ID] = event_ids
392
- @params[Params::GOAL_NAME] = event_key
393
- end
394
- end
395
237
  end
@@ -1,5 +1,5 @@
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.
@@ -83,7 +83,7 @@ module Optimizely
83
83
  class InvalidDatafileVersionError < Error
84
84
  # Raised when a datafile with an unsupported version is provided
85
85
 
86
- def initialize(msg = 'Provided datafile is an unsupported version.')
86
+ def initialize(msg = 'Provided datafile is an unsupported version. Please use SDK version 1.1.2 or earlier for datafile version 1.')
87
87
  super
88
88
  end
89
89
  end
@@ -1,5 +1,5 @@
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.
@@ -16,283 +16,6 @@
16
16
  module Optimizely
17
17
  module Helpers
18
18
  module Constants
19
- JSON_SCHEMA_V1 = {
20
- 'type' => 'object',
21
- 'properties' => {
22
- 'projectId' => {
23
- 'type' => 'string'
24
- },
25
- 'accountId' => {
26
- 'type' => 'string'
27
- },
28
- 'groups' => {
29
- 'type' => 'array',
30
- 'items' => {
31
- 'type' => 'object',
32
- 'properties' => {
33
- 'id' => {
34
- 'type' => 'string'
35
- },
36
- 'policy' => {
37
- 'type' => 'string'
38
- },
39
- 'trafficAllocation' => {
40
- 'type' => 'array',
41
- 'items' => {
42
- 'type' => 'object',
43
- 'properties' => {
44
- 'entityId' => {
45
- 'type' => 'string'
46
- },
47
- 'endOfRange' => {
48
- 'type' => 'integer'
49
- }
50
- },
51
- 'required' => [
52
- 'entityId',
53
- 'endOfRange'
54
- ]
55
- }
56
- },
57
- 'experiments' => {
58
- 'type' => 'array',
59
- 'items' => {
60
- 'type' => 'object',
61
- 'properties' => {
62
- 'id' => {
63
- 'type' => 'string'
64
- },
65
- 'key' => {
66
- 'type' => 'string'
67
- },
68
- 'status' => {
69
- 'type' => 'string'
70
- },
71
- 'variations' => {
72
- 'type' => 'array',
73
- 'items' => {
74
- 'type' => 'object',
75
- 'properties' => {
76
- 'id' => {
77
- 'type' => 'string'
78
- },
79
- 'key' => {
80
- 'type' => 'string'
81
- }
82
- },
83
- 'required' => [
84
- 'id',
85
- 'key'
86
- ]
87
- }
88
- },
89
- 'trafficAllocation' => {
90
- 'type' => 'array',
91
- 'items' => {
92
- 'type' => 'object',
93
- 'properties' => {
94
- 'entityId' => {
95
- 'type' => 'string'
96
- },
97
- 'endOfRange' => {
98
- 'type' => 'integer'
99
- }
100
- },
101
- 'required' => [
102
- 'entityId',
103
- 'endOfRange'
104
- ]
105
- }
106
- },
107
- 'audienceIds' => {
108
- 'type' => 'array',
109
- 'items' => {
110
- 'type' => 'string'
111
- }
112
- },
113
- 'forcedVariations' => {
114
- 'type' => 'object'
115
- }
116
- },
117
- 'required' => [
118
- 'id',
119
- 'key',
120
- 'status',
121
- 'variations',
122
- 'trafficAllocation',
123
- 'audienceIds',
124
- 'forcedVariations'
125
- ]
126
- }
127
- }
128
- },
129
- 'required' => [
130
- 'id',
131
- 'policy',
132
- 'trafficAllocation',
133
- 'experiments'
134
- ]
135
- }
136
- },
137
- 'experiments' => {
138
- 'type' => 'array',
139
- 'items' => {
140
- 'type' => 'object',
141
- 'properties' => {
142
- 'id' => {
143
- 'type' => 'string'
144
- },
145
- 'key' => {
146
- 'type' => 'string'
147
- },
148
- 'status' => {
149
- 'type' => 'string'
150
- },
151
- 'variations' => {
152
- 'type' => 'array',
153
- 'items' => {
154
- 'type' => 'object',
155
- 'properties' => {
156
- 'id' => {
157
- 'type' => 'string'
158
- },
159
- 'key' => {
160
- 'type' => 'string'
161
- }
162
- },
163
- 'required' => [
164
- 'id',
165
- 'key'
166
- ]
167
- }
168
- },
169
- 'trafficAllocation' => {
170
- 'type' => 'array',
171
- 'items' => {
172
- 'type' => 'object',
173
- 'properties' => {
174
- 'entityId' => {
175
- 'type' => 'string'
176
- },
177
- 'endOfRange' => {
178
- 'type' => 'integer'
179
- }
180
- },
181
- 'required' => [
182
- 'entityId',
183
- 'endOfRange'
184
- ]
185
- }
186
- },
187
- 'audienceIds' => {
188
- 'type' => 'array',
189
- 'items' => {
190
- 'type' => 'string'
191
- }
192
- },
193
- 'forcedVariations' => {
194
- 'type' => 'object'
195
- }
196
- },
197
- 'required' => [
198
- 'id',
199
- 'key',
200
- 'variations',
201
- 'trafficAllocation',
202
- 'audienceIds',
203
- 'forcedVariations',
204
- 'status',
205
- ]
206
- }
207
- },
208
- 'events' => {
209
- 'type' => 'array',
210
- 'items' => {
211
- 'type' => 'object',
212
- 'properties' => {
213
- 'key' => {
214
- 'type' => 'string'
215
- },
216
- 'experimentIds' => {
217
- 'type' => 'array',
218
- 'items' => {
219
- 'type' => 'string'
220
- }
221
- },
222
- 'id' => {
223
- 'type' => 'string'
224
- }
225
- },
226
- 'required' => [
227
- 'key',
228
- 'experimentIds',
229
- 'id'
230
- ]
231
- }
232
- },
233
- 'audiences' => {
234
- 'type' => 'array',
235
- 'items' => {
236
- 'type' => 'object',
237
- 'properties' => {
238
- 'id' => {
239
- 'type' => 'string'
240
- },
241
- 'name' => {
242
- 'type' => 'string'
243
- },
244
- 'conditions' => {
245
- 'type' => 'string'
246
- }
247
- },
248
- 'required' => [
249
- 'id',
250
- 'name',
251
- 'conditions'
252
- ]
253
- }
254
- },
255
- 'dimensions' => {
256
- 'type' => 'array',
257
- 'items' => {
258
- 'type' => 'object',
259
- 'properties' => {
260
- 'id' => {
261
- 'type' => 'string'
262
- },
263
- 'key' => {
264
- 'type' => 'string'
265
- },
266
- 'segmentId' => {
267
- 'type' => 'string'
268
- }
269
- },
270
- 'required' => [
271
- 'id',
272
- 'key',
273
- 'segmentId'
274
- ]
275
- }
276
- },
277
- 'version' => {
278
- 'type' => 'string'
279
- },
280
- 'revision' => {
281
- 'type' => 'string'
282
- }
283
- },
284
- 'required' => [
285
- 'projectId',
286
- 'accountId',
287
- 'experiments',
288
- 'events',
289
- 'groups',
290
- 'audiences',
291
- 'dimensions',
292
- 'version',
293
- 'revision'
294
- ]
295
- }
296
19
 
297
20
  JSON_SCHEMA_V2 = {
298
21
  'type' => 'object',
@@ -1,5 +1,5 @@
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.
@@ -55,13 +55,7 @@ module Optimizely
55
55
  return false
56
56
  end
57
57
 
58
- version = datafile['version']
59
-
60
- if version == Optimizely::V1_CONFIG_VERSION
61
- JSON::Validator.validate(Helpers::Constants::JSON_SCHEMA_V1, datafile)
62
- else
63
- JSON::Validator.validate(Helpers::Constants::JSON_SCHEMA_V2, datafile)
64
- end
58
+ JSON::Validator.validate(Helpers::Constants::JSON_SCHEMA_V2, datafile)
65
59
  end
66
60
 
67
61
  def error_handler_valid?(error_handler)
@@ -1,5 +1,5 @@
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.
@@ -20,6 +20,9 @@ module Optimizely
20
20
  V1_CONFIG_VERSION = '1'
21
21
  V2_CONFIG_VERSION = '2'
22
22
 
23
+ SUPPORTED_VERSIONS = [V2_CONFIG_VERSION]
24
+ UNSUPPORTED_VERSIONS = [V1_CONFIG_VERSION]
25
+
23
26
  class ProjectConfig
24
27
  # Representation of the Optimizely project config.
25
28
 
@@ -32,6 +35,7 @@ module Optimizely
32
35
  attr_reader :error_handler
33
36
  attr_reader :logger
34
37
 
38
+ attr_reader :parsing_succeeded
35
39
  attr_reader :version
36
40
  attr_reader :account_id
37
41
  attr_reader :project_id
@@ -59,16 +63,18 @@ module Optimizely
59
63
 
60
64
  config = JSON.load(datafile)
61
65
 
66
+ @parsing_succeeded = false
62
67
  @error_handler = error_handler
63
68
  @logger = logger
64
69
  @version = config['version']
70
+
71
+ if UNSUPPORTED_VERSIONS.include?(@version)
72
+ return
73
+ end
74
+
65
75
  @account_id = config['accountId']
66
76
  @project_id = config['projectId']
67
- if @version == V1_CONFIG_VERSION
68
- @attributes = config['dimensions']
69
- else
70
- @attributes = config['attributes']
71
- end
77
+ @attributes = config['attributes']
72
78
  @events = config['events']
73
79
  @experiments = config['experiments']
74
80
  @revision = config['revision']
@@ -95,6 +101,7 @@ module Optimizely
95
101
  @variation_id_map[key] = generate_key_map(variations, 'id')
96
102
  @variation_key_map[key] = generate_key_map(variations, 'key')
97
103
  end
104
+ @parsing_succeeded = true
98
105
  end
99
106
 
100
107
  def experiment_running?(experiment_key)
@@ -124,36 +131,30 @@ module Optimizely
124
131
  nil
125
132
  end
126
133
 
127
- def get_goal_keys
128
- # Retrieves all goals in the project except 'Total Revenue'
134
+ def get_experiment_key(experiment_id)
135
+ # Retrieves experiment key for a given ID.
129
136
  #
130
- # Returns array of all goal keys except 'Total Revenue'
131
-
132
- goal_keys = @event_key_map.keys
133
- goal_keys.delete(REVENUE_GOAL_KEY) if goal_keys.include?(REVENUE_GOAL_KEY)
134
- goal_keys
135
- end
136
-
137
- def get_revenue_goal_id
138
- # Get ID of the revenue goal for the project
137
+ # experiment_id - String ID representing the experiment.
139
138
  #
140
- # Returns revenue goal ID
139
+ # Returns String key.
141
140
 
142
- revenue_goal = @event_key_map[REVENUE_GOAL_KEY]
143
- return revenue_goal['id'] if revenue_goal
141
+ experiment = @experiment_id_map[experiment_id]
142
+ return experiment['key'] unless experiment.nil?
143
+ @logger.log Logger::ERROR, "Experiment id '#{experiment_id}' is not in datafile."
144
+ @error_handler.handle_error InvalidExperimentError
144
145
  nil
145
146
  end
146
147
 
147
- def get_experiment_ids_for_goal(goal_key)
148
- # Get experiment IDs for the provided goal key.
148
+ def get_experiment_ids_for_event(event_key)
149
+ # Get experiment IDs for the provided event key.
149
150
  #
150
- # goal_key - Goal key for which experiment IDs are to be retrieved.
151
+ # event_key - Event key for which experiment IDs are to be retrieved.
151
152
  #
152
- # Returns array of all experiment IDs for the goal.
153
+ # Returns array of all experiment IDs for the event.
153
154
 
154
- goal = @event_key_map[goal_key]
155
- return goal['experimentIds'] if goal
156
- @logger.log Logger::ERROR, "Event '#{goal_key}' is not in datafile."
155
+ event = @event_key_map[event_key]
156
+ return event['experimentIds'] if event
157
+ @logger.log Logger::ERROR, "Event '#{event_key}' is not in datafile."
157
158
  @error_handler.handle_error InvalidEventError
158
159
  []
159
160
  end
@@ -272,14 +273,6 @@ module Optimizely
272
273
  nil
273
274
  end
274
275
 
275
- def get_segment_id(attribute_key)
276
- attribute = @attribute_key_map[attribute_key]
277
- return attribute['segmentId'] if attribute
278
- @logger.log Logger::ERROR, "Attribute key '#{attribute_key}' is not in datafile."
279
- @error_handler.handle_error InvalidAttributeError
280
- nil
281
- end
282
-
283
276
  def user_in_forced_variation?(experiment_key, user_id)
284
277
  # Determines if a given user is in a forced variation
285
278
  #
@@ -293,6 +286,14 @@ module Optimizely
293
286
  false
294
287
  end
295
288
 
289
+ def parsing_succeeded?
290
+ # Helper method to determine if parsing the datafile was successful.
291
+ #
292
+ # Returns Boolean depending on whether parsing the datafile succeeded or not.
293
+
294
+ @parsing_succeeded
295
+ end
296
+
296
297
  private
297
298
 
298
299
  def generate_key_map(array, key)
@@ -14,5 +14,5 @@
14
14
  # limitations under the License.
15
15
  #
16
16
  module Optimizely
17
- VERSION = '1.1.2'.freeze
17
+ VERSION = '1.2.0'.freeze
18
18
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: optimizely-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Delikat
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2017-03-22 00:00:00.000000000 Z
13
+ date: 2017-05-05 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
@@ -153,7 +153,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
153
153
  version: '0'
154
154
  requirements: []
155
155
  rubyforge_project:
156
- rubygems_version: 2.5.2
156
+ rubygems_version: 2.6.11
157
157
  signing_key:
158
158
  specification_version: 4
159
159
  summary: Ruby SDK for Optimizely's testing framework