vwo-sdk 1.30.0 → 1.37.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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