vwo-sdk 1.30.0 → 1.37.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,11 +12,11 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- require_relative '../logger'
16
15
  require_relative '../enums'
17
16
  require_relative '../utils/campaign'
18
17
  require_relative '../services/segment_evaluator'
19
18
  require_relative '../utils/validations'
19
+ require_relative '../utils/log_message'
20
20
  require_relative 'bucketer'
21
21
  require_relative '../constants'
22
22
  require_relative '../services/hooks_manager'
@@ -33,14 +33,14 @@ class VWO
33
33
  include VWO::CONSTANTS
34
34
  include VWO::Utils::UUID
35
35
 
36
- FILE = FileNameEnum::VariationDecider
36
+ FILE = FileNameEnum::VARIATION_DECIDER
37
37
 
38
38
  # Initializes various services
39
39
  # @param[Hash] - Settings file
40
40
  # @param[Class] - Class instance having the capability of
41
41
  # get and save.
42
42
  def initialize(settings_file, user_storage_service = nil, options = {})
43
- @logger = VWO::Logger.get_instance
43
+ @logger = VWO::Utils::Logger
44
44
  @user_storage_service = user_storage_service
45
45
  @bucketer = VWO::Core::Bucketer.new
46
46
  @settings_file = settings_file
@@ -66,43 +66,17 @@ class VWO
66
66
 
67
67
  return unless campaign
68
68
 
69
- is_campaign_part_of_group = @settings_file && is_part_of_group(@settings_file, campaign["id"])
69
+ is_campaign_part_of_group = @settings_file && part_of_group?(@settings_file, campaign['id'])
70
70
 
71
71
  @has_stored_variation = false
72
- decision = {
73
- :campaign_id => campaign['id'],
74
- :campaign_key => campaign_key,
75
- :campaign_type => campaign['type'],
76
- # campaign segmentation conditions
77
- :custom_variables => custom_variables,
78
- # event name
79
- :event => Hooks::DECISION_TYPES['CAMPAIGN_DECISION'],
80
- # goal tracked in case of track API
81
- :goal_identifier => goal_identifier,
82
- # campaign whitelisting flag
83
- :is_forced_variation_enabled => campaign['isForcedVariationEnabled'] ? campaign['isForcedVariationEnabled'] : false,
84
- :sdk_version => SDK_VERSION,
85
- # API name which triggered the event
86
- :source => api_name,
87
- # Passed in API
88
- :user_id => user_id,
89
- # Campaign Whitelisting conditions
90
- :variation_targeting_variables => variation_targeting_variables,
91
- :is_user_whitelisted => false,
92
- :from_user_storage_service => false,
93
- :is_feature_enabled => true,
94
- # VWO generated UUID based on passed UserId and Account ID
95
- :vwo_user_id => generator_for(user_id, @settings_file['accountId'])
96
- }
72
+ decision = initialize_decision_properties(user_id, campaign, api_name, custom_variables, variation_targeting_variables, goal_identifier)
97
73
 
98
- if campaign.has_key?("name")
99
- decision[:campaign_name] = campaign['name']
100
- end
74
+ decision[:campaign_name] = campaign['name'] if campaign.key?('name')
101
75
 
102
76
  if is_campaign_part_of_group
103
- group_id = @settings_file["campaignGroups"][campaign["id"].to_s]
77
+ group_id = @settings_file['campaignGroups'][campaign['id'].to_s]
104
78
  decision[:group_id] = group_id
105
- group_name = @settings_file["groups"][group_id.to_s]["name"]
79
+ group_name = @settings_file['groups'][group_id.to_s]['name']
106
80
  decision[:group_name] = group_name
107
81
  end
108
82
 
@@ -110,189 +84,250 @@ class VWO
110
84
  variation = get_variation_if_whitelisting_passed(user_id, campaign, variation_targeting_variables, api_name, decision, true)
111
85
  return variation if variation && variation['name']
112
86
 
113
- user_campaign_map = get_user_storage(user_id, campaign_key)
114
- variation = get_stored_variation(user_id, campaign_key, user_campaign_map) if valid_hash?(user_campaign_map)
87
+ if campaign.key?('isAlwaysCheckSegment')
88
+ is_presegmentation = presegmentation?(campaign, user_id, custom_variables, api_name)
89
+ return get_variation_if_presegmentation_applied(is_presegmentation, campaign, user_id, goal_identifier, decision)
90
+ else
91
+ user_campaign_map = get_user_storage(user_id, campaign_key)
92
+ variation = get_stored_variation(user_id, campaign_key, user_campaign_map) if valid_hash?(user_campaign_map)
115
93
 
116
- if variation
117
- variation = variation.dup # deep copy
118
- end
94
+ variation = variation.dup if variation # deep copy
119
95
 
120
- if variation
121
- if valid_string?(user_campaign_map['goal_identifier']) && api_name == ApiMethods::TRACK
122
- variation['goal_identifier'] = user_campaign_map['goal_identifier']
123
- end
124
- @has_stored_variation = true
125
- @logger.log(
126
- LogLevelEnum::INFO,
127
- format(
128
- LogMessageEnum::InfoMessages::GOT_STORED_VARIATION,
129
- file: FILE,
130
- campaign_key: campaign_key,
131
- user_id: user_id,
132
- variation_name: variation['name']
133
- )
134
- )
135
- decision[:from_user_storage_service] = !!variation['name']
136
96
  if variation
137
- if campaign['type'] == CampaignTypes::VISUAL_AB || campaign['type'] == CampaignTypes::FEATURE_TEST
138
- decision[:variation_name] = variation['name']
139
- decision[:variation_id] = variation['id']
140
- if campaign['type'] == CampaignTypes::FEATURE_TEST
141
- decision[:is_feature_enabled] = variation['isFeatureEnabled']
142
- end
143
- end
144
- @hooks_manager.execute(decision)
145
- end
146
- return variation
147
- else
148
- @logger.log(
149
- LogLevelEnum::DEBUG,
150
- format(
151
- LogMessageEnum::DebugMessages::NO_STORED_VARIATION,
152
- file: FILE,
153
- campaign_key: campaign_key,
154
- user_id: user_id
97
+ variation['goal_identifier'] = user_campaign_map['goal_identifier'] if valid_string?(user_campaign_map['goal_identifier']) && api_name == ApiMethods::TRACK
98
+ @has_stored_variation = true
99
+ @logger.log(
100
+ LogLevelEnum::INFO,
101
+ 'GOT_STORED_VARIATION',
102
+ {
103
+ '{file}' => FILE,
104
+ '{campaignKey}' => campaign_key,
105
+ '{userId}' => user_id,
106
+ '{variationName}' => variation['name']
107
+ }
155
108
  )
156
- )
157
-
158
- if ([ApiMethods::TRACK, ApiMethods::GET_VARIATION_NAME, ApiMethods::GET_FEATURE_VARIABLE_VALUE].include? api_name) && @user_storage_service
109
+ decision[:from_user_storage_service] = !!variation['name']
110
+ decision = add_variation_to_decision_properties(decision, campaign, variation)
111
+ @hooks_manager.execute(decision)
112
+ return variation
113
+ else
159
114
  @logger.log(
160
115
  LogLevelEnum::DEBUG,
161
- format(
162
- LogMessageEnum::DebugMessages::CAMPAIGN_NOT_ACTIVATED,
163
- file: FILE,
164
- campaign_key: campaign_key,
165
- user_id: user_id,
166
- api_name: api_name
167
- )
116
+ 'USER_STORAGE_SERVICE_NO_STORED_DATA',
117
+ {
118
+ '{file}' => FILE,
119
+ '{campaignKey}' => campaign_key,
120
+ '{userId}' => user_id
121
+ }
168
122
  )
169
123
 
170
- @logger.log(
171
- LogLevelEnum::INFO,
172
- format(
173
- LogMessageEnum::InfoMessages::CAMPAIGN_NOT_ACTIVATED,
174
- file: FILE,
175
- campaign_key: campaign_key,
176
- user_id: user_id,
177
- api_name: api_name,
178
- reason: api_name == ApiMethods::TRACK ? 'track it' : 'get the decision/value'
179
- )
180
- )
181
- return
124
+ return if campaign_not_activated?(user_id, campaign_key, api_name)
182
125
  end
183
126
  end
184
127
 
185
128
  # Pre-segmentation
186
- is_presegmentation = check_presegmentation(campaign, user_id, custom_variables, api_name)
129
+ is_presegmentation = presegmentation?(campaign, user_id, custom_variables, api_name)
187
130
  is_presegmentation_and_traffic_passed = is_presegmentation && @bucketer.user_part_of_campaign?(user_id, campaign)
188
- unless is_presegmentation_and_traffic_passed
189
- return nil
190
- end
131
+ return nil unless is_presegmentation_and_traffic_passed
191
132
 
192
133
  if is_presegmentation_and_traffic_passed && is_campaign_part_of_group
193
134
  group_campaigns = get_group_campaigns(@settings_file, group_id)
194
-
195
135
  if group_campaigns
196
- is_any_campaign_whitelisted_or_stored = check_whitelisting_or_storage_for_grouped_campaigns(user_id, campaign, group_campaigns, group_name, variation_targeting_variables, true)
197
-
198
- if is_any_campaign_whitelisted_or_stored
136
+ if whitelisting_or_storage_for_grouped_campaigns?(user_id, campaign, group_campaigns, group_name, variation_targeting_variables, true)
199
137
  @logger.log(
200
138
  LogLevelEnum::INFO,
201
- format(
202
- LogMessageEnum::InfoMessages::CALLED_CAMPAIGN_NOT_WINNER,
203
- file: FILE,
204
- campaign_key: campaign_key,
205
- user_id: user_id,
206
- group_name: group_name
207
- )
139
+ 'MEG_CALLED_CAMPAIGN_NOT_WINNER',
140
+ {
141
+ '{file}' => FILE,
142
+ '{campaignKey}' => campaign_key,
143
+ '{userId}' => user_id,
144
+ '{groupName}' => group_name
145
+ }
208
146
  )
209
147
  return nil
210
148
  end
211
-
212
149
  eligible_campaigns = get_eligible_campaigns(user_id, group_campaigns, campaign, custom_variables)
213
150
  non_eligible_campaigns_key = get_non_eligible_campaigns_key(eligible_campaigns, group_campaigns)
214
-
215
151
  @logger.log(
216
152
  LogLevelEnum::DEBUG,
217
- format(
218
- LogMessageEnum::DebugMessages::GOT_ELIGIBLE_CAMPAIGNS,
219
- file: FILE,
220
- user_id: user_id,
221
- eligible_campaigns_key: get_eligible_campaigns_key(eligible_campaigns).join(","),
222
- ineligible_campaigns_log_text: non_eligible_campaigns_key ? ("campaigns:" + non_eligible_campaigns_key.join("'")) : "no campaigns",
223
- group_name: group_name
224
- )
153
+ 'MEG_ELIGIBLE_CAMPAIGNS',
154
+ {
155
+ '{file}' => FILE,
156
+ '{userId}' => user_id,
157
+ '{eligibleCampaignKeys}' => get_eligible_campaigns_key(eligible_campaigns).join(','),
158
+ '{inEligibleText}' => non_eligible_campaigns_key ? "campaigns: + #{non_eligible_campaigns_key.join("'")}" : 'no campaigns',
159
+ '{groupName}' => group_name
160
+ }
225
161
  )
226
162
 
227
163
  @logger.log(
228
164
  LogLevelEnum::INFO,
229
- format(
230
- LogMessageEnum::InfoMessages::GOT_ELIGIBLE_CAMPAIGNS,
231
- file: FILE,
232
- user_id: user_id,
233
- no_of_eligible_campaigns: eligible_campaigns.length,
234
- no_of_group_campaigns: group_campaigns.length,
235
- group_name: group_name
236
- )
165
+ 'MEG_ELIGIBLE_CAMPAIGNS',
166
+ {
167
+ '{file}' => FILE,
168
+ '{userId}' => user_id,
169
+ '{noOfEligibleCampaigns}' => eligible_campaigns.length,
170
+ '{noOfGroupCampaigns}' => group_campaigns.length,
171
+ '{groupName}' => group_name
172
+ }
237
173
  )
238
174
 
239
175
  winner_campaign = get_winner_campaign(user_id, eligible_campaigns, group_id)
240
176
  @logger.log(
241
177
  LogLevelEnum::INFO,
242
- format(
243
- LogMessageEnum::InfoMessages::GOT_WINNER_CAMPAIGN,
244
- file: FILE,
245
- user_id: user_id,
246
- campaign_key: winner_campaign["key"],
247
- group_name: group_name
248
- )
178
+ 'MEG_GOT_WINNER_CAMPAIGN',
179
+ {
180
+ '{file}' => FILE,
181
+ '{userId}' => user_id,
182
+ '{campaignKey}' => winner_campaign['key'],
183
+ '{groupName}' => group_name
184
+ }
249
185
  )
250
186
 
251
- if winner_campaign && winner_campaign["id"] == campaign["id"]
252
- variation = get_variation_allotted(user_id, campaign, true)
253
- if variation && variation['name']
254
- save_user_storage(user_id, campaign_key, campaign['type'], variation['name'], goal_identifier, true) if variation['name']
255
- else
256
- return nil
257
- end
258
- else
259
- @logger.log(
260
- LogLevelEnum::INFO,
261
- format(
262
- LogMessageEnum::InfoMessages::CALLED_CAMPAIGN_NOT_WINNER,
263
- file: FILE,
264
- campaign_key: campaign_key,
265
- user_id: user_id,
266
- group_name: group_name
267
- )
268
- )
269
- return nil
270
- end
187
+ return if meg_called_campaign_not_winner?(user_id, group_name, campaign, winner_campaign)
188
+
189
+ variation = get_variation_allotted(user_id, campaign, true)
190
+ return nil unless variation && variation['name']
191
+
192
+ save_user_storage(user_id, campaign_key, campaign['type'], variation['name'], goal_identifier, true) if variation['name']
271
193
  end
272
194
  end
273
195
 
274
- if variation.nil?
275
- variation = get_variation_allotted(user_id, campaign)
196
+ variation ||= get_variation_if_presegmentation_applied(is_presegmentation, campaign, user_id, goal_identifier, decision)
197
+ return unless variation
276
198
 
277
- if variation && variation['name']
278
- save_user_storage(user_id, campaign_key, campaign['type'], variation['name'], goal_identifier) if variation['name']
279
- else
280
- @logger.log(
281
- LogLevelEnum::INFO,
282
- format(LogMessageEnum::InfoMessages::NO_VARIATION_ALLOCATED, file: FILE, campaign_key: campaign_key, user_id: user_id)
283
- )
284
- end
199
+ decision = add_variation_to_decision_properties(decision, campaign, variation)
200
+ @hooks_manager.execute(decision)
201
+ variation
202
+ end
203
+
204
+ def campaign_not_activated?(user_id, campaign_key, api_name)
205
+ if ([ApiMethods::TRACK, ApiMethods::GET_VARIATION_NAME, ApiMethods::GET_FEATURE_VARIABLE_VALUE].include? api_name) && @user_storage_service
206
+ @logger.log(
207
+ LogLevelEnum::WARNING,
208
+ 'CAMPAIGN_NOT_ACTIVATED',
209
+ {
210
+ '{file}' => FILE,
211
+ '{campaignKey}' => campaign_key,
212
+ '{userId}' => user_id,
213
+ '{api}' => api_name
214
+ }
215
+ )
216
+
217
+ @logger.log(
218
+ LogLevelEnum::INFO,
219
+ 'CAMPAIGN_NOT_ACTIVATED',
220
+ {
221
+ '{file}' => FILE,
222
+ '{campaignKey}' => campaign_key,
223
+ '{userId}' => user_id,
224
+ '{reason}' => api_name == ApiMethods::TRACK ? 'track it' : 'get the decision/value'
225
+ }
226
+ )
227
+ return true
228
+ end
229
+ false
230
+ end
231
+
232
+ def meg_called_campaign_not_winner?(user_id, group_name, campaign, winner_campaign)
233
+ unless winner_campaign && winner_campaign['id'] == campaign['id']
234
+ @logger.log(
235
+ LogLevelEnum::INFO,
236
+ 'MEG_CALLED_CAMPAIGN_NOT_WINNER',
237
+ {
238
+ '{file}' => FILE,
239
+ '{campaignKey}' => campaign['key'],
240
+ '{userId}' => user_id,
241
+ '{groupName}' => group_name
242
+ }
243
+ )
244
+ return true
245
+ end
246
+ false
247
+ end
248
+
249
+ # Intitialize decision properties for hook manager
250
+ #
251
+ # @param[String] :user_id The unique ID assigned to User
252
+ # @param[Hash] :campaign Campaign hash itself
253
+ # @param[String] :api_name Name of the current api call
254
+ # @param[String] :goal_identifier The unique campaign's goal identifier
255
+ # @param[Hash] :custom_variables Key/value pair for segmentation
256
+ # @param[Hash] :variation_targeting_variables Key/value pair for whitelisting
257
+ # @return[Hash] Decision properties for the callback by hook manager
258
+ def initialize_decision_properties(user_id, campaign, api_name, custom_variables = {}, variation_targeting_variables = {}, goal_identifier = '')
259
+ {
260
+ campaign_id: campaign['id'],
261
+ campaign_key: campaign['key'],
262
+ campaign_type: campaign['type'],
263
+ # campaign segmentation conditions
264
+ custom_variables: custom_variables,
265
+ # event name
266
+ event: Hooks::DECISION_TYPES['CAMPAIGN_DECISION'],
267
+ # goal tracked in case of track API
268
+ goal_identifier: goal_identifier,
269
+ # campaign whitelisting flag
270
+ is_forced_variation_enabled: campaign['isForcedVariationEnabled'] || false,
271
+ sdk_version: SDK_VERSION,
272
+ # API name which triggered the event
273
+ source: api_name,
274
+ # Passed in API
275
+ user_id: user_id,
276
+ # Campaign Whitelisting conditions
277
+ variation_targeting_variables: variation_targeting_variables,
278
+ is_user_whitelisted: false,
279
+ from_user_storage_service: false,
280
+ is_feature_enabled: true,
281
+ # VWO generated UUID based on passed UserId and Account ID
282
+ vwo_user_id: generator_for(user_id, @settings_file['accountId'])
283
+ }
284
+ end
285
+
286
+ # Add variation details to decision properties for hook manager
287
+ #
288
+ # @param[Hash] :campaign Campaign details
289
+ # @param[Hash] :variation Variation assigned to user
290
+ # @param[Hash] :decision Decision properties
291
+ # @return[Hash] :decision Decision properties
292
+ def add_variation_to_decision_properties(decision, campaign, variation)
293
+ if campaign['type'] == CampaignTypes::VISUAL_AB || campaign['type'] == CampaignTypes::FEATURE_TEST
294
+ decision[:variation_name] = variation['name']
295
+ decision[:variation_id] = variation['id']
296
+ end
297
+ decision[:is_feature_enabled] = variation['isFeatureEnabled'] if campaign['type'] == CampaignTypes::FEATURE_TEST
298
+ decision
299
+ end
300
+
301
+ # Get variation by murmur logic if pre segmentation pass
302
+ #
303
+ # @param[Boolean] :is_presegmentation The unique key assigned to User
304
+ # @param[Hash] :campaign Campaign hash for Unique campaign key
305
+ # @param[String] :user_id the unique ID assigned to User
306
+ # @param[String] :goal_identifier goal Identifier used in track API
307
+ # @param[Hash] :decision data containing campaign info passed to hooks manager
308
+ #
309
+ # @return[Hash]
310
+ def get_variation_if_presegmentation_applied(is_presegmentation, campaign, user_id, goal_identifier, decision)
311
+ return nil unless is_presegmentation
285
312
 
313
+ campaign_key = campaign['key']
314
+ variation = get_variation_allotted(user_id, campaign)
315
+ if variation && variation['name']
316
+ save_user_storage(user_id, campaign_key, campaign['type'], variation['name'], goal_identifier) if variation['name']
317
+ else
318
+ @logger.log(
319
+ LogLevelEnum::INFO,
320
+ 'DECISION_NO_VARIATION_ALLOTED',
321
+ {
322
+ '{file}' => FILE,
323
+ '{campaignKey}' => campaign_key,
324
+ '{userId}' => user_id
325
+ }
326
+ )
286
327
  end
287
328
 
288
329
  if variation
289
- if campaign['type'] == CampaignTypes::VISUAL_AB || campaign['type'] == CampaignTypes::FEATURE_TEST
290
- decision[:variation_name] = variation['name']
291
- decision[:variation_id] = variation['id']
292
- if campaign['type'] == CampaignTypes::FEATURE_TEST
293
- decision[:is_feature_enabled] = variation['isFeatureEnabled']
294
- end
295
- end
330
+ decision = add_variation_to_decision_properties(decision, campaign, variation)
296
331
  @hooks_manager.execute(decision)
297
332
  end
298
333
  variation
@@ -309,37 +344,29 @@ class VWO
309
344
  unless valid_value?(user_id)
310
345
  @logger.log(
311
346
  LogLevelEnum::ERROR,
312
- format(LogMessageEnum::ErrorMessages::INVALID_USER_ID, file: FILE, user_id: user_id, method: 'get_variation_alloted')
347
+ 'USER_ID_INVALID',
348
+ {
349
+ '{file}' => FILE,
350
+ '{userId}' => user_id
351
+ },
352
+ disable_logs
313
353
  )
314
354
  return
315
355
  end
316
356
 
317
- if @bucketer.user_part_of_campaign?(user_id, campaign)
318
- variation = get_variation_of_campaign_for_user(user_id, campaign)
319
- @logger.log(
320
- LogLevelEnum::DEBUG,
321
- format(
322
- LogMessageEnum::DebugMessages::GOT_VARIATION_FOR_USER,
323
- file: FILE,
324
- variation_name: variation['name'],
325
- user_id: user_id,
326
- campaign_key: campaign['key'],
327
- method: 'get_variation_allotted'
328
- ),
329
- disable_logs
330
- )
331
- variation
357
+ if @bucketer.user_part_of_campaign?(user_id, campaign, true)
358
+ get_variation_of_campaign_for_user(user_id, campaign, disable_logs)
359
+
332
360
  else
333
361
  # not part of campaign
334
362
  @logger.log(
335
- LogLevelEnum::DEBUG,
336
- format(
337
- LogMessageEnum::DebugMessages::USER_NOT_PART_OF_CAMPAIGN,
338
- file: FILE,
339
- user_id: user_id,
340
- campaign_key: nil,
341
- method: 'get_variation_allotted'
342
- ),
363
+ LogLevelEnum::INFO,
364
+ 'USER_NOT_PART_OF_CAMPAIGN',
365
+ {
366
+ '{file}' => FILE,
367
+ '{campaignKey}' => nil,
368
+ '{userId}' => user_id
369
+ },
343
370
  disable_logs
344
371
  )
345
372
  nil
@@ -352,44 +379,36 @@ class VWO
352
379
  # @param[Hash] :campaign The Campaign of which user is to be made a part of
353
380
  # @return[Hash] Variation allotted to User
354
381
 
355
- def get_variation_of_campaign_for_user(user_id, campaign)
356
- unless campaign
357
- @logger.log(
358
- LogLevelEnum::ERROR,
359
- format(
360
- LogMessageEnum::ErrorMessages::INVALID_CAMPAIGN,
361
- file: FILE,
362
- method: 'get_variation_of_campaign_for_user'
363
- )
364
- )
365
- return nil
366
- end
367
-
368
- variation = @bucketer.bucket_user_to_variation(user_id, campaign)
382
+ def get_variation_of_campaign_for_user(user_id, campaign, disable_logs = false)
383
+ variation = @bucketer.bucket_user_to_variation(user_id, campaign, disable_logs)
369
384
 
370
385
  if variation && variation['name']
371
386
  @logger.log(
372
387
  LogLevelEnum::INFO,
373
- format(
374
- LogMessageEnum::InfoMessages::GOT_VARIATION_FOR_USER,
375
- file: FILE,
376
- variation_name: variation['name'],
377
- user_id: user_id,
378
- campaign_key: campaign['key']
379
- )
388
+ 'USER_VARIATION_ALLOCATION_STATUS',
389
+ {
390
+ '{file}' => FILE,
391
+ '{status}' => variation ? "got variation: + #{variation['name']}" : 'did not get any variation',
392
+ '{userId}' => user_id,
393
+ '{campaignKey}' => campaign['key']
394
+ },
395
+ disable_logs
380
396
  )
381
397
  return variation
382
398
  end
383
399
 
384
- @logger.log(
385
- LogLevelEnum::INFO,
386
- format(
387
- LogMessageEnum::InfoMessages::USER_GOT_NO_VARIATION,
388
- file: FILE,
389
- user_id: user_id,
390
- campaign_key: campaign['key']
400
+ if campaign
401
+ @logger.log(
402
+ LogLevelEnum::INFO,
403
+ 'DECISION_NO_VARIATION_ALLOTED',
404
+ {
405
+ '{file}' => FILE,
406
+ '{userId}' => user_id,
407
+ '{campaignKey}' => campaign['key']
408
+ },
409
+ disable_logs
391
410
  )
392
- )
411
+ end
393
412
  nil
394
413
  end
395
414
 
@@ -402,11 +421,12 @@ class VWO
402
421
  # @param[Boolean] :disable_logs optional: disable logs if True
403
422
  # @return[Boolean] true if found otherwise false
404
423
 
405
- def save_user_storage(user_id, campaign_key, campaign_type, variation_name, goal_identifier, disable_logs = false)
424
+ def save_user_storage(user_id, campaign_key, _campaign_type, variation_name, goal_identifier, disable_logs = false)
406
425
  unless @user_storage_service
407
426
  @logger.log(
408
427
  LogLevelEnum::DEBUG,
409
- format(LogMessageEnum::DebugMessages::NO_USER_STORAGE_SERVICE_SAVE, file: FILE),
428
+ 'USER_STORAGE_SERVICE_NOT_CONFIGURED',
429
+ { '{file}' => FILE },
410
430
  disable_logs
411
431
  )
412
432
  return false
@@ -415,40 +435,41 @@ class VWO
415
435
  new_campaign_user_mapping['campaign_key'] = campaign_key
416
436
  new_campaign_user_mapping['user_id'] = user_id
417
437
  new_campaign_user_mapping['variation_name'] = variation_name
418
- if !goal_identifier.empty?
419
- new_campaign_user_mapping['goal_identifier'] = goal_identifier
420
- end
438
+ new_campaign_user_mapping['goal_identifier'] = goal_identifier unless goal_identifier.empty?
421
439
 
422
440
  @user_storage_service.set(new_campaign_user_mapping)
423
441
 
424
442
  @logger.log(
425
443
  LogLevelEnum::INFO,
426
- format(LogMessageEnum::InfoMessages::SAVING_DATA_USER_STORAGE_SERVICE, file: FILE, user_id: user_id),
427
- disable_logs
428
- )
429
-
430
- @logger.log(
431
- LogLevelEnum::INFO,
432
- format(
433
- LogMessageEnum::InfoMessages::VARIATION_ALLOCATED,
434
- file: FILE,
435
- campaign_key: campaign_key,
436
- user_id: user_id,
437
- variation_name: variation_name,
438
- campaign_type: campaign_type
439
- ),
444
+ 'SETTING_DATA_USER_STORAGE_SERVICE',
445
+ {
446
+ '{file}' => FILE,
447
+ '{userId}' => user_id,
448
+ '{campaignKey}' => campaign_key
449
+ },
440
450
  disable_logs
441
451
  )
442
452
  true
443
- rescue StandardError
453
+ rescue StandardError => e
444
454
  @logger.log(
445
455
  LogLevelEnum::ERROR,
446
- format(LogMessageEnum::ErrorMessages::SAVE_USER_STORAGE_SERVICE_FAILED, file: FILE, user_id: user_id),
456
+ 'USER_STORAGE_SERVICE_SET_FAILED',
457
+ {
458
+ '{file}' => FILE,
459
+ '{userId}' => user_id,
460
+ '{error}' => e.message
461
+ },
447
462
  disable_logs
448
463
  )
449
464
  false
450
465
  end
451
466
 
467
+ def update_goal_identifier(user_id, campaign, variation, goal_identifier)
468
+ updated_goal_identifier = variation['goal_identifier']
469
+ updated_goal_identifier += VWO_DELIMITER + goal_identifier
470
+ save_user_storage(user_id, campaign['key'], campaign['name'], variation['name'], updated_goal_identifier) if variation['name']
471
+ end
472
+
452
473
  private
453
474
 
454
475
  # Evaluate all the variations in the campaign to find
@@ -462,20 +483,18 @@ class VWO
462
483
  #
463
484
  # @return[Hash]
464
485
 
465
- def evaluate_whitelisting(user_id, campaign, api_name, campaign_key, variation_targeting_variables = {}, disable_logs = false)
466
- if campaign.key?('isUserListEnabled') && campaign["isUserListEnabled"]
467
- vwo_user_id = generator_for(user_id, @settings_file['accountId'])
486
+ def evaluate_whitelisting(user_id, campaign, _api_name, campaign_key, variation_targeting_variables = {}, disable_logs = false)
487
+ if campaign.key?('isUserListEnabled') && campaign['isUserListEnabled']
488
+ vwo_user_id = generator_for(user_id, @settings_file['accountId'], true)
468
489
  if variation_targeting_variables.nil?
469
490
  variation_targeting_variables = { _vwo_user_id: vwo_user_id }
470
491
  else
471
492
  variation_targeting_variables[:_vwo_user_id] = vwo_user_id
472
493
  end
494
+ elsif variation_targeting_variables.nil?
495
+ variation_targeting_variables = { _vwo_user_id: user_id }
473
496
  else
474
- if variation_targeting_variables.nil?
475
- variation_targeting_variables = { _vwo_user_id: user_id }
476
- else
477
- variation_targeting_variables[:_vwo_user_id] = user_id
478
- end
497
+ variation_targeting_variables[:_vwo_user_id] = user_id
479
498
  end
480
499
  targeted_variations = []
481
500
 
@@ -490,36 +509,37 @@ class VWO
490
509
  status = StatusEnum::FAILED
491
510
  end
492
511
  @logger.log(
493
- LogLevelEnum::DEBUG,
494
- format(
495
- LogMessageEnum::DebugMessages::SEGMENTATION_STATUS,
496
- file: FILE,
497
- campaign_key: campaign_key,
498
- user_id: user_id,
499
- status: status,
500
- custom_variables: variation_targeting_variables,
501
- variation_name: status == StatusEnum::PASSED ? (campaign['type'] == CampaignTypes::FEATURE_ROLLOUT ? 'and hence becomes part of the rollout' : "for " + variation['name']) : '',
502
- segmentation_type: SegmentationTypeEnum::WHITELISTING,
503
- api_name: api_name
504
- ),
512
+ LogLevelEnum::INFO,
513
+ 'SEGMENTATION_STATUS',
514
+ {
515
+ '{file}' => FILE,
516
+ '{campaignKey}' => campaign_key,
517
+ '{userId}' => user_id,
518
+ '{customVariables}' => variation_targeting_variables,
519
+ '{status}' => status,
520
+ '{segmentationType}' => SegmentationTypeEnum::WHITELISTING,
521
+ '{variation}' => if status == StatusEnum::PASSED
522
+ campaign['type'] == CampaignTypes::FEATURE_ROLLOUT ? 'and hence becomes part of the rollout' : "#{variation['name']} and hence becomes part of the rollout"
523
+ else
524
+ ''
525
+ end
526
+ },
505
527
  disable_logs
506
528
  )
507
529
  else
508
530
  @logger.log(
509
531
  LogLevelEnum::DEBUG,
510
- format(
511
- LogMessageEnum::InfoMessages::SKIPPING_SEGMENTATION,
512
- file: FILE,
513
- campaign_key: campaign_key,
514
- user_id: user_id,
515
- api_name: api_name,
516
- variation: variation['name']
517
- ),
532
+ 'SEGMENTATION_SKIPPED',
533
+ {
534
+ '{file}' => FILE,
535
+ '{campaignKey}' => campaign_key,
536
+ '{userId}' => user_id,
537
+ '{variation}' => campaign['type'] == CampaignTypes::FEATURE_ROLLOUT ? '' : "for variation:#{variation['name']}"
538
+ },
518
539
  disable_logs
519
540
  )
520
541
  end
521
542
  end
522
-
523
543
  if targeted_variations.length > 1
524
544
  targeted_variations_deep_clone = Marshal.load(Marshal.dump(targeted_variations))
525
545
  scale_variation_weights(targeted_variations_deep_clone)
@@ -541,7 +561,8 @@ class VWO
541
561
  targeted_variations_deep_clone,
542
562
  @bucketer.get_bucket_value_for_user(
543
563
  user_id,
544
- campaign
564
+ campaign,
565
+ disable_logs
545
566
  )
546
567
  )
547
568
  else
@@ -570,7 +591,7 @@ class VWO
570
591
  end
571
592
 
572
593
  def scale_campaigns_weight(campaigns)
573
- normalize_weight = 100/campaigns.length
594
+ normalize_weight = 100 / campaigns.length
574
595
  campaigns.each do |campaign|
575
596
  campaign['weight'] = normalize_weight
576
597
  end
@@ -587,28 +608,36 @@ class VWO
587
608
  unless @user_storage_service
588
609
  @logger.log(
589
610
  LogLevelEnum::DEBUG,
590
- format(LogMessageEnum::DebugMessages::NO_USER_STORAGE_SERVICE_LOOKUP, file: FILE),
611
+ 'USER_STORAGE_SERVICE_NOT_CONFIGURED',
612
+ { '{file}' => FILE },
591
613
  disable_logs
592
614
  )
593
615
  return false
594
616
  end
595
617
 
596
618
  data = @user_storage_service.get(user_id, campaign_key)
597
- @logger.log(
598
- LogLevelEnum::INFO,
599
- format(
600
- LogMessageEnum::InfoMessages::LOOKING_UP_USER_STORAGE_SERVICE,
601
- file: FILE,
602
- user_id: user_id,
603
- status: data.nil? ? 'Not Found' : 'Found'
604
- ),
605
- disable_logs
606
- )
619
+ if data
620
+ @logger.log(
621
+ LogLevelEnum::INFO,
622
+ 'GETTING_DATA_USER_STORAGE_SERVICE',
623
+ {
624
+ '{file}' => FILE,
625
+ '{userId}' => user_id,
626
+ '{campaignKey}' => campaign_key
627
+ },
628
+ disable_logs
629
+ )
630
+ end
607
631
  data
608
- rescue StandardError
632
+ rescue StandardError => e
609
633
  @logger.log(
610
634
  LogLevelEnum::ERROR,
611
- format(LogMessageEnum::ErrorMessages::LOOK_UP_USER_STORAGE_SERVICE_FAILED, file: FILE, user_id: user_id),
635
+ 'USER_STORAGE_SERVICE_GET_FAILED',
636
+ {
637
+ '{file}' => FILE,
638
+ '{userId}' => user_id,
639
+ '{error}' => e.message
640
+ },
612
641
  disable_logs
613
642
  )
614
643
  false
@@ -623,21 +652,10 @@ class VWO
623
652
  #
624
653
  # @return[Object, nil] if found then variation settings object otherwise None
625
654
 
626
- def get_stored_variation(user_id, campaign_key, user_campaign_map, disable_logs = false)
655
+ def get_stored_variation(_user_id, campaign_key, user_campaign_map, _disable_logs = false)
627
656
  return unless user_campaign_map['campaign_key'] == campaign_key
628
657
 
629
658
  variation_name = user_campaign_map['variation_name']
630
- @logger.log(
631
- LogLevelEnum::DEBUG,
632
- format(
633
- LogMessageEnum::DebugMessages::GETTING_STORED_VARIATION,
634
- file: FILE,
635
- campaign_key: campaign_key,
636
- user_id: user_id,
637
- variation_name: variation_name
638
- ),
639
- disable_logs
640
- )
641
659
 
642
660
  get_campaign_variation(
643
661
  @settings_file,
@@ -655,66 +673,58 @@ class VWO
655
673
  # @param[Boolean] :disable_logs optional: disable logs if True
656
674
  #
657
675
  # @return[Boolean]
658
- def check_presegmentation(campaign, user_id, custom_variables, api_name, disable_logs = false)
676
+ def presegmentation?(campaign, user_id, custom_variables, _api_name, disable_logs = false)
659
677
  campaign_key = campaign['key']
660
678
  segments = get_segments(campaign)
661
679
  is_valid_segments = valid_value?(segments)
662
680
 
663
681
  if is_valid_segments
664
- unless custom_variables
665
- @logger.log(
666
- LogLevelEnum::INFO,
667
- format(
668
- LogMessageEnum::InfoMessages::NO_CUSTOM_VARIABLES,
669
- file: FILE,
670
- campaign_key: campaign_key,
671
- user_id: user_id,
672
- api_name: api_name
673
- ),
674
- disable_logs
675
- )
676
- custom_variables = {}
677
- end
678
- unless @segment_evaluator.evaluate(campaign_key, user_id, segments, custom_variables, disable_logs)
679
- @logger.log(
680
- LogLevelEnum::INFO,
681
- format(
682
- LogMessageEnum::InfoMessages::USER_FAILED_SEGMENTATION,
683
- file: FileNameEnum::SegmentEvaluator,
684
- user_id: user_id,
685
- campaign_key: campaign_key,
686
- custom_variables: custom_variables
687
- ),
688
- disable_logs
689
- )
690
- return false
691
- end
682
+ custom_variables ||= {}
683
+ response = @segment_evaluator.evaluate(campaign_key, user_id, segments, custom_variables, disable_logs)
692
684
  @logger.log(
693
685
  LogLevelEnum::INFO,
694
- format(
695
- LogMessageEnum::InfoMessages::USER_PASSED_SEGMENTATION,
696
- file: FileNameEnum::SegmentEvaluator,
697
- user_id: user_id,
698
- campaign_key: campaign_key,
699
- custom_variables: custom_variables
700
- ),
686
+ 'SEGMENTATION_STATUS',
687
+ {
688
+ '{file}' => FILE,
689
+ '{userId}' => user_id,
690
+ '{status}' => response ? 'passed' : 'failed',
691
+ '{campaignKey}' => campaign_key,
692
+ '{customVariables}' => custom_variables,
693
+ '{segmentationType}' => 'pre-segmentation',
694
+ '{variation}' => ''
695
+ },
701
696
  disable_logs
702
697
  )
698
+ return response
703
699
  else
704
700
  @logger.log(
705
- LogLevelEnum::INFO,
706
- format(
707
- LogMessageEnum::InfoMessages::SKIPPING_SEGMENTATION,
708
- file: FILE,
709
- campaign_key: campaign_key,
710
- user_id: user_id,
711
- api_name: api_name,
712
- variation: ''
713
- ),
701
+ LogLevelEnum::DEBUG,
702
+ 'SEGMENTATION_SKIPPED',
703
+ {
704
+ '{file}' => FILE,
705
+ '{userId}' => user_id,
706
+ '{campaignKey}' => campaign_key,
707
+ '{variation}' => ''
708
+ },
714
709
  disable_logs
715
710
  )
716
711
  end
717
712
  true
713
+ rescue StandardError => e
714
+ @logger.log(
715
+ LogLevelEnum::ERROR,
716
+ 'SEGMENTATION_ERROR',
717
+ {
718
+ '{file}' => FILE,
719
+ '{userId}' => user_id,
720
+ '{campaignKey}' => campaign_key,
721
+ '{variation}' => '',
722
+ '{customVariables}' => custom_variables,
723
+ '{err}' => e.message
724
+ },
725
+ disable_logs
726
+ )
727
+ false
718
728
  end
719
729
 
720
730
  # Finds and returns eligible campaigns from group_campaigns.
@@ -729,11 +739,11 @@ class VWO
729
739
  eligible_campaigns = []
730
740
 
731
741
  group_campaigns.each do |campaign|
732
- if called_campaign["id"] == campaign["id"] || check_presegmentation(campaign, user_id, custom_variables, '', true) && @bucketer.user_part_of_campaign?(user_id, campaign)
742
+ if called_campaign['id'] == campaign['id'] || presegmentation?(campaign, user_id, custom_variables, '', true) && @bucketer.user_part_of_campaign?(user_id, campaign, true)
733
743
  eligible_campaigns.push(campaign)
734
744
  end
735
745
  end
736
- return eligible_campaigns
746
+ eligible_campaigns
737
747
  end
738
748
 
739
749
  # Finds and returns the winner campaign from eligible_campaigns list.
@@ -743,14 +753,12 @@ class VWO
743
753
  #
744
754
  # @return[Hash]
745
755
  def get_winner_campaign(user_id, eligible_campaigns, group_id)
746
- if eligible_campaigns.length == 1
747
- return eligible_campaigns[0]
748
- end
756
+ return eligible_campaigns[0] if eligible_campaigns.length == 1
749
757
 
750
758
  eligible_campaigns = scale_campaigns_weight(eligible_campaigns)
751
759
  eligible_campaigns = set_campaign_allocation(eligible_campaigns)
752
760
  bucket_value = @bucketer.get_bucket_value_for_user(user_id, {}, group_id, true)
753
- return @bucketer.get_campaign_using_range(bucket_value, eligible_campaigns)
761
+ @bucketer.get_campaign_using_range(bucket_value, eligible_campaigns)
754
762
  end
755
763
 
756
764
  # Get campaign keys of all eligible Campaigns.
@@ -761,7 +769,7 @@ class VWO
761
769
  def get_eligible_campaigns_key(eligible_campaigns)
762
770
  eligible_campaigns_key = []
763
771
  eligible_campaigns.each do |campaign|
764
- eligible_campaigns_key.push(campaign["key"])
772
+ eligible_campaigns_key.push(campaign['key'])
765
773
  end
766
774
  eligible_campaigns_key
767
775
  end
@@ -775,9 +783,7 @@ class VWO
775
783
  def get_non_eligible_campaigns_key(eligible_campaigns, group_campaigns)
776
784
  non_eligible_campaigns_key = []
777
785
  group_campaigns.each do |campaign|
778
- unless eligible_campaigns.include? campaign
779
- non_eligible_campaigns_key.push(campaign["key"])
780
- end
786
+ non_eligible_campaigns_key.push(campaign['key']) unless eligible_campaigns.include? campaign
781
787
  end
782
788
  non_eligible_campaigns_key
783
789
  end
@@ -792,54 +798,54 @@ class VWO
792
798
  # @param[Boolean] :disable_logs optional: disable logs if True
793
799
  # @return[Boolean]
794
800
 
795
- def check_whitelisting_or_storage_for_grouped_campaigns(user_id, called_campaign, group_campaigns, group_name, variation_targeting_variables, disable_logs = false)
801
+ def whitelisting_or_storage_for_grouped_campaigns?(user_id, called_campaign, group_campaigns, group_name, variation_targeting_variables, disable_logs = false)
796
802
  group_campaigns.each do |campaign|
797
- if called_campaign["id"] != campaign["id"]
798
- targeted_variation = evaluate_whitelisting(
799
- user_id,
800
- campaign,
801
- '',
802
- campaign["key"],
803
- variation_targeting_variables,
804
- true
805
- )
806
- if targeted_variation
807
- @logger.log(
808
- LogLevelEnum::INFO,
809
- format(
810
- LogMessageEnum::InfoMessages::OTHER_CAMPAIGN_SATISFIES_WHITELISTING_OR_STORAGE,
811
- file: FILE,
812
- campaign_key: campaign["key"],
813
- user_id: user_id,
814
- group_name: group_name,
815
- type: "whitelisting"
816
- ),
817
- disable_logs
818
- )
819
- return true
820
- end
821
- end
803
+ next unless called_campaign['id'] != campaign['id']
804
+
805
+ targeted_variation = evaluate_whitelisting(
806
+ user_id,
807
+ campaign,
808
+ '',
809
+ campaign['key'],
810
+ variation_targeting_variables,
811
+ true
812
+ )
813
+ next unless targeted_variation
814
+
815
+ @logger.log(
816
+ LogLevelEnum::INFO,
817
+ 'OTHER_CAMPAIGN_SATISFIES_WHITELISTING_STORAGE',
818
+ {
819
+ '{file}' => FILE,
820
+ '{campaignKey}' => campaign['key'],
821
+ '{userId}' => user_id,
822
+ '{groupName}' => group_name,
823
+ '{type}' => 'whitelisting'
824
+ },
825
+ disable_logs
826
+ )
827
+ return true
822
828
  end
823
829
 
824
830
  group_campaigns.each do |campaign|
825
- if called_campaign["id"] != campaign["id"]
826
- user_storage_data = get_user_storage(user_id, campaign["key"], true)
827
- if user_storage_data
828
- @logger.log(
829
- LogLevelEnum::INFO,
830
- format(
831
- LogMessageEnum::InfoMessages::OTHER_CAMPAIGN_SATISFIES_WHITELISTING_OR_STORAGE,
832
- file: FILE,
833
- campaign_key: campaign["key"],
834
- user_id: user_id,
835
- group_name: group_name,
836
- type: "user storage"
837
- ),
838
- disable_logs
839
- )
840
- return true
841
- end
842
- end
831
+ next unless called_campaign['id'] != campaign['id']
832
+
833
+ user_storage_data = get_user_storage(user_id, campaign['key'], true)
834
+ next unless user_storage_data
835
+
836
+ @logger.log(
837
+ LogLevelEnum::INFO,
838
+ 'OTHER_CAMPAIGN_SATISFIES_WHITELISTING_STORAGE',
839
+ {
840
+ '{file}' => FILE,
841
+ '{campaignKey}' => campaign['key'],
842
+ '{userId}' => user_id,
843
+ '{groupName}' => group_name,
844
+ '{type}' => 'user storag'
845
+ },
846
+ disable_logs
847
+ )
848
+ return true
843
849
  end
844
850
  false
845
851
  end
@@ -872,28 +878,21 @@ class VWO
872
878
 
873
879
  @logger.log(
874
880
  LogLevelEnum::INFO,
875
- format(
876
- LogMessageEnum::InfoMessages::SEGMENTATION_STATUS,
877
- file: FILE,
878
- campaign_key: campaign_key,
879
- user_id: user_id,
880
- status: status,
881
- custom_variables: variation_targeting_variables ? variation_targeting_variables : {},
882
- variation_name: (status == StatusEnum::PASSED && campaign['type'] != CampaignTypes::FEATURE_ROLLOUT) ? "and #{variation['name']} is Assigned" : ' ',
883
- segmentation_type: SegmentationTypeEnum::WHITELISTING,
884
- api_name: api_name
885
- ),
881
+ 'SEGMENTATION_STATUS',
882
+ {
883
+ '{file}' => FILE,
884
+ '{campaignKey}' => campaign_key,
885
+ '{userId}' => user_id,
886
+ '{customVariables}' => variation_targeting_variables || {},
887
+ '{status}' => status,
888
+ '{segmentationType}' => SegmentationTypeEnum::WHITELISTING,
889
+ '{variation}' => status == StatusEnum::PASSED && campaign['type'] != CampaignTypes::FEATURE_ROLLOUT ? "for variation:#{variation['name']}" : ' '
890
+ },
886
891
  disable_logs
887
892
  )
888
893
 
889
894
  if variation
890
- if campaign['type'] == CampaignTypes::VISUAL_AB || campaign['type'] == CampaignTypes::FEATURE_TEST
891
- decision[:variation_name] = variation['name']
892
- decision[:variation_id] = variation['id']
893
- if campaign['type'] == CampaignTypes::FEATURE_TEST
894
- decision[:is_feature_enabled] = variation['isFeatureEnabled']
895
- end
896
- end
895
+ decision = add_variation_to_decision_properties(decision, campaign, variation)
897
896
  decision[:is_user_whitelisted] = true
898
897
  @hooks_manager.execute(decision)
899
898
  end
@@ -901,20 +900,20 @@ class VWO
901
900
  return variation if variation && variation['name']
902
901
  else
903
902
  @logger.log(
904
- LogLevelEnum::INFO,
905
- format(
906
- LogMessageEnum::InfoMessages::WHITELISTING_SKIPPED,
907
- file: FILE,
908
- campaign_key: campaign_key,
909
- user_id: user_id,
910
- api_name: api_name
911
- ),
903
+ LogLevelEnum::DEBUG,
904
+ 'WHITELISTING_SKIPPED',
905
+ {
906
+ '{file}' => FILE,
907
+ '{campaignKey}' => campaign_key,
908
+ '{userId}' => user_id,
909
+ '{reason}' => '',
910
+ '{variation}' => ''
911
+ },
912
912
  disable_logs
913
913
  )
914
914
  end
915
915
  nil
916
916
  end
917
-
918
917
  end
919
918
  end
920
919
  end