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 +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
|