vwo-sdk 1.5.0 → 1.15.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4e24789df43b6c0c71d71f8211c44ffe12c113e91943b91f8de581214353ccce
4
- data.tar.gz: 95ec02ba3df3e88f91bf3f49eb5ab3ab64302b2c664d4d3caa6f569f2e618952
3
+ metadata.gz: 2307340499cf5f0fd3fd13ab5025fc4892721123c82bc3df8eff266362d01c82
4
+ data.tar.gz: a0a3343ef9af102444cd9a6dd590937d5c737a3ed46765d3bd9cb8e18ceb41bf
5
5
  SHA512:
6
- metadata.gz: 1dcc6b57a5fd35867d58d71f3f4b73b0da3ca73f01d009493f76b8981562e6d315f004a9095bcd20f230de1bf19559ac0a2ffefaa5befb72abae0e61de8fce6d
7
- data.tar.gz: a7b154c4d9ae6c5b681c6cb27b231f3183a21c496e3eb5d33eecf81df0ca1d55922db127db37debe175aa060575f56dfe42ddbff29c0843e69839bcb57e4fc8c
6
+ metadata.gz: f1c28b3c5b86fa2b4488b5995a9931aaea708d1788f803c079f2fc5684fca81d82465f798e9653d227d448d6c08688791cf0740f774526ed38bd345e252b91fa
7
+ data.tar.gz: e4d2fcfca37407b7ed7ff008bc2d2d6b03e7f6bddd2f653825fd059306de36254ee4d46165e0973524e701d62e9a972c11a586089c5279027fb4ea5dc589060f
data/lib/vwo/constants.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2019-2020 Wingify Software Pvt. Ltd.
1
+ # Copyright 2019-2021 Wingify Software Pvt. Ltd.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -26,20 +26,29 @@ class VWO
26
26
  HTTP_PROTOCOL = 'http://'
27
27
  HTTPS_PROTOCOL = 'https://'
28
28
  URL_NAMESPACE = '6ba7b811-9dad-11d1-80b4-00c04fd430c8'
29
- SDK_VERSION = '1.5.0'
29
+ SDK_VERSION = '1.15.0'
30
30
  SDK_NAME = 'ruby'
31
+ VWO_DELIMITER = '_vwo_'
32
+ MAX_EVENTS_PER_REQUEST = 5000
33
+ MIN_EVENTS_PER_REQUEST = 1
34
+ DEFAULT_EVENTS_PER_REQUEST = 100
35
+ DEFAULT_REQUEST_TIME_INTERVAL = 600 # 10 * 60(secs) = 600 secs i.e. 10 minutes
36
+ MIN_REQUEST_TIME_INTERVAL = 2
31
37
 
32
38
  module ENDPOINTS
33
39
  BASE_URL = 'dev.visualwebsiteoptimizer.com'
34
- ACCOUNT_SETTINGS = '/server-side/settings'
40
+ SETTINGS_URL = '/server-side/settings'
41
+ WEBHOOK_SETTINGS_URL = '/server-side/pull'
35
42
  TRACK_USER = '/server-side/track-user'
36
43
  TRACK_GOAL = '/server-side/track-goal'
37
44
  PUSH = '/server-side/push'
45
+ BATCH_EVENTS = '/server-side/batch-events'
38
46
  end
39
47
 
40
48
  module EVENTS
41
49
  TRACK_USER = 'track-user'
42
50
  TRACK_GOAL = 'track-goal'
51
+ PUSH = 'push'
43
52
  end
44
53
 
45
54
  module DATATYPE
@@ -61,6 +70,12 @@ class VWO
61
70
  BOOLEAN = 'boolean'
62
71
  end
63
72
 
73
+ module Hooks
74
+ DECISION_TYPES = {
75
+ 'CAMPAIGN_DECISION' => 'CAMPAIGN_DECISION'
76
+ }
77
+ end
78
+
64
79
  RUBY_VARIABLE_TYPES = {
65
80
  'string' => [String],
66
81
  'integer' => [Integer],
@@ -68,6 +83,12 @@ class VWO
68
83
  'boolean' => [TrueClass, FalseClass]
69
84
  }
70
85
 
86
+ GOAL_TYPES = {
87
+ 'REVENUE' => 'REVENUE_TRACKING',
88
+ 'CUSTOM' => 'CUSTOM_GOAL',
89
+ 'ALL' => 'ALL'
90
+ }
91
+
71
92
  module ApiMethods
72
93
  ACTIVATE = 'activate'
73
94
  GET_VARIATION_NAME = 'get_variation_name'
@@ -75,6 +96,8 @@ class VWO
75
96
  IS_FEATURE_ENABLED = 'is_feature_enabled'
76
97
  GET_FEATURE_VARIABLE_VALUE = 'get_feature_variable_value'
77
98
  PUSH = 'push'
99
+ GET_AND_UPDATE_SETTINGS_FILE = 'get_and_update_settings_file'
100
+ FLUSH_EVENTS = 'flush_events'
78
101
  end
79
102
 
80
103
  module PushApi
@@ -1,4 +1,4 @@
1
- # Copyright 2019-2020 Wingify Software Pvt. Ltd.
1
+ # Copyright 2019-2021 Wingify Software Pvt. Ltd.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -116,11 +116,9 @@ class VWO
116
116
  )
117
117
  )
118
118
 
119
- get_variation(campaign, bucket_value)
119
+ get_variation(campaign['variations'], bucket_value)
120
120
  end
121
121
 
122
- private
123
-
124
122
  # Returns the Variation by checking the Start and End
125
123
  # Bucket Allocations of each Variation
126
124
  #
@@ -128,8 +126,8 @@ class VWO
128
126
  # @param[Integer] :bucket_value The bucket Value of the user
129
127
  # @return[Hash|nil] Variation data allotted to the user or None if not
130
128
  #
131
- def get_variation(campaign, bucket_value)
132
- campaign['variations'].find do |variation|
129
+ def get_variation(variations, bucket_value)
130
+ variations.find do |variation|
133
131
  (variation['start_variation_allocation']..variation['end_variation_allocation']).cover?(bucket_value)
134
132
  end
135
133
  end
@@ -1,4 +1,4 @@
1
- # Copyright 2019-2020 Wingify Software Pvt. Ltd.
1
+ # Copyright 2019-2021 Wingify Software Pvt. Ltd.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -18,15 +18,20 @@ require_relative '../utils/campaign'
18
18
  require_relative '../services/segment_evaluator'
19
19
  require_relative '../utils/validations'
20
20
  require_relative 'bucketer'
21
+ require_relative '../constants'
22
+ require_relative '../services/hooks_manager'
23
+ require_relative '../utils/uuid'
21
24
 
22
25
  class VWO
23
26
  module Core
24
27
  class VariationDecider
25
- attr_reader :user_storage_service
28
+ attr_reader :user_storage_service, :has_stored_variation, :hooks_manager
26
29
 
27
30
  include VWO::Enums
28
31
  include VWO::Utils::Campaign
29
32
  include VWO::Utils::Validations
33
+ include VWO::CONSTANTS
34
+ include VWO::Utils::UUID
30
35
 
31
36
  FILE = FileNameEnum::VariationDecider
32
37
 
@@ -34,15 +39,17 @@ class VWO
34
39
  # @param[Hash] - Settings file
35
40
  # @param[Class] - Class instance having the capability of
36
41
  # get and save.
37
- def initialize(settings_file, user_storage_service = nil)
42
+ def initialize(settings_file, user_storage_service = nil, options = {})
38
43
  @logger = VWO::Logger.get_instance
39
44
  @user_storage_service = user_storage_service
40
45
  @bucketer = VWO::Core::Bucketer.new
41
46
  @settings_file = settings_file
42
47
  @segment_evaluator = VWO::Services::SegmentEvaluator.new
48
+ @hooks_manager = VWO::Services::HooksManager.new(options)
43
49
  end
44
50
 
45
51
  # Returns variation for the user for the passed campaign-key
52
+ # Check if Whitelisting is applicable, evaluate it, if any eligible variation is found,return, otherwise skip it
46
53
  # Check in User Storage, if user found, validate variation and return
47
54
  # Otherwise, proceed with variation assignment logic
48
55
  #
@@ -50,16 +57,110 @@ class VWO
50
57
  # @param[String] :user_id The unique ID assigned to User
51
58
  # @param[Hash] :campaign Campaign hash itself
52
59
  # @param[String] :campaign_key The unique ID of the campaign passed
60
+ # @param[String] :goal_identifier The unique campaign's goal identifier
53
61
  # @return[String,String] ({variation_id, variation_name}|Nil): Tuple of
54
62
  # variation_id and variation_name if variation allotted, else nil
55
63
 
56
- def get_variation(user_id, campaign, campaign_key, custom_variables = {})
64
+ def get_variation(user_id, campaign, api_name, campaign_key, custom_variables = {}, variation_targeting_variables = {}, goal_identifier = '')
57
65
  campaign_key ||= campaign['key']
58
66
 
67
+ return unless campaign
68
+
69
+ @has_stored_variation = false
70
+ decision = {
71
+ :campaign_id => campaign['id'],
72
+ :campaign_key => campaign_key,
73
+ :campaign_type => campaign['type'],
74
+ # campaign segmentation conditions
75
+ :custom_variables => custom_variables,
76
+ # event name
77
+ :event => Hooks::DECISION_TYPES['CAMPAIGN_DECISION'],
78
+ # goal tracked in case of track API
79
+ :goal_identifier => goal_identifier,
80
+ # campaign whitelisting flag
81
+ :is_forced_variation_enabled => campaign['isForcedVariationEnabled'] ? campaign['isForcedVariationEnabled'] : false,
82
+ :sdk_version => SDK_VERSION,
83
+ # API name which triggered the event
84
+ :source => api_name,
85
+ # Passed in API
86
+ :user_id => user_id,
87
+ # Campaign Whitelisting conditions
88
+ :variation_targeting_variables => variation_targeting_variables,
89
+ :is_user_whitelisted => false,
90
+ :from_user_storage_service => false,
91
+ :is_feature_enabled => true,
92
+ # VWO generated UUID based on passed UserId and Account ID
93
+ :vwo_user_id => generator_for(user_id, @settings_file['accountId'])
94
+ }
95
+
96
+ if campaign['isForcedVariationEnabled']
97
+ variation = evaluate_whitelisting(
98
+ user_id,
99
+ campaign,
100
+ api_name,
101
+ campaign_key,
102
+ variation_targeting_variables
103
+ )
104
+ status = if variation
105
+ StatusEnum::PASSED
106
+ else
107
+ StatusEnum::FAILED
108
+ end
109
+
110
+ @logger.log(
111
+ LogLevelEnum::INFO,
112
+ format(
113
+ LogMessageEnum::InfoMessages::SEGMENTATION_STATUS,
114
+ file: FILE,
115
+ campaign_key: campaign_key,
116
+ user_id: user_id,
117
+ status: status,
118
+ custom_variables: variation_targeting_variables,
119
+ variation_name: status == StatusEnum::PASSED ? "and #{variation['name']} is Assigned" : ' ',
120
+ segmentation_type: SegmentationTypeEnum::WHITELISTING,
121
+ api_name: api_name
122
+ )
123
+ )
124
+
125
+ if variation
126
+ if campaign['type'] == CampaignTypes::VISUAL_AB || campaign['type'] == CampaignTypes::FEATURE_TEST
127
+ decision[:variation_name] = variation['name']
128
+ decision[:variation_id] = variation['id']
129
+ if campaign['type'] == CampaignTypes::FEATURE_TEST
130
+ decision[:is_feature_enabled] = variation['isFeatureEnabled']
131
+ elsif campaign['type'] == CampaignTypes::VISUAL_AB
132
+ decision[:is_user_whitelisted] = !!variation['name']
133
+ end
134
+ end
135
+ @hooks_manager.execute(decision)
136
+ end
137
+
138
+ return variation if variation && variation['name']
139
+ else
140
+ @logger.log(
141
+ LogLevelEnum::INFO,
142
+ format(
143
+ LogMessageEnum::InfoMessages::WHITELISTING_SKIPPED,
144
+ file: FILE,
145
+ campaign_key: campaign_key,
146
+ user_id: user_id,
147
+ api_name: api_name
148
+ )
149
+ )
150
+ end
151
+
59
152
  user_campaign_map = get_user_storage(user_id, campaign_key)
60
153
  variation = get_stored_variation(user_id, campaign_key, user_campaign_map) if valid_hash?(user_campaign_map)
61
154
 
62
155
  if variation
156
+ variation = variation.dup # deep copy
157
+ end
158
+
159
+ if variation
160
+ if valid_string?(user_campaign_map['goal_identifier']) && api_name == ApiMethods::TRACK
161
+ variation['goal_identifier'] = user_campaign_map['goal_identifier']
162
+ end
163
+ @has_stored_variation = true
63
164
  @logger.log(
64
165
  LogLevelEnum::INFO,
65
166
  format(
@@ -70,11 +171,58 @@ class VWO
70
171
  variation_name: variation['name']
71
172
  )
72
173
  )
174
+ decision[:from_user_storage_service] = !!variation['name']
175
+ if variation
176
+ if campaign['type'] == CampaignTypes::VISUAL_AB || campaign['type'] == CampaignTypes::FEATURE_TEST
177
+ decision[:variation_name] = variation['name']
178
+ decision[:variation_id] = variation['id']
179
+ if campaign['type'] == CampaignTypes::FEATURE_TEST
180
+ decision[:is_feature_enabled] = variation['isFeatureEnabled']
181
+ end
182
+ end
183
+ @hooks_manager.execute(decision)
184
+ end
73
185
  return variation
186
+ else
187
+ @logger.log(
188
+ LogLevelEnum::DEBUG,
189
+ format(
190
+ LogMessageEnum::DebugMessages::NO_STORED_VARIATION,
191
+ file: FILE,
192
+ campaign_key: campaign_key,
193
+ user_id: user_id
194
+ )
195
+ )
196
+
197
+ if ([ApiMethods::TRACK, ApiMethods::GET_VARIATION_NAME, ApiMethods::GET_FEATURE_VARIABLE_VALUE].include? api_name) &&
198
+ @user_storage_service && campaign['type'] != CampaignTypes::FEATURE_ROLLOUT
199
+ @logger.log(
200
+ LogLevelEnum::DEBUG,
201
+ format(
202
+ LogMessageEnum::DebugMessages::CAMPAIGN_NOT_ACTIVATED,
203
+ file: FILE,
204
+ campaign_key: campaign_key,
205
+ user_id: user_id,
206
+ api_name: api_name
207
+ )
208
+ )
209
+
210
+ @logger.log(
211
+ LogLevelEnum::INFO,
212
+ format(
213
+ LogMessageEnum::InfoMessages::CAMPAIGN_NOT_ACTIVATED,
214
+ file: FILE,
215
+ campaign_key: campaign_key,
216
+ user_id: user_id,
217
+ api_name: api_name,
218
+ reason: api_name == ApiMethods::TRACK ? 'track it' : 'get the decision/value'
219
+ )
220
+ )
221
+ return
222
+ end
74
223
  end
75
224
 
76
225
  # Pre-segmentation
77
- return unless campaign
78
226
 
79
227
  segments = get_segments(campaign)
80
228
  is_valid_segments = valid_value?(segments)
@@ -88,21 +236,44 @@ class VWO
88
236
  file: FILE,
89
237
  campaign_key: campaign_key,
90
238
  user_id: user_id,
91
- api_name: ApiMethods::GET_FEATURE_VARIABLE_VALUE
239
+ api_name: api_name
92
240
  )
93
241
  )
94
242
  custom_variables = {}
95
243
  end
96
- return unless @segment_evaluator.evaluate(campaign_key, user_id, segments, custom_variables)
244
+ unless @segment_evaluator.evaluate(campaign_key, user_id, segments, custom_variables)
245
+ @logger.log(
246
+ LogLevelEnum::INFO,
247
+ format(
248
+ LogMessageEnum::InfoMessages::USER_FAILED_SEGMENTATION,
249
+ file: FileNameEnum::SegmentEvaluator,
250
+ user_id: user_id,
251
+ campaign_key: campaign_key,
252
+ custom_variables: custom_variables
253
+ )
254
+ )
255
+ return
256
+ end
257
+ @logger.log(
258
+ LogLevelEnum::INFO,
259
+ format(
260
+ LogMessageEnum::InfoMessages::USER_PASSED_SEGMENTATION,
261
+ file: FileNameEnum::SegmentEvaluator,
262
+ user_id: user_id,
263
+ campaign_key: campaign_key,
264
+ custom_variables: custom_variables
265
+ )
266
+ )
97
267
  else
98
268
  @logger.log(
99
269
  LogLevelEnum::INFO,
100
270
  format(
101
- LogMessageEnum::InfoMessages::SKIPPING_PRE_SEGMENTATION,
271
+ LogMessageEnum::InfoMessages::SKIPPING_SEGMENTATION,
102
272
  file: FILE,
103
273
  campaign_key: campaign_key,
104
274
  user_id: user_id,
105
- api_name: ApiMethods::GET_FEATURE_VARIABLE_VALUE
275
+ api_name: api_name,
276
+ variation: ''
106
277
  )
107
278
  )
108
279
  end
@@ -110,7 +281,7 @@ class VWO
110
281
  variation = get_variation_allotted(user_id, campaign)
111
282
 
112
283
  if variation && variation['name']
113
- save_user_storage(user_id, campaign_key, variation['name']) if variation['name']
284
+ save_user_storage(user_id, campaign_key, variation['name'], goal_identifier) if variation['name']
114
285
 
115
286
  @logger.log(
116
287
  LogLevelEnum::INFO,
@@ -129,6 +300,17 @@ class VWO
129
300
  format(LogMessageEnum::InfoMessages::NO_VARIATION_ALLOCATED, file: FILE, campaign_key: campaign_key, user_id: user_id)
130
301
  )
131
302
  end
303
+
304
+ if variation
305
+ if campaign['type'] == CampaignTypes::VISUAL_AB || campaign['type'] == CampaignTypes::FEATURE_TEST
306
+ decision[:variation_name] = variation['name']
307
+ decision[:variation_id] = variation['id']
308
+ if campaign['type'] == CampaignTypes::FEATURE_TEST
309
+ decision[:is_feature_enabled] = variation['isFeatureEnabled']
310
+ end
311
+ end
312
+ @hooks_manager.execute(decision)
313
+ end
132
314
  variation
133
315
  end
134
316
 
@@ -183,6 +365,7 @@ class VWO
183
365
  # @param[String] :user_id The unique ID assigned to a user
184
366
  # @param[Hash] :campaign The Campaign of which user is to be made a part of
185
367
  # @return[Hash] Variation allotted to User
368
+
186
369
  def get_variation_of_campaign_for_user(user_id, campaign)
187
370
  unless campaign
188
371
  @logger.log(
@@ -224,8 +407,152 @@ class VWO
224
407
  nil
225
408
  end
226
409
 
410
+ # If UserStorageService is provided, save the assigned variation
411
+ #
412
+ # @param[String] :user_id Unique user identifier
413
+ # @param[String] :campaign_key Unique campaign identifier
414
+ # @param[String] :variation_name Variation identifier
415
+ # @param[String] :goal_identifier The unique campaign's goal identifier
416
+ # @return[Boolean] true if found otherwise false
417
+
418
+ def save_user_storage(user_id, campaign_key, variation_name, goal_identifier)
419
+ unless @user_storage_service
420
+ @logger.log(
421
+ LogLevelEnum::DEBUG,
422
+ format(LogMessageEnum::DebugMessages::NO_USER_STORAGE_SERVICE_SAVE, file: FILE)
423
+ )
424
+ return false
425
+ end
426
+ new_campaign_user_mapping = {}
427
+ new_campaign_user_mapping['campaign_key'] = campaign_key
428
+ new_campaign_user_mapping['user_id'] = user_id
429
+ new_campaign_user_mapping['variation_name'] = variation_name
430
+ if !goal_identifier.empty?
431
+ new_campaign_user_mapping['goal_identifier'] = goal_identifier
432
+ end
433
+
434
+ @user_storage_service.set(new_campaign_user_mapping)
435
+
436
+ @logger.log(
437
+ LogLevelEnum::INFO,
438
+ format(LogMessageEnum::InfoMessages::SAVING_DATA_USER_STORAGE_SERVICE, file: FILE, user_id: user_id)
439
+ )
440
+ true
441
+ rescue StandardError
442
+ @logger.log(
443
+ LogLevelEnum::ERROR,
444
+ format(LogMessageEnum::ErrorMessages::SAVE_USER_STORAGE_SERVICE_FAILED, file: FILE, user_id: user_id)
445
+ )
446
+ false
447
+ end
448
+
227
449
  private
228
450
 
451
+ # Evaluate all the variations in the campaign to find
452
+ #
453
+ # @param[String] :user_id The unique key assigned to User
454
+ # @param[Hash] :campaign Campaign hash for Unique campaign key
455
+ # @param[String] :api_name The key Passed to identify the calling API
456
+ # @param[String] :campaign_key Unique campaign key
457
+ # @param[Hash] :variation_targeting_variables Key/value pair of Whitelisting Custom Attributes
458
+ #
459
+ # @return[Hash]
460
+
461
+ def evaluate_whitelisting(user_id, campaign, api_name, campaign_key, variation_targeting_variables = {})
462
+ if variation_targeting_variables.nil?
463
+ variation_targeting_variables = { '_vwo_user_id' => user_id }
464
+ else
465
+ variation_targeting_variables['_vwo_user_id'] = user_id
466
+ end
467
+ targeted_variations = []
468
+
469
+ campaign['variations'].each do |variation|
470
+ segments = get_segments(variation)
471
+ is_valid_segments = valid_value?(segments)
472
+ if is_valid_segments
473
+ if @segment_evaluator.evaluate(campaign_key, user_id, segments, variation_targeting_variables)
474
+ targeted_variations.push(variation)
475
+ status = StatusEnum::PASSED
476
+ else
477
+ status = StatusEnum::FAILED
478
+ end
479
+ @logger.log(
480
+ LogLevelEnum::DEBUG,
481
+ format(
482
+ LogMessageEnum::DebugMessages::SEGMENTATION_STATUS,
483
+ file: FILE,
484
+ campaign_key: campaign_key,
485
+ user_id: user_id,
486
+ status: status,
487
+ custom_variables: variation_targeting_variables,
488
+ variation_name: variation['name'],
489
+ segmentation_type: SegmentationTypeEnum::WHITELISTING,
490
+ api_name: api_name
491
+ )
492
+ )
493
+ else
494
+ @logger.log(
495
+ LogLevelEnum::DEBUG,
496
+ format(
497
+ LogMessageEnum::InfoMessages::SKIPPING_SEGMENTATION,
498
+ file: FILE,
499
+ campaign_key: campaign_key,
500
+ user_id: user_id,
501
+ api_name: api_name,
502
+ variation: variation['name']
503
+ )
504
+ )
505
+ end
506
+ end
507
+
508
+ if targeted_variations.length > 1
509
+ targeted_variations_deep_clone = Marshal.load(Marshal.dump(targeted_variations))
510
+ scale_variation_weights(targeted_variations_deep_clone)
511
+ current_allocation = 0
512
+ targeted_variations_deep_clone.each do |variation|
513
+ step_factor = get_variation_bucketing_range(variation['weight'])
514
+ if step_factor > 0
515
+ start_range = current_allocation + 1
516
+ end_range = current_allocation + step_factor
517
+ variation['start_variation_allocation'] = start_range
518
+ variation['end_variation_allocation'] = end_range
519
+ current_allocation += step_factor
520
+ else
521
+ variation['start_variation_allocation'] = -1
522
+ variation['end_variation_allocation'] = -1
523
+ end
524
+ end
525
+ whitelisted_variation = @bucketer.get_variation(
526
+ targeted_variations_deep_clone,
527
+ @bucketer.get_bucket_value_for_user(
528
+ user_id
529
+ )
530
+ )
531
+ else
532
+ whitelisted_variation = targeted_variations[0]
533
+ end
534
+ whitelisted_variation
535
+ end
536
+
537
+ # It extracts the weights from all the variations inside the campaign
538
+ # and scales them so that the total sum of eligible variations' weights become 100%
539
+ #
540
+ # 1. variations
541
+
542
+ def scale_variation_weights(variations)
543
+ total_weight = variations.reduce(0) { |final_weight, variation| final_weight + variation['weight'].to_f }
544
+ if total_weight == 0
545
+ weight = 100 / variations.length
546
+ variations.each do |variation|
547
+ variation['weight'] = weight
548
+ end
549
+ else
550
+ variations.each do |variation|
551
+ variation['weight'] = (variation['weight'] / total_weight) * 100
552
+ end
553
+ end
554
+ end
555
+
229
556
  # Get the UserStorageData after looking up into get method
230
557
  # Being provided via UserStorageService
231
558
  #
@@ -290,41 +617,6 @@ class VWO
290
617
  variation_name
291
618
  )
292
619
  end
293
-
294
- # If UserStorageService is provided, save the assigned variation
295
- #
296
- # @param[String] :user_id Unique user identifier
297
- # @param[String] :campaign_key Unique campaign identifier
298
- # @param[String] :variation_name Variation identifier
299
- # @return[Boolean] true if found otherwise false
300
-
301
- def save_user_storage(user_id, campaign_key, variation_name)
302
- unless @user_storage_service
303
- @logger.log(
304
- LogLevelEnum::DEBUG,
305
- format(LogMessageEnum::DebugMessages::NO_USER_STORAGE_SERVICE_SAVE, file: FILE)
306
- )
307
- return false
308
- end
309
- new_campaign_user_mapping = {}
310
- new_campaign_user_mapping['campaign_key'] = campaign_key
311
- new_campaign_user_mapping['user_id'] = user_id
312
- new_campaign_user_mapping['variation_name'] = variation_name
313
-
314
- @user_storage_service.set(new_campaign_user_mapping)
315
-
316
- @logger.log(
317
- LogLevelEnum::INFO,
318
- format(LogMessageEnum::InfoMessages::SAVING_DATA_USER_STORAGE_SERVICE, file: FILE, user_id: user_id)
319
- )
320
- true
321
- rescue StandardError
322
- @logger.log(
323
- LogLevelEnum::ERROR,
324
- format(LogMessageEnum::ErrorMessages::SAVE_USER_STORAGE_SERVICE_FAILED, file: FILE, user_id: user_id)
325
- )
326
- false
327
- end
328
620
  end
329
621
  end
330
622
  end