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 +4 -4
- data/lib/vwo/constants.rb +26 -3
- data/lib/vwo/core/bucketer.rb +4 -6
- data/lib/vwo/core/variation_decider.rb +337 -45
- data/lib/vwo/enums.rb +49 -9
- data/lib/vwo/logger.rb +1 -1
- data/lib/vwo/schemas/settings_file.rb +1 -1
- data/lib/vwo/services/batch_events_dispatcher.rb +110 -0
- data/lib/vwo/services/batch_events_queue.rb +175 -0
- data/lib/vwo/services/event_dispatcher.rb +1 -13
- data/lib/vwo/services/hooks_manager.rb +36 -0
- data/lib/vwo/services/operand_evaluator.rb +10 -2
- data/lib/vwo/services/segment_evaluator.rb +5 -26
- data/lib/vwo/services/settings_file_manager.rb +8 -4
- data/lib/vwo/services/settings_file_processor.rb +6 -1
- data/lib/vwo/services/usage_stats.rb +29 -0
- data/lib/vwo/user_storage.rb +1 -1
- data/lib/vwo/utils/campaign.rb +108 -1
- data/lib/vwo/utils/custom_dimensions.rb +26 -3
- data/lib/vwo/utils/feature.rb +1 -1
- data/lib/vwo/utils/function.rb +1 -1
- data/lib/vwo/utils/impression.rb +58 -7
- data/lib/vwo/utils/request.rb +15 -1
- data/lib/vwo/utils/segment.rb +1 -1
- data/lib/vwo/utils/uuid.rb +1 -1
- data/lib/vwo/utils/validations.rb +85 -1
- data/lib/vwo.rb +586 -203
- metadata +36 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2307340499cf5f0fd3fd13ab5025fc4892721123c82bc3df8eff266362d01c82
|
4
|
+
data.tar.gz: a0a3343ef9af102444cd9a6dd590937d5c737a3ed46765d3bd9cb8e18ceb41bf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f1c28b3c5b86fa2b4488b5995a9931aaea708d1788f803c079f2fc5684fca81d82465f798e9653d227d448d6c08688791cf0740f774526ed38bd345e252b91fa
|
7
|
+
data.tar.gz: e4d2fcfca37407b7ed7ff008bc2d2d6b03e7f6bddd2f653825fd059306de36254ee4d46165e0973524e701d62e9a972c11a586089c5279027fb4ea5dc589060f
|
data/lib/vwo/constants.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright 2019-
|
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.
|
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
|
-
|
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
|
data/lib/vwo/core/bucketer.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright 2019-
|
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(
|
132
|
-
|
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-
|
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:
|
239
|
+
api_name: api_name
|
92
240
|
)
|
93
241
|
)
|
94
242
|
custom_variables = {}
|
95
243
|
end
|
96
|
-
|
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::
|
271
|
+
LogMessageEnum::InfoMessages::SKIPPING_SEGMENTATION,
|
102
272
|
file: FILE,
|
103
273
|
campaign_key: campaign_key,
|
104
274
|
user_id: user_id,
|
105
|
-
api_name:
|
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
|