vwo-sdk 1.5.0 → 1.15.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  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