vwo-sdk 1.36.0 → 1.37.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/vwo/constants.rb +2 -2
- data/lib/vwo/core/bucketer.rb +18 -26
- data/lib/vwo/core/variation_decider.rb +216 -220
- data/lib/vwo/enums.rb +16 -19
- data/lib/vwo/logger.rb +2 -3
- data/lib/vwo/schemas/settings_file.rb +6 -6
- data/lib/vwo/services/batch_events_dispatcher.rb +19 -21
- data/lib/vwo/services/batch_events_queue.rb +27 -43
- data/lib/vwo/services/event_dispatcher.rb +31 -21
- data/lib/vwo/services/hooks_manager.rb +8 -12
- data/lib/vwo/services/segment_evaluator.rb +7 -6
- data/lib/vwo/services/settings_file_manager.rb +5 -5
- data/lib/vwo/services/settings_file_processor.rb +1 -1
- data/lib/vwo/services/usage_stats.rb +5 -4
- data/lib/vwo/utils/campaign.rb +37 -39
- data/lib/vwo/utils/custom_dimensions.rb +2 -2
- data/lib/vwo/utils/data_location_manager.rb +2 -9
- data/lib/vwo/utils/feature.rb +2 -2
- data/lib/vwo/utils/impression.rb +29 -37
- data/lib/vwo/utils/log_message.rb +23 -30
- data/lib/vwo/utils/request.rb +7 -9
- data/lib/vwo/utils/utility.rb +44 -34
- data/lib/vwo/utils/validations.rb +72 -5
- data/lib/vwo.rb +197 -344
- metadata +8 -8
@@ -33,7 +33,7 @@ class VWO
|
|
33
33
|
include VWO::CONSTANTS
|
34
34
|
include VWO::Utils::UUID
|
35
35
|
|
36
|
-
FILE = FileNameEnum::
|
36
|
+
FILE = FileNameEnum::VARIATION_DECIDER
|
37
37
|
|
38
38
|
# Initializes various services
|
39
39
|
# @param[Hash] - Settings file
|
@@ -66,43 +66,17 @@ class VWO
|
|
66
66
|
|
67
67
|
return unless campaign
|
68
68
|
|
69
|
-
is_campaign_part_of_group = @settings_file &&
|
69
|
+
is_campaign_part_of_group = @settings_file && part_of_group?(@settings_file, campaign['id'])
|
70
70
|
|
71
71
|
@has_stored_variation = false
|
72
|
-
decision =
|
73
|
-
:campaign_id => campaign['id'],
|
74
|
-
:campaign_key => campaign_key,
|
75
|
-
:campaign_type => campaign['type'],
|
76
|
-
# campaign segmentation conditions
|
77
|
-
:custom_variables => custom_variables,
|
78
|
-
# event name
|
79
|
-
:event => Hooks::DECISION_TYPES['CAMPAIGN_DECISION'],
|
80
|
-
# goal tracked in case of track API
|
81
|
-
:goal_identifier => goal_identifier,
|
82
|
-
# campaign whitelisting flag
|
83
|
-
:is_forced_variation_enabled => campaign['isForcedVariationEnabled'] ? campaign['isForcedVariationEnabled'] : false,
|
84
|
-
:sdk_version => SDK_VERSION,
|
85
|
-
# API name which triggered the event
|
86
|
-
:source => api_name,
|
87
|
-
# Passed in API
|
88
|
-
:user_id => user_id,
|
89
|
-
# Campaign Whitelisting conditions
|
90
|
-
:variation_targeting_variables => variation_targeting_variables,
|
91
|
-
:is_user_whitelisted => false,
|
92
|
-
:from_user_storage_service => false,
|
93
|
-
:is_feature_enabled => true,
|
94
|
-
# VWO generated UUID based on passed UserId and Account ID
|
95
|
-
:vwo_user_id => generator_for(user_id, @settings_file['accountId'])
|
96
|
-
}
|
72
|
+
decision = initialize_decision_properties(user_id, campaign, api_name, custom_variables, variation_targeting_variables, goal_identifier)
|
97
73
|
|
98
|
-
if campaign.
|
99
|
-
decision[:campaign_name] = campaign['name']
|
100
|
-
end
|
74
|
+
decision[:campaign_name] = campaign['name'] if campaign.key?('name')
|
101
75
|
|
102
76
|
if is_campaign_part_of_group
|
103
|
-
group_id = @settings_file[
|
77
|
+
group_id = @settings_file['campaignGroups'][campaign['id'].to_s]
|
104
78
|
decision[:group_id] = group_id
|
105
|
-
group_name = @settings_file[
|
79
|
+
group_name = @settings_file['groups'][group_id.to_s]['name']
|
106
80
|
decision[:group_name] = group_name
|
107
81
|
end
|
108
82
|
|
@@ -110,21 +84,17 @@ class VWO
|
|
110
84
|
variation = get_variation_if_whitelisting_passed(user_id, campaign, variation_targeting_variables, api_name, decision, true)
|
111
85
|
return variation if variation && variation['name']
|
112
86
|
|
113
|
-
if campaign.
|
114
|
-
is_presegmentation =
|
115
|
-
return
|
87
|
+
if campaign.key?('isAlwaysCheckSegment')
|
88
|
+
is_presegmentation = presegmentation?(campaign, user_id, custom_variables, api_name)
|
89
|
+
return get_variation_if_presegmentation_applied(is_presegmentation, campaign, user_id, goal_identifier, decision)
|
116
90
|
else
|
117
91
|
user_campaign_map = get_user_storage(user_id, campaign_key)
|
118
92
|
variation = get_stored_variation(user_id, campaign_key, user_campaign_map) if valid_hash?(user_campaign_map)
|
119
93
|
|
94
|
+
variation = variation.dup if variation # deep copy
|
95
|
+
|
120
96
|
if variation
|
121
|
-
variation =
|
122
|
-
end
|
123
|
-
|
124
|
-
if variation
|
125
|
-
if valid_string?(user_campaign_map['goal_identifier']) && api_name == ApiMethods::TRACK
|
126
|
-
variation['goal_identifier'] = user_campaign_map['goal_identifier']
|
127
|
-
end
|
97
|
+
variation['goal_identifier'] = user_campaign_map['goal_identifier'] if valid_string?(user_campaign_map['goal_identifier']) && api_name == ApiMethods::TRACK
|
128
98
|
@has_stored_variation = true
|
129
99
|
@logger.log(
|
130
100
|
LogLevelEnum::INFO,
|
@@ -137,16 +107,8 @@ class VWO
|
|
137
107
|
}
|
138
108
|
)
|
139
109
|
decision[:from_user_storage_service] = !!variation['name']
|
140
|
-
|
141
|
-
|
142
|
-
decision[:variation_name] = variation['name']
|
143
|
-
decision[:variation_id] = variation['id']
|
144
|
-
if campaign['type'] == CampaignTypes::FEATURE_TEST
|
145
|
-
decision[:is_feature_enabled] = variation['isFeatureEnabled']
|
146
|
-
end
|
147
|
-
end
|
148
|
-
@hooks_manager.execute(decision)
|
149
|
-
end
|
110
|
+
decision = add_variation_to_decision_properties(decision, campaign, variation)
|
111
|
+
@hooks_manager.execute(decision)
|
150
112
|
return variation
|
151
113
|
else
|
152
114
|
@logger.log(
|
@@ -158,48 +120,20 @@ class VWO
|
|
158
120
|
'{userId}' => user_id
|
159
121
|
}
|
160
122
|
)
|
161
|
-
|
162
|
-
if (
|
163
|
-
@logger.log(
|
164
|
-
LogLevelEnum::WARNING,
|
165
|
-
'CAMPAIGN_NOT_ACTIVATED',
|
166
|
-
{
|
167
|
-
'{file}' => FILE,
|
168
|
-
'{campaignKey}' => campaign_key,
|
169
|
-
'{userId}' => user_id,
|
170
|
-
'{api}' => api_name
|
171
|
-
}
|
172
|
-
)
|
173
|
-
|
174
|
-
@logger.log(
|
175
|
-
LogLevelEnum::INFO,
|
176
|
-
'CAMPAIGN_NOT_ACTIVATED',
|
177
|
-
{
|
178
|
-
'{file}' => FILE,
|
179
|
-
'{campaignKey}' => campaign_key,
|
180
|
-
'{userId}' => user_id,
|
181
|
-
'{reason}' => api_name == ApiMethods::TRACK ? 'track it' : 'get the decision/value'
|
182
|
-
}
|
183
|
-
)
|
184
|
-
return
|
185
|
-
end
|
123
|
+
|
124
|
+
return if campaign_not_activated?(user_id, campaign_key, api_name)
|
186
125
|
end
|
187
126
|
end
|
188
127
|
|
189
|
-
|
190
|
-
|
191
128
|
# Pre-segmentation
|
192
|
-
is_presegmentation =
|
129
|
+
is_presegmentation = presegmentation?(campaign, user_id, custom_variables, api_name)
|
193
130
|
is_presegmentation_and_traffic_passed = is_presegmentation && @bucketer.user_part_of_campaign?(user_id, campaign)
|
194
|
-
unless is_presegmentation_and_traffic_passed
|
195
|
-
return nil
|
196
|
-
end
|
131
|
+
return nil unless is_presegmentation_and_traffic_passed
|
197
132
|
|
198
133
|
if is_presegmentation_and_traffic_passed && is_campaign_part_of_group
|
199
134
|
group_campaigns = get_group_campaigns(@settings_file, group_id)
|
200
135
|
if group_campaigns
|
201
|
-
|
202
|
-
if is_any_campaign_whitelisted_or_stored
|
136
|
+
if whitelisting_or_storage_for_grouped_campaigns?(user_id, campaign, group_campaigns, group_name, variation_targeting_variables, true)
|
203
137
|
@logger.log(
|
204
138
|
LogLevelEnum::INFO,
|
205
139
|
'MEG_CALLED_CAMPAIGN_NOT_WINNER',
|
@@ -207,7 +141,7 @@ class VWO
|
|
207
141
|
'{file}' => FILE,
|
208
142
|
'{campaignKey}' => campaign_key,
|
209
143
|
'{userId}' => user_id,
|
210
|
-
'{groupName}' => group_name
|
144
|
+
'{groupName}' => group_name
|
211
145
|
}
|
212
146
|
)
|
213
147
|
return nil
|
@@ -220,8 +154,8 @@ class VWO
|
|
220
154
|
{
|
221
155
|
'{file}' => FILE,
|
222
156
|
'{userId}' => user_id,
|
223
|
-
'{eligibleCampaignKeys}' => get_eligible_campaigns_key(eligible_campaigns).join(
|
224
|
-
'{inEligibleText}' => non_eligible_campaigns_key ?
|
157
|
+
'{eligibleCampaignKeys}' => get_eligible_campaigns_key(eligible_campaigns).join(','),
|
158
|
+
'{inEligibleText}' => non_eligible_campaigns_key ? "campaigns: + #{non_eligible_campaigns_key.join("'")}" : 'no campaigns',
|
225
159
|
'{groupName}' => group_name
|
226
160
|
}
|
227
161
|
)
|
@@ -245,47 +179,123 @@ class VWO
|
|
245
179
|
{
|
246
180
|
'{file}' => FILE,
|
247
181
|
'{userId}' => user_id,
|
248
|
-
'{campaignKey}' => winner_campaign[
|
249
|
-
'{groupName}' => group_name
|
182
|
+
'{campaignKey}' => winner_campaign['key'],
|
183
|
+
'{groupName}' => group_name
|
250
184
|
}
|
251
185
|
)
|
252
186
|
|
253
|
-
if
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
end
|
260
|
-
else
|
261
|
-
@logger.log(
|
262
|
-
LogLevelEnum::INFO,
|
263
|
-
'MEG_CALLED_CAMPAIGN_NOT_WINNER',
|
264
|
-
{
|
265
|
-
'{file}' => FILE,
|
266
|
-
'{campaignKey}' => campaign_key,
|
267
|
-
'{userId}' => user_id,
|
268
|
-
'{groupName}' => group_name,
|
269
|
-
}
|
270
|
-
)
|
271
|
-
return nil
|
272
|
-
end
|
187
|
+
return if meg_called_campaign_not_winner?(user_id, group_name, campaign, winner_campaign)
|
188
|
+
|
189
|
+
variation = get_variation_allotted(user_id, campaign, true)
|
190
|
+
return nil unless variation && variation['name']
|
191
|
+
|
192
|
+
save_user_storage(user_id, campaign_key, campaign['type'], variation['name'], goal_identifier, true) if variation['name']
|
273
193
|
end
|
274
194
|
end
|
275
195
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
196
|
+
variation ||= get_variation_if_presegmentation_applied(is_presegmentation, campaign, user_id, goal_identifier, decision)
|
197
|
+
return unless variation
|
198
|
+
|
199
|
+
decision = add_variation_to_decision_properties(decision, campaign, variation)
|
200
|
+
@hooks_manager.execute(decision)
|
201
|
+
variation
|
202
|
+
end
|
203
|
+
|
204
|
+
def campaign_not_activated?(user_id, campaign_key, api_name)
|
205
|
+
if ([ApiMethods::TRACK, ApiMethods::GET_VARIATION_NAME, ApiMethods::GET_FEATURE_VARIABLE_VALUE].include? api_name) && @user_storage_service
|
206
|
+
@logger.log(
|
207
|
+
LogLevelEnum::WARNING,
|
208
|
+
'CAMPAIGN_NOT_ACTIVATED',
|
209
|
+
{
|
210
|
+
'{file}' => FILE,
|
211
|
+
'{campaignKey}' => campaign_key,
|
212
|
+
'{userId}' => user_id,
|
213
|
+
'{api}' => api_name
|
214
|
+
}
|
215
|
+
)
|
216
|
+
|
217
|
+
@logger.log(
|
218
|
+
LogLevelEnum::INFO,
|
219
|
+
'CAMPAIGN_NOT_ACTIVATED',
|
220
|
+
{
|
221
|
+
'{file}' => FILE,
|
222
|
+
'{campaignKey}' => campaign_key,
|
223
|
+
'{userId}' => user_id,
|
224
|
+
'{reason}' => api_name == ApiMethods::TRACK ? 'track it' : 'get the decision/value'
|
225
|
+
}
|
226
|
+
)
|
227
|
+
return true
|
228
|
+
end
|
229
|
+
false
|
230
|
+
end
|
231
|
+
|
232
|
+
def meg_called_campaign_not_winner?(user_id, group_name, campaign, winner_campaign)
|
233
|
+
unless winner_campaign && winner_campaign['id'] == campaign['id']
|
234
|
+
@logger.log(
|
235
|
+
LogLevelEnum::INFO,
|
236
|
+
'MEG_CALLED_CAMPAIGN_NOT_WINNER',
|
237
|
+
{
|
238
|
+
'{file}' => FILE,
|
239
|
+
'{campaignKey}' => campaign['key'],
|
240
|
+
'{userId}' => user_id,
|
241
|
+
'{groupName}' => group_name
|
242
|
+
}
|
243
|
+
)
|
244
|
+
return true
|
286
245
|
end
|
246
|
+
false
|
247
|
+
end
|
287
248
|
|
288
|
-
|
249
|
+
# Intitialize decision properties for hook manager
|
250
|
+
#
|
251
|
+
# @param[String] :user_id The unique ID assigned to User
|
252
|
+
# @param[Hash] :campaign Campaign hash itself
|
253
|
+
# @param[String] :api_name Name of the current api call
|
254
|
+
# @param[String] :goal_identifier The unique campaign's goal identifier
|
255
|
+
# @param[Hash] :custom_variables Key/value pair for segmentation
|
256
|
+
# @param[Hash] :variation_targeting_variables Key/value pair for whitelisting
|
257
|
+
# @return[Hash] Decision properties for the callback by hook manager
|
258
|
+
def initialize_decision_properties(user_id, campaign, api_name, custom_variables = {}, variation_targeting_variables = {}, goal_identifier = '')
|
259
|
+
{
|
260
|
+
campaign_id: campaign['id'],
|
261
|
+
campaign_key: campaign['key'],
|
262
|
+
campaign_type: campaign['type'],
|
263
|
+
# campaign segmentation conditions
|
264
|
+
custom_variables: custom_variables,
|
265
|
+
# event name
|
266
|
+
event: Hooks::DECISION_TYPES['CAMPAIGN_DECISION'],
|
267
|
+
# goal tracked in case of track API
|
268
|
+
goal_identifier: goal_identifier,
|
269
|
+
# campaign whitelisting flag
|
270
|
+
is_forced_variation_enabled: campaign['isForcedVariationEnabled'] || false,
|
271
|
+
sdk_version: SDK_VERSION,
|
272
|
+
# API name which triggered the event
|
273
|
+
source: api_name,
|
274
|
+
# Passed in API
|
275
|
+
user_id: user_id,
|
276
|
+
# Campaign Whitelisting conditions
|
277
|
+
variation_targeting_variables: variation_targeting_variables,
|
278
|
+
is_user_whitelisted: false,
|
279
|
+
from_user_storage_service: false,
|
280
|
+
is_feature_enabled: true,
|
281
|
+
# VWO generated UUID based on passed UserId and Account ID
|
282
|
+
vwo_user_id: generator_for(user_id, @settings_file['accountId'])
|
283
|
+
}
|
284
|
+
end
|
285
|
+
|
286
|
+
# Add variation details to decision properties for hook manager
|
287
|
+
#
|
288
|
+
# @param[Hash] :campaign Campaign details
|
289
|
+
# @param[Hash] :variation Variation assigned to user
|
290
|
+
# @param[Hash] :decision Decision properties
|
291
|
+
# @return[Hash] :decision Decision properties
|
292
|
+
def add_variation_to_decision_properties(decision, campaign, variation)
|
293
|
+
if campaign['type'] == CampaignTypes::VISUAL_AB || campaign['type'] == CampaignTypes::FEATURE_TEST
|
294
|
+
decision[:variation_name] = variation['name']
|
295
|
+
decision[:variation_id] = variation['id']
|
296
|
+
end
|
297
|
+
decision[:is_feature_enabled] = variation['isFeatureEnabled'] if campaign['type'] == CampaignTypes::FEATURE_TEST
|
298
|
+
decision
|
289
299
|
end
|
290
300
|
|
291
301
|
# Get variation by murmur logic if pre segmentation pass
|
@@ -297,10 +307,9 @@ class VWO
|
|
297
307
|
# @param[Hash] :decision data containing campaign info passed to hooks manager
|
298
308
|
#
|
299
309
|
# @return[Hash]
|
300
|
-
def
|
301
|
-
unless is_presegmentation
|
302
|
-
|
303
|
-
end
|
310
|
+
def get_variation_if_presegmentation_applied(is_presegmentation, campaign, user_id, goal_identifier, decision)
|
311
|
+
return nil unless is_presegmentation
|
312
|
+
|
304
313
|
campaign_key = campaign['key']
|
305
314
|
variation = get_variation_allotted(user_id, campaign)
|
306
315
|
if variation && variation['name']
|
@@ -318,13 +327,7 @@ class VWO
|
|
318
327
|
end
|
319
328
|
|
320
329
|
if variation
|
321
|
-
|
322
|
-
decision[:variation_name] = variation['name']
|
323
|
-
decision[:variation_id] = variation['id']
|
324
|
-
if campaign['type'] == CampaignTypes::FEATURE_TEST
|
325
|
-
decision[:is_feature_enabled] = variation['isFeatureEnabled']
|
326
|
-
end
|
327
|
-
end
|
330
|
+
decision = add_variation_to_decision_properties(decision, campaign, variation)
|
328
331
|
@hooks_manager.execute(decision)
|
329
332
|
end
|
330
333
|
variation
|
@@ -352,8 +355,8 @@ class VWO
|
|
352
355
|
end
|
353
356
|
|
354
357
|
if @bucketer.user_part_of_campaign?(user_id, campaign, true)
|
355
|
-
|
356
|
-
|
358
|
+
get_variation_of_campaign_for_user(user_id, campaign, disable_logs)
|
359
|
+
|
357
360
|
else
|
358
361
|
# not part of campaign
|
359
362
|
@logger.log(
|
@@ -385,7 +388,7 @@ class VWO
|
|
385
388
|
'USER_VARIATION_ALLOCATION_STATUS',
|
386
389
|
{
|
387
390
|
'{file}' => FILE,
|
388
|
-
'{status}' => variation ?
|
391
|
+
'{status}' => variation ? "got variation: + #{variation['name']}" : 'did not get any variation',
|
389
392
|
'{userId}' => user_id,
|
390
393
|
'{campaignKey}' => campaign['key']
|
391
394
|
},
|
@@ -418,12 +421,12 @@ class VWO
|
|
418
421
|
# @param[Boolean] :disable_logs optional: disable logs if True
|
419
422
|
# @return[Boolean] true if found otherwise false
|
420
423
|
|
421
|
-
def save_user_storage(user_id, campaign_key,
|
424
|
+
def save_user_storage(user_id, campaign_key, _campaign_type, variation_name, goal_identifier, disable_logs = false)
|
422
425
|
unless @user_storage_service
|
423
426
|
@logger.log(
|
424
427
|
LogLevelEnum::DEBUG,
|
425
428
|
'USER_STORAGE_SERVICE_NOT_CONFIGURED',
|
426
|
-
{'{file}' => FILE},
|
429
|
+
{ '{file}' => FILE },
|
427
430
|
disable_logs
|
428
431
|
)
|
429
432
|
return false
|
@@ -432,9 +435,7 @@ class VWO
|
|
432
435
|
new_campaign_user_mapping['campaign_key'] = campaign_key
|
433
436
|
new_campaign_user_mapping['user_id'] = user_id
|
434
437
|
new_campaign_user_mapping['variation_name'] = variation_name
|
435
|
-
|
436
|
-
new_campaign_user_mapping['goal_identifier'] = goal_identifier
|
437
|
-
end
|
438
|
+
new_campaign_user_mapping['goal_identifier'] = goal_identifier unless goal_identifier.empty?
|
438
439
|
|
439
440
|
@user_storage_service.set(new_campaign_user_mapping)
|
440
441
|
|
@@ -463,6 +464,12 @@ class VWO
|
|
463
464
|
false
|
464
465
|
end
|
465
466
|
|
467
|
+
def update_goal_identifier(user_id, campaign, variation, goal_identifier)
|
468
|
+
updated_goal_identifier = variation['goal_identifier']
|
469
|
+
updated_goal_identifier += VWO_DELIMITER + goal_identifier
|
470
|
+
save_user_storage(user_id, campaign['key'], campaign['name'], variation['name'], updated_goal_identifier) if variation['name']
|
471
|
+
end
|
472
|
+
|
466
473
|
private
|
467
474
|
|
468
475
|
# Evaluate all the variations in the campaign to find
|
@@ -476,20 +483,18 @@ class VWO
|
|
476
483
|
#
|
477
484
|
# @return[Hash]
|
478
485
|
|
479
|
-
def evaluate_whitelisting(user_id, campaign,
|
480
|
-
if campaign.key?('isUserListEnabled') && campaign[
|
486
|
+
def evaluate_whitelisting(user_id, campaign, _api_name, campaign_key, variation_targeting_variables = {}, disable_logs = false)
|
487
|
+
if campaign.key?('isUserListEnabled') && campaign['isUserListEnabled']
|
481
488
|
vwo_user_id = generator_for(user_id, @settings_file['accountId'], true)
|
482
489
|
if variation_targeting_variables.nil?
|
483
490
|
variation_targeting_variables = { _vwo_user_id: vwo_user_id }
|
484
491
|
else
|
485
492
|
variation_targeting_variables[:_vwo_user_id] = vwo_user_id
|
486
493
|
end
|
494
|
+
elsif variation_targeting_variables.nil?
|
495
|
+
variation_targeting_variables = { _vwo_user_id: user_id }
|
487
496
|
else
|
488
|
-
|
489
|
-
variation_targeting_variables = { _vwo_user_id: user_id }
|
490
|
-
else
|
491
|
-
variation_targeting_variables[:_vwo_user_id] = user_id
|
492
|
-
end
|
497
|
+
variation_targeting_variables[:_vwo_user_id] = user_id
|
493
498
|
end
|
494
499
|
targeted_variations = []
|
495
500
|
|
@@ -513,7 +518,11 @@ class VWO
|
|
513
518
|
'{customVariables}' => variation_targeting_variables,
|
514
519
|
'{status}' => status,
|
515
520
|
'{segmentationType}' => SegmentationTypeEnum::WHITELISTING,
|
516
|
-
'{variation}' => status == StatusEnum::PASSED
|
521
|
+
'{variation}' => if status == StatusEnum::PASSED
|
522
|
+
campaign['type'] == CampaignTypes::FEATURE_ROLLOUT ? 'and hence becomes part of the rollout' : "#{variation['name']} and hence becomes part of the rollout"
|
523
|
+
else
|
524
|
+
''
|
525
|
+
end
|
517
526
|
},
|
518
527
|
disable_logs
|
519
528
|
)
|
@@ -525,7 +534,7 @@ class VWO
|
|
525
534
|
'{file}' => FILE,
|
526
535
|
'{campaignKey}' => campaign_key,
|
527
536
|
'{userId}' => user_id,
|
528
|
-
'{variation}' => campaign['type'] == CampaignTypes::FEATURE_ROLLOUT ? '' :
|
537
|
+
'{variation}' => campaign['type'] == CampaignTypes::FEATURE_ROLLOUT ? '' : "for variation:#{variation['name']}"
|
529
538
|
},
|
530
539
|
disable_logs
|
531
540
|
)
|
@@ -582,7 +591,7 @@ class VWO
|
|
582
591
|
end
|
583
592
|
|
584
593
|
def scale_campaigns_weight(campaigns)
|
585
|
-
normalize_weight = 100/campaigns.length
|
594
|
+
normalize_weight = 100 / campaigns.length
|
586
595
|
campaigns.each do |campaign|
|
587
596
|
campaign['weight'] = normalize_weight
|
588
597
|
end
|
@@ -600,7 +609,7 @@ class VWO
|
|
600
609
|
@logger.log(
|
601
610
|
LogLevelEnum::DEBUG,
|
602
611
|
'USER_STORAGE_SERVICE_NOT_CONFIGURED',
|
603
|
-
{'{file}' => FILE},
|
612
|
+
{ '{file}' => FILE },
|
604
613
|
disable_logs
|
605
614
|
)
|
606
615
|
return false
|
@@ -643,7 +652,7 @@ class VWO
|
|
643
652
|
#
|
644
653
|
# @return[Object, nil] if found then variation settings object otherwise None
|
645
654
|
|
646
|
-
def get_stored_variation(
|
655
|
+
def get_stored_variation(_user_id, campaign_key, user_campaign_map, _disable_logs = false)
|
647
656
|
return unless user_campaign_map['campaign_key'] == campaign_key
|
648
657
|
|
649
658
|
variation_name = user_campaign_map['variation_name']
|
@@ -664,15 +673,13 @@ class VWO
|
|
664
673
|
# @param[Boolean] :disable_logs optional: disable logs if True
|
665
674
|
#
|
666
675
|
# @return[Boolean]
|
667
|
-
def
|
676
|
+
def presegmentation?(campaign, user_id, custom_variables, _api_name, disable_logs = false)
|
668
677
|
campaign_key = campaign['key']
|
669
678
|
segments = get_segments(campaign)
|
670
679
|
is_valid_segments = valid_value?(segments)
|
671
680
|
|
672
681
|
if is_valid_segments
|
673
|
-
|
674
|
-
custom_variables = {}
|
675
|
-
end
|
682
|
+
custom_variables ||= {}
|
676
683
|
response = @segment_evaluator.evaluate(campaign_key, user_id, segments, custom_variables, disable_logs)
|
677
684
|
@logger.log(
|
678
685
|
LogLevelEnum::INFO,
|
@@ -732,11 +739,11 @@ class VWO
|
|
732
739
|
eligible_campaigns = []
|
733
740
|
|
734
741
|
group_campaigns.each do |campaign|
|
735
|
-
if called_campaign[
|
742
|
+
if called_campaign['id'] == campaign['id'] || presegmentation?(campaign, user_id, custom_variables, '', true) && @bucketer.user_part_of_campaign?(user_id, campaign, true)
|
736
743
|
eligible_campaigns.push(campaign)
|
737
744
|
end
|
738
745
|
end
|
739
|
-
|
746
|
+
eligible_campaigns
|
740
747
|
end
|
741
748
|
|
742
749
|
# Finds and returns the winner campaign from eligible_campaigns list.
|
@@ -746,14 +753,12 @@ class VWO
|
|
746
753
|
#
|
747
754
|
# @return[Hash]
|
748
755
|
def get_winner_campaign(user_id, eligible_campaigns, group_id)
|
749
|
-
if eligible_campaigns.length == 1
|
750
|
-
return eligible_campaigns[0]
|
751
|
-
end
|
756
|
+
return eligible_campaigns[0] if eligible_campaigns.length == 1
|
752
757
|
|
753
758
|
eligible_campaigns = scale_campaigns_weight(eligible_campaigns)
|
754
759
|
eligible_campaigns = set_campaign_allocation(eligible_campaigns)
|
755
760
|
bucket_value = @bucketer.get_bucket_value_for_user(user_id, {}, group_id, true)
|
756
|
-
|
761
|
+
@bucketer.get_campaign_using_range(bucket_value, eligible_campaigns)
|
757
762
|
end
|
758
763
|
|
759
764
|
# Get campaign keys of all eligible Campaigns.
|
@@ -764,7 +769,7 @@ class VWO
|
|
764
769
|
def get_eligible_campaigns_key(eligible_campaigns)
|
765
770
|
eligible_campaigns_key = []
|
766
771
|
eligible_campaigns.each do |campaign|
|
767
|
-
eligible_campaigns_key.push(campaign[
|
772
|
+
eligible_campaigns_key.push(campaign['key'])
|
768
773
|
end
|
769
774
|
eligible_campaigns_key
|
770
775
|
end
|
@@ -778,9 +783,7 @@ class VWO
|
|
778
783
|
def get_non_eligible_campaigns_key(eligible_campaigns, group_campaigns)
|
779
784
|
non_eligible_campaigns_key = []
|
780
785
|
group_campaigns.each do |campaign|
|
781
|
-
unless eligible_campaigns.include? campaign
|
782
|
-
non_eligible_campaigns_key.push(campaign["key"])
|
783
|
-
end
|
786
|
+
non_eligible_campaigns_key.push(campaign['key']) unless eligible_campaigns.include? campaign
|
784
787
|
end
|
785
788
|
non_eligible_campaigns_key
|
786
789
|
end
|
@@ -795,54 +798,54 @@ class VWO
|
|
795
798
|
# @param[Boolean] :disable_logs optional: disable logs if True
|
796
799
|
# @return[Boolean]
|
797
800
|
|
798
|
-
def
|
801
|
+
def whitelisting_or_storage_for_grouped_campaigns?(user_id, called_campaign, group_campaigns, group_name, variation_targeting_variables, disable_logs = false)
|
799
802
|
group_campaigns.each do |campaign|
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
803
|
+
next unless called_campaign['id'] != campaign['id']
|
804
|
+
|
805
|
+
targeted_variation = evaluate_whitelisting(
|
806
|
+
user_id,
|
807
|
+
campaign,
|
808
|
+
'',
|
809
|
+
campaign['key'],
|
810
|
+
variation_targeting_variables,
|
811
|
+
true
|
812
|
+
)
|
813
|
+
next unless targeted_variation
|
814
|
+
|
815
|
+
@logger.log(
|
816
|
+
LogLevelEnum::INFO,
|
817
|
+
'OTHER_CAMPAIGN_SATISFIES_WHITELISTING_STORAGE',
|
818
|
+
{
|
819
|
+
'{file}' => FILE,
|
820
|
+
'{campaignKey}' => campaign['key'],
|
821
|
+
'{userId}' => user_id,
|
822
|
+
'{groupName}' => group_name,
|
823
|
+
'{type}' => 'whitelisting'
|
824
|
+
},
|
825
|
+
disable_logs
|
826
|
+
)
|
827
|
+
return true
|
825
828
|
end
|
826
829
|
|
827
830
|
group_campaigns.each do |campaign|
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
831
|
+
next unless called_campaign['id'] != campaign['id']
|
832
|
+
|
833
|
+
user_storage_data = get_user_storage(user_id, campaign['key'], true)
|
834
|
+
next unless user_storage_data
|
835
|
+
|
836
|
+
@logger.log(
|
837
|
+
LogLevelEnum::INFO,
|
838
|
+
'OTHER_CAMPAIGN_SATISFIES_WHITELISTING_STORAGE',
|
839
|
+
{
|
840
|
+
'{file}' => FILE,
|
841
|
+
'{campaignKey}' => campaign['key'],
|
842
|
+
'{userId}' => user_id,
|
843
|
+
'{groupName}' => group_name,
|
844
|
+
'{type}' => 'user storag'
|
845
|
+
},
|
846
|
+
disable_logs
|
847
|
+
)
|
848
|
+
return true
|
846
849
|
end
|
847
850
|
false
|
848
851
|
end
|
@@ -880,22 +883,16 @@ class VWO
|
|
880
883
|
'{file}' => FILE,
|
881
884
|
'{campaignKey}' => campaign_key,
|
882
885
|
'{userId}' => user_id,
|
883
|
-
'{customVariables}' => variation_targeting_variables
|
886
|
+
'{customVariables}' => variation_targeting_variables || {},
|
884
887
|
'{status}' => status,
|
885
888
|
'{segmentationType}' => SegmentationTypeEnum::WHITELISTING,
|
886
|
-
'{variation}' =>
|
889
|
+
'{variation}' => status == StatusEnum::PASSED && campaign['type'] != CampaignTypes::FEATURE_ROLLOUT ? "for variation:#{variation['name']}" : ' '
|
887
890
|
},
|
888
891
|
disable_logs
|
889
892
|
)
|
890
893
|
|
891
894
|
if variation
|
892
|
-
|
893
|
-
decision[:variation_name] = variation['name']
|
894
|
-
decision[:variation_id] = variation['id']
|
895
|
-
if campaign['type'] == CampaignTypes::FEATURE_TEST
|
896
|
-
decision[:is_feature_enabled] = variation['isFeatureEnabled']
|
897
|
-
end
|
898
|
-
end
|
895
|
+
decision = add_variation_to_decision_properties(decision, campaign, variation)
|
899
896
|
decision[:is_user_whitelisted] = true
|
900
897
|
@hooks_manager.execute(decision)
|
901
898
|
end
|
@@ -917,7 +914,6 @@ class VWO
|
|
917
914
|
end
|
918
915
|
nil
|
919
916
|
end
|
920
|
-
|
921
917
|
end
|
922
918
|
end
|
923
919
|
end
|