vwo-sdk 1.6.0 → 1.16.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 +29 -4
- data/lib/vwo/core/bucketer.rb +1 -1
- data/lib/vwo/core/variation_decider.rb +158 -40
- data/lib/vwo/enums.rb +30 -5
- data/lib/vwo/logger.rb +1 -1
- data/lib/vwo/schemas/settings_file.rb +32 -2
- 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 +1 -1
- data/lib/vwo/services/segment_evaluator.rb +1 -1
- 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 +3 -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 +575 -203
- metadata +35 -12
data/lib/vwo.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.
|
@@ -25,11 +25,14 @@ require_relative 'vwo/utils/feature'
|
|
25
25
|
require_relative 'vwo/utils/custom_dimensions'
|
26
26
|
require_relative 'vwo/constants'
|
27
27
|
require_relative 'vwo/core/variation_decider'
|
28
|
+
require_relative 'vwo/services/batch_events_dispatcher'
|
29
|
+
require_relative 'vwo/services/batch_events_queue'
|
30
|
+
require_relative 'vwo/services/usage_stats'
|
28
31
|
|
29
32
|
# VWO main file
|
30
33
|
class VWO
|
31
|
-
attr_accessor :is_instance_valid, :logger
|
32
|
-
|
34
|
+
attr_accessor :is_instance_valid, :logger, :settings_file_manager, :variation_decider
|
35
|
+
attr_reader :usage_stats
|
33
36
|
include Enums
|
34
37
|
include Utils::Validations
|
35
38
|
include Utils::Feature
|
@@ -65,7 +68,15 @@ class VWO
|
|
65
68
|
@is_development_mode = is_development_mode
|
66
69
|
@logger = VWO::Logger.get_instance(logger)
|
67
70
|
@logger.instance.level = options[:log_level] if (0..5).include?(options[:log_level])
|
71
|
+
usage_stats = {}
|
72
|
+
|
73
|
+
usage_stats[:cl] = 1 if logger
|
74
|
+
usage_stats[:ll] = 1 if options[:log_level]
|
75
|
+
usage_stats[:ss] = 1 if @user_storage
|
76
|
+
usage_stats[:ig] = 1 if options.key?(:integrations)
|
77
|
+
usage_stats[:eb] = 1 if options.key?(:batch_events)
|
68
78
|
|
79
|
+
@settings_file_manager = VWO::Services::SettingsFileManager.new(@account_id, @sdk_key)
|
69
80
|
unless valid_settings_file?(get_settings(settings_file))
|
70
81
|
@logger.log(
|
71
82
|
LogLevelEnum::ERROR,
|
@@ -74,6 +85,45 @@ class VWO
|
|
74
85
|
@is_instance_valid = false
|
75
86
|
return
|
76
87
|
end
|
88
|
+
|
89
|
+
if options.key?(:should_track_returning_user)
|
90
|
+
if [true, false].include? options[:should_track_returning_user]
|
91
|
+
@should_track_returning_user = options[:should_track_returning_user]
|
92
|
+
usage_stats[:tr] = 1 if @should_track_returning_user
|
93
|
+
else
|
94
|
+
@logger.log(
|
95
|
+
LogLevelEnum::ERROR,
|
96
|
+
format(
|
97
|
+
LogMessageEnum::ErrorMessages::INVALID_TRACK_RETURNING_USER_VALUE,
|
98
|
+
file: FILE
|
99
|
+
)
|
100
|
+
)
|
101
|
+
@is_instance_valid = false
|
102
|
+
return
|
103
|
+
end
|
104
|
+
else
|
105
|
+
@should_track_returning_user = false
|
106
|
+
end
|
107
|
+
|
108
|
+
if options.key?(:goal_type_to_track)
|
109
|
+
if GOAL_TYPES.key? options[:goal_type_to_track]
|
110
|
+
@goal_type_to_track = options[:goal_type_to_track]
|
111
|
+
usage_stats[:gt] = 1
|
112
|
+
else
|
113
|
+
@logger.log(
|
114
|
+
LogLevelEnum::ERROR,
|
115
|
+
format(
|
116
|
+
LogMessageEnum::ErrorMessages::INVALID_GOAL_TYPE,
|
117
|
+
file: FILE
|
118
|
+
)
|
119
|
+
)
|
120
|
+
@is_instance_valid = false
|
121
|
+
return
|
122
|
+
end
|
123
|
+
else
|
124
|
+
@goal_type_to_track = 'ALL'
|
125
|
+
end
|
126
|
+
|
77
127
|
@is_instance_valid = true
|
78
128
|
@config = VWO::Services::SettingsFileProcessor.new(get_settings)
|
79
129
|
|
@@ -89,8 +139,54 @@ class VWO
|
|
89
139
|
@config.process_settings_file
|
90
140
|
@settings_file = @config.get_settings_file
|
91
141
|
|
142
|
+
@usage_stats = VWO::Services::UsageStats.new(usage_stats, @is_development_mode)
|
143
|
+
|
144
|
+
if options.key?(:batch_events)
|
145
|
+
if options[:batch_events].is_a?(Hash)
|
146
|
+
unless is_valid_batch_event_settings(options[:batch_events])
|
147
|
+
@is_instance_valid = false
|
148
|
+
return
|
149
|
+
end
|
150
|
+
@batch_event_dispatcher = VWO::Services::BatchEventsDispatcher.new
|
151
|
+
def dispatcher (events, callback)
|
152
|
+
@batch_event_dispatcher.dispatch(
|
153
|
+
{
|
154
|
+
ev: events
|
155
|
+
},
|
156
|
+
callback,
|
157
|
+
{
|
158
|
+
a: @account_id,
|
159
|
+
sd: SDK_NAME,
|
160
|
+
sv: SDK_VERSION,
|
161
|
+
env: @sdk_key
|
162
|
+
}.merge(@usage_stats.usage_stats)
|
163
|
+
)
|
164
|
+
end
|
165
|
+
@batch_events_queue = VWO::Services::BatchEventsQueue.new(
|
166
|
+
options[:batch_events].merge(
|
167
|
+
{
|
168
|
+
account_id: @account_id,
|
169
|
+
dispatcher: method(:dispatcher)
|
170
|
+
}
|
171
|
+
)
|
172
|
+
)
|
173
|
+
@batch_events_queue.flush(manual: true)
|
174
|
+
@batch_events = options[:batch_events]
|
175
|
+
else
|
176
|
+
@logger.log(
|
177
|
+
LogLevelEnum::ERROR,
|
178
|
+
format(
|
179
|
+
LogMessageEnum::ErrorMessages::EVENT_BATCHING_NOT_OBJECT,
|
180
|
+
file: FILE
|
181
|
+
)
|
182
|
+
)
|
183
|
+
@is_instance_valid = false
|
184
|
+
return
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
92
188
|
# Assign VariationDecider to VWO
|
93
|
-
@variation_decider = VWO::Core::VariationDecider.new(@settings_file, user_storage)
|
189
|
+
@variation_decider = VWO::Core::VariationDecider.new(@settings_file, user_storage, options)
|
94
190
|
|
95
191
|
if is_development_mode
|
96
192
|
@logger.log(
|
@@ -118,9 +214,55 @@ class VWO
|
|
118
214
|
|
119
215
|
# VWO get_settings method to get settings for a particular account_id
|
120
216
|
def get_settings(settings_file = nil)
|
121
|
-
@
|
122
|
-
settings_file ||
|
123
|
-
@
|
217
|
+
@settings_file ||=
|
218
|
+
settings_file || @settings_file_manager.get_settings_file
|
219
|
+
@settings_file
|
220
|
+
end
|
221
|
+
|
222
|
+
# This API method: fetch the latest settings file and update it
|
223
|
+
|
224
|
+
# VWO get_settings method to get settings for a particular account_id
|
225
|
+
def get_and_update_settings_file
|
226
|
+
|
227
|
+
unless @is_instance_valid
|
228
|
+
@logger.log(
|
229
|
+
LogLevelEnum::ERROR,
|
230
|
+
format(
|
231
|
+
LogMessageEnum::ErrorMessages::API_CONFIG_CORRUPTED,
|
232
|
+
file: FILE,
|
233
|
+
api_name: ApiMethods.GET_AND_UPDATE_SETTINGS_FILE
|
234
|
+
)
|
235
|
+
)
|
236
|
+
return
|
237
|
+
end
|
238
|
+
|
239
|
+
latest_settings = @settings_file_manager.get_settings_file(true)
|
240
|
+
latest_settings = JSON.parse(latest_settings)
|
241
|
+
if latest_settings == @settings_file
|
242
|
+
@logger.log(
|
243
|
+
LogLevelEnum::INFO,
|
244
|
+
format(
|
245
|
+
LogMessageEnum::InfoMessages::SETTINGS_NOT_UPDATED,
|
246
|
+
api_name: ApiMethods::GET_AND_UPDATE_SETTINGS_FILE,
|
247
|
+
file: FILE
|
248
|
+
)
|
249
|
+
)
|
250
|
+
end
|
251
|
+
|
252
|
+
@config.update_settings_file(latest_settings)
|
253
|
+
@settings_file = @config.get_settings_file
|
254
|
+
@settings_file
|
255
|
+
rescue StandardError => e
|
256
|
+
@logger.log(
|
257
|
+
LogLevelEnum::ERROR,
|
258
|
+
format(
|
259
|
+
LogMessageEnum::ErrorMessages::API_NOT_WORKING,
|
260
|
+
file: FILE,
|
261
|
+
api_name: ApiMethods::GET_AND_UPDATE_SETTINGS_FILE,
|
262
|
+
exception: e
|
263
|
+
)
|
264
|
+
)
|
265
|
+
nil
|
124
266
|
end
|
125
267
|
|
126
268
|
# This API method: Gets the variation assigned for the user
|
@@ -136,18 +278,31 @@ class VWO
|
|
136
278
|
#
|
137
279
|
# @param[String] :campaign_key Unique campaign key
|
138
280
|
# @param[String] :user_id ID assigned to a user
|
139
|
-
# @param[Hash]
|
281
|
+
# @param[Hash] :options Options for custom variables required for segmentation
|
140
282
|
# @return[String|None] If variation is assigned then variation-name
|
141
283
|
# otherwise null in case of user not becoming part
|
142
284
|
|
143
285
|
def activate(campaign_key, user_id, options = {})
|
286
|
+
unless @is_instance_valid
|
287
|
+
@logger.log(
|
288
|
+
LogLevelEnum::ERROR,
|
289
|
+
format(
|
290
|
+
LogMessageEnum::ErrorMessages::API_CONFIG_CORRUPTED,
|
291
|
+
file: FILE,
|
292
|
+
api_name: ApiMethods::ACTIVATE
|
293
|
+
)
|
294
|
+
)
|
295
|
+
return
|
296
|
+
end
|
297
|
+
|
144
298
|
# Retrieve custom variables
|
145
299
|
custom_variables = options['custom_variables'] || options[:custom_variables]
|
146
300
|
variation_targeting_variables = options['variation_targeting_variables'] || options[:variation_targeting_variables]
|
147
301
|
|
302
|
+
should_track_returning_user = get_should_track_returning_user(options)
|
148
303
|
# Validate input parameters
|
149
304
|
unless valid_string?(campaign_key) && valid_string?(user_id) && (custom_variables.nil? || valid_hash?(custom_variables)) &&
|
150
|
-
|
305
|
+
(variation_targeting_variables.nil? || valid_hash?(variation_targeting_variables)) && [true, false].include?(should_track_returning_user)
|
151
306
|
@logger.log(
|
152
307
|
LogLevelEnum::ERROR,
|
153
308
|
format(
|
@@ -159,18 +314,6 @@ class VWO
|
|
159
314
|
return
|
160
315
|
end
|
161
316
|
|
162
|
-
unless @is_instance_valid
|
163
|
-
@logger.log(
|
164
|
-
LogLevelEnum::ERROR,
|
165
|
-
format(
|
166
|
-
LogMessageEnum::ErrorMessages::API_CONFIG_CORRUPTED,
|
167
|
-
file: FILE,
|
168
|
-
api_name: ApiMethods::ACTIVATE
|
169
|
-
)
|
170
|
-
)
|
171
|
-
return
|
172
|
-
end
|
173
|
-
|
174
317
|
# Get the campaign settings
|
175
318
|
campaign = get_campaign(@settings_file, campaign_key)
|
176
319
|
|
@@ -234,15 +377,56 @@ class VWO
|
|
234
377
|
return
|
235
378
|
end
|
236
379
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
380
|
+
if is_eligible_to_send_impression(should_track_returning_user)
|
381
|
+
if defined?(@batch_events)
|
382
|
+
impression = create_bulk_event_impression(
|
383
|
+
@settings_file,
|
384
|
+
campaign['id'],
|
385
|
+
variation['id'],
|
386
|
+
user_id
|
387
|
+
)
|
388
|
+
@batch_events_queue.enqueue(impression)
|
389
|
+
else
|
390
|
+
# Variation found, dispatch it to server
|
391
|
+
impression = create_impression(
|
392
|
+
@settings_file,
|
393
|
+
campaign['id'],
|
394
|
+
variation['id'],
|
395
|
+
user_id,
|
396
|
+
@sdk_key,
|
397
|
+
nil, # goal_id
|
398
|
+
nil, # revenue
|
399
|
+
usage_stats: @usage_stats.usage_stats
|
400
|
+
)
|
401
|
+
if @event_dispatcher.dispatch(impression)
|
402
|
+
@logger.log(
|
403
|
+
LogLevelEnum::INFO,
|
404
|
+
format(
|
405
|
+
LogMessageEnum::InfoMessages::IMPRESSION_SUCCESS,
|
406
|
+
file: FILE,
|
407
|
+
sdk_key: @sdk_key,
|
408
|
+
account_id: @account_id,
|
409
|
+
campaign_id: campaign['id'],
|
410
|
+
variation_id: variation['id'],
|
411
|
+
end_point: EVENTS::TRACK_USER
|
412
|
+
)
|
413
|
+
)
|
414
|
+
end
|
415
|
+
end
|
416
|
+
variation['name']
|
417
|
+
else
|
418
|
+
@logger.log(
|
419
|
+
LogLevelEnum::INFO,
|
420
|
+
format(
|
421
|
+
LogMessageEnum::InfoMessages::USER_ALREADY_TRACKED,
|
422
|
+
file: FILE,
|
423
|
+
user_id: user_id,
|
424
|
+
campaign_key: campaign_key,
|
425
|
+
api_name: ApiMethods::ACTIVATE
|
426
|
+
)
|
427
|
+
)
|
428
|
+
nil
|
429
|
+
end
|
246
430
|
rescue StandardError => e
|
247
431
|
@logger.log(
|
248
432
|
LogLevelEnum::ERROR,
|
@@ -253,7 +437,7 @@ class VWO
|
|
253
437
|
exception: e
|
254
438
|
)
|
255
439
|
)
|
256
|
-
|
440
|
+
e
|
257
441
|
end
|
258
442
|
|
259
443
|
# This API method: Gets the variation name assigned for the
|
@@ -274,13 +458,24 @@ class VWO
|
|
274
458
|
# Otherwise null in case of user not becoming part
|
275
459
|
#
|
276
460
|
def get_variation_name(campaign_key, user_id, options = {})
|
461
|
+
unless @is_instance_valid
|
462
|
+
@logger.log(
|
463
|
+
LogLevelEnum::ERROR,
|
464
|
+
format(
|
465
|
+
LogMessageEnum::ErrorMessages::API_CONFIG_CORRUPTED,
|
466
|
+
file: FILE,
|
467
|
+
api_name: ApiMethods::GET_VARIATION_NAME
|
468
|
+
)
|
469
|
+
)
|
470
|
+
return
|
471
|
+
end
|
277
472
|
# Retrieve custom variables
|
278
473
|
custom_variables = options['custom_variables'] || options[:custom_variables]
|
279
474
|
variation_targeting_variables = options['variation_targeting_variables'] || options[:variation_targeting_variables]
|
280
475
|
|
281
476
|
# Validate input parameters
|
282
477
|
unless valid_string?(campaign_key) && valid_string?(user_id) && (custom_variables.nil? || valid_hash?(custom_variables)) &&
|
283
|
-
|
478
|
+
(variation_targeting_variables.nil? || valid_hash?(variation_targeting_variables))
|
284
479
|
@logger.log(
|
285
480
|
LogLevelEnum::ERROR,
|
286
481
|
format(
|
@@ -291,17 +486,6 @@ class VWO
|
|
291
486
|
)
|
292
487
|
return
|
293
488
|
end
|
294
|
-
unless @is_instance_valid
|
295
|
-
@logger.log(
|
296
|
-
LogLevelEnum::ERROR,
|
297
|
-
format(
|
298
|
-
LogMessageEnum::ErrorMessages::API_CONFIG_CORRUPTED,
|
299
|
-
file: FILE,
|
300
|
-
api_name: ApiMethods::GET_VARIATION_NAME
|
301
|
-
)
|
302
|
-
)
|
303
|
-
return
|
304
|
-
end
|
305
489
|
|
306
490
|
# Get the campaign settings
|
307
491
|
campaign = get_campaign(@settings_file, campaign_key)
|
@@ -379,34 +563,11 @@ class VWO
|
|
379
563
|
# @param[String] :campaign_key Unique campaign key
|
380
564
|
# @param[String] :user_id ID assigned to a user
|
381
565
|
# @param[String] :goal_identifier Unique campaign's goal identifier
|
382
|
-
# @param[
|
566
|
+
# @param[Hash] :options Contains revenue value and custom variables
|
383
567
|
# @param[Numeric|String] :revenue_value It is the revenue generated on triggering the goal
|
384
568
|
#
|
385
|
-
def track(campaign_key, user_id, goal_identifier, *args)
|
386
|
-
if args[0].is_a?(Hash)
|
387
|
-
revenue_value = args[0]['revenue_value'] || args[0][:revenue_value]
|
388
|
-
custom_variables = args[0]['custom_variables'] || args[0][:custom_variables]
|
389
|
-
variation_targeting_variables = args[0]['variation_targeting_variables'] || args[0][:variation_targeting_variables]
|
390
|
-
elsif args.is_a?(Array)
|
391
|
-
revenue_value = args[0]
|
392
|
-
custom_variables = nil
|
393
|
-
end
|
394
|
-
|
395
|
-
# Check for valid args
|
396
|
-
unless valid_string?(campaign_key) && valid_string?(user_id) && (custom_variables.nil? || valid_hash?(custom_variables)) &&
|
397
|
-
(variation_targeting_variables.nil? || valid_hash?(variation_targeting_variables))
|
398
|
-
# log invalid params
|
399
|
-
@logger.log(
|
400
|
-
LogLevelEnum::ERROR,
|
401
|
-
format(
|
402
|
-
LogMessageEnum::ErrorMessages::TRACK_API_INVALID_PARAMS,
|
403
|
-
file: FILE,
|
404
|
-
api_name: ApiMethods.TRACK
|
405
|
-
)
|
406
|
-
)
|
407
|
-
return false
|
408
|
-
end
|
409
569
|
|
570
|
+
def track(campaign_key, user_id, goal_identifier, options = {})
|
410
571
|
unless @is_instance_valid
|
411
572
|
@logger.log(
|
412
573
|
LogLevelEnum::ERROR,
|
@@ -419,98 +580,187 @@ class VWO
|
|
419
580
|
return false
|
420
581
|
end
|
421
582
|
|
422
|
-
|
423
|
-
|
583
|
+
revenue_value = options['revenue_value'] || options[:revenue_value]
|
584
|
+
custom_variables = options['custom_variables'] || options[:custom_variables]
|
585
|
+
variation_targeting_variables = options['variation_targeting_variables'] || options[:variation_targeting_variables]
|
586
|
+
should_track_returning_user = get_should_track_returning_user(options)
|
587
|
+
goal_type_to_track = get_goal_type_to_track(options)
|
424
588
|
|
425
|
-
#
|
426
|
-
|
427
|
-
|
589
|
+
# Check for valid args
|
590
|
+
unless (valid_string?(campaign_key) || campaign_key.is_a?(Array) || campaign_key.nil?) && valid_string?(user_id) && valid_string?(goal_identifier) && (custom_variables.nil? || valid_hash?(custom_variables)) &&
|
591
|
+
(variation_targeting_variables.nil? || valid_hash?(variation_targeting_variables)) && [true, false].include?(should_track_returning_user) && (GOAL_TYPES.key? (goal_type_to_track))
|
592
|
+
# log invalid params
|
428
593
|
@logger.log(
|
429
594
|
LogLevelEnum::ERROR,
|
430
595
|
format(
|
431
|
-
LogMessageEnum::ErrorMessages::
|
596
|
+
LogMessageEnum::ErrorMessages::TRACK_API_INVALID_PARAMS,
|
432
597
|
file: FILE,
|
433
|
-
campaign_key: campaign_key,
|
434
598
|
api_name: ApiMethods::TRACK
|
435
599
|
)
|
436
600
|
)
|
437
601
|
return false
|
438
602
|
end
|
439
603
|
|
440
|
-
|
604
|
+
# Get campaigns settings
|
605
|
+
campaigns = get_campaigns(@settings_file, campaign_key, goal_identifier, goal_type_to_track)
|
441
606
|
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
format(
|
446
|
-
LogMessageEnum::ErrorMessages::INVALID_API,
|
447
|
-
file: FILE,
|
448
|
-
api_name: ApiMethods::TRACK,
|
449
|
-
user_id: user_id,
|
450
|
-
campaign_key: campaign_key,
|
451
|
-
campaign_type: campaign_type
|
452
|
-
)
|
453
|
-
)
|
454
|
-
return false
|
607
|
+
# Validate campaign
|
608
|
+
if campaigns.nil?
|
609
|
+
return nil
|
455
610
|
end
|
456
611
|
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
612
|
+
result = {}
|
613
|
+
campaigns.each do |campaign|
|
614
|
+
begin
|
615
|
+
campaign_type = campaign['type']
|
616
|
+
|
617
|
+
if campaign_type == CampaignTypes::FEATURE_ROLLOUT
|
618
|
+
@logger.log(
|
619
|
+
LogLevelEnum::ERROR,
|
620
|
+
format(
|
621
|
+
LogMessageEnum::ErrorMessages::INVALID_API,
|
622
|
+
file: FILE,
|
623
|
+
api_name: ApiMethods::TRACK,
|
624
|
+
user_id: user_id,
|
625
|
+
campaign_key: campaign['key'],
|
626
|
+
campaign_type: campaign_type
|
627
|
+
)
|
471
628
|
)
|
472
|
-
|
473
|
-
|
474
|
-
|
629
|
+
result[campaign['key']] = false
|
630
|
+
next
|
631
|
+
end
|
632
|
+
|
633
|
+
variation = @variation_decider.get_variation(user_id, campaign, ApiMethods::TRACK, campaign['key'], custom_variables, variation_targeting_variables, goal_identifier)
|
634
|
+
|
635
|
+
if variation
|
636
|
+
goal = get_campaign_goal(campaign, goal_identifier)
|
637
|
+
if goal.nil? || !goal["id"]
|
638
|
+
@logger.log(
|
639
|
+
LogLevelEnum::ERROR,
|
640
|
+
format(
|
641
|
+
LogMessageEnum::ErrorMessages::TRACK_API_GOAL_NOT_FOUND,
|
642
|
+
file: FILE,
|
643
|
+
goal_identifier: goal_identifier,
|
644
|
+
user_id: user_id,
|
645
|
+
campaign_key: campaign['key'],
|
646
|
+
api_name: ApiMethods::TRACK
|
647
|
+
)
|
648
|
+
)
|
649
|
+
result[campaign['key']] = false
|
650
|
+
next
|
651
|
+
elsif goal['type'] == GoalTypes::REVENUE && !valid_value?(revenue_value)
|
652
|
+
@logger.log(
|
653
|
+
LogLevelEnum::ERROR,
|
654
|
+
format(
|
655
|
+
LogMessageEnum::ErrorMessages::TRACK_API_REVENUE_NOT_PASSED_FOR_REVENUE_GOAL,
|
656
|
+
file: FILE,
|
657
|
+
user_id: user_id,
|
658
|
+
goal_identifier: goal_identifier,
|
659
|
+
campaign_key: campaign['key'],
|
660
|
+
api_name: ApiMethods::TRACK
|
661
|
+
)
|
662
|
+
)
|
663
|
+
result[campaign['key']] = false
|
664
|
+
next
|
665
|
+
elsif goal['type'] == GoalTypes::CUSTOM
|
666
|
+
revenue_value = nil
|
667
|
+
end
|
668
|
+
|
669
|
+
if variation['goal_identifier']
|
670
|
+
identifiers = variation['goal_identifier'].split(VWO_DELIMITER)
|
671
|
+
else
|
672
|
+
variation['goal_identifier'] = ''
|
673
|
+
identifiers = []
|
674
|
+
end
|
675
|
+
|
676
|
+
if !identifiers.include? goal_identifier
|
677
|
+
updated_goal_identifier = variation['goal_identifier']
|
678
|
+
updated_goal_identifier += VWO_DELIMITER + goal_identifier
|
679
|
+
@variation_decider.save_user_storage(user_id, campaign['key'], variation['name'], updated_goal_identifier) if variation['name']
|
680
|
+
# set variation at user storage
|
681
|
+
elsif !should_track_returning_user
|
682
|
+
@logger.log(
|
683
|
+
LogLevelEnum::INFO,
|
684
|
+
format(
|
685
|
+
LogMessageEnum::InfoMessages::GOAL_ALREADY_TRACKED,
|
686
|
+
file: FILE,
|
687
|
+
user_id: user_id,
|
688
|
+
campaign_key: campaign['key'],
|
689
|
+
goal_identifier: goal_identifier,
|
690
|
+
api_name: ApiMethods::TRACK
|
691
|
+
)
|
692
|
+
)
|
693
|
+
result[campaign['key']] = false
|
694
|
+
next
|
695
|
+
end
|
696
|
+
|
697
|
+
if defined?(@batch_events)
|
698
|
+
impression = create_bulk_event_impression(
|
699
|
+
@settings_file,
|
700
|
+
campaign['id'],
|
701
|
+
variation['id'],
|
702
|
+
user_id,
|
703
|
+
goal['id'],
|
704
|
+
revenue_value
|
705
|
+
)
|
706
|
+
@batch_events_queue.enqueue(impression)
|
707
|
+
else
|
708
|
+
impression = create_impression(
|
709
|
+
@settings_file,
|
710
|
+
campaign['id'],
|
711
|
+
variation['id'],
|
712
|
+
user_id,
|
713
|
+
@sdk_key,
|
714
|
+
goal['id'],
|
715
|
+
revenue_value
|
716
|
+
)
|
717
|
+
if @event_dispatcher.dispatch(impression)
|
718
|
+
@logger.log(
|
719
|
+
LogLevelEnum::INFO,
|
720
|
+
format(
|
721
|
+
LogMessageEnum::InfoMessages::IMPRESSION_SUCCESS,
|
722
|
+
file: FILE,
|
723
|
+
sdk_key: @sdk_key,
|
724
|
+
account_id: @account_id,
|
725
|
+
campaign_id: campaign['id'],
|
726
|
+
variation_id: variation['id'],
|
727
|
+
end_point: EVENTS::TRACK_GOAL
|
728
|
+
)
|
729
|
+
)
|
730
|
+
@logger.log(
|
731
|
+
LogLevelEnum::INFO,
|
732
|
+
format(
|
733
|
+
LogMessageEnum::InfoMessages::MAIN_KEYS_FOR_IMPRESSION,
|
734
|
+
file: FILE,
|
735
|
+
sdk_key: @sdk_key,
|
736
|
+
campaign_id: impression[:experiment_id],
|
737
|
+
account_id: impression[:account_id],
|
738
|
+
variation_id: impression[:combination]
|
739
|
+
)
|
740
|
+
)
|
741
|
+
end
|
742
|
+
end
|
743
|
+
result[campaign['key']] = true
|
744
|
+
next
|
745
|
+
end
|
746
|
+
result[campaign['key']] = false
|
747
|
+
rescue StandardError => e
|
475
748
|
@logger.log(
|
476
749
|
LogLevelEnum::ERROR,
|
477
750
|
format(
|
478
|
-
|
751
|
+
e.message,
|
479
752
|
file: FILE,
|
480
|
-
|
481
|
-
goal_identifier: goal_identifier,
|
482
|
-
campaign_key: campaign_key,
|
483
|
-
api_name: ApiMethods::TRACK
|
753
|
+
exception: e
|
484
754
|
)
|
485
755
|
)
|
486
|
-
return false
|
487
|
-
elsif goal['type'] == GoalTypes::CUSTOM
|
488
|
-
revenue_value = nil
|
489
756
|
end
|
490
|
-
|
491
|
-
@settings_file,
|
492
|
-
campaign['id'],
|
493
|
-
variation['id'],
|
494
|
-
user_id,
|
495
|
-
goal['id'],
|
496
|
-
revenue_value
|
497
|
-
)
|
498
|
-
@event_dispatcher.dispatch(impression)
|
757
|
+
end
|
499
758
|
|
500
|
-
|
501
|
-
|
502
|
-
format(
|
503
|
-
LogMessageEnum::InfoMessages::MAIN_KEYS_FOR_IMPRESSION,
|
504
|
-
file: FILE,
|
505
|
-
campaign_id: impression[:experiment_id],
|
506
|
-
user_id: impression[:uId],
|
507
|
-
account_id: impression[:account_id],
|
508
|
-
variation_id: impression[:combination]
|
509
|
-
)
|
510
|
-
)
|
511
|
-
return true
|
759
|
+
if result.length() == 0
|
760
|
+
return nil
|
512
761
|
end
|
513
|
-
|
762
|
+
|
763
|
+
result
|
514
764
|
rescue StandardError => e
|
515
765
|
@logger.log(
|
516
766
|
LogLevelEnum::ERROR,
|
@@ -538,13 +788,34 @@ class VWO
|
|
538
788
|
# @return[Boolean] true if user becomes part of feature test/rollout, otherwise false.
|
539
789
|
|
540
790
|
def feature_enabled?(campaign_key, user_id, options = {})
|
791
|
+
unless @is_instance_valid
|
792
|
+
@logger.log(
|
793
|
+
LogLevelEnum::ERROR,
|
794
|
+
format(
|
795
|
+
LogMessageEnum::ErrorMessages::API_CONFIG_CORRUPTED,
|
796
|
+
file: FILE,
|
797
|
+
api_name: ApiMethods::IS_FEATURE_ENABLED
|
798
|
+
)
|
799
|
+
)
|
800
|
+
return false
|
801
|
+
end
|
802
|
+
|
541
803
|
# Retrieve custom variables
|
542
804
|
custom_variables = options['custom_variables'] || options[:custom_variables]
|
543
805
|
variation_targeting_variables = options['variation_targeting_variables'] || options[:variation_targeting_variables]
|
544
|
-
|
806
|
+
should_track_returning_user = get_should_track_returning_user(options)
|
807
|
+
@logger.log(
|
808
|
+
LogLevelEnum::INFO,
|
809
|
+
format(
|
810
|
+
LogMessageEnum::InfoMessages::API_CALLED,
|
811
|
+
file: FILE,
|
812
|
+
api_name: ApiMethods::IS_FEATURE_ENABLED,
|
813
|
+
user_id: user_id
|
814
|
+
)
|
815
|
+
)
|
545
816
|
# Validate input parameters
|
546
817
|
unless valid_string?(campaign_key) && valid_string?(user_id) && (custom_variables.nil? || valid_hash?(custom_variables)) &&
|
547
|
-
|
818
|
+
(variation_targeting_variables.nil? || valid_hash?(variation_targeting_variables)) && [true, false].include?(should_track_returning_user)
|
548
819
|
@logger.log(
|
549
820
|
LogLevelEnum::ERROR,
|
550
821
|
format(
|
@@ -555,17 +826,6 @@ class VWO
|
|
555
826
|
)
|
556
827
|
return false
|
557
828
|
end
|
558
|
-
unless @is_instance_valid
|
559
|
-
@logger.log(
|
560
|
-
LogLevelEnum::ERROR,
|
561
|
-
format(
|
562
|
-
LogMessageEnum::ErrorMessages::API_CONFIG_CORRUPTED,
|
563
|
-
file: FILE,
|
564
|
-
api_name: ApiMethods::IS_FEATURE_ENABLED
|
565
|
-
)
|
566
|
-
)
|
567
|
-
return false
|
568
|
-
end
|
569
829
|
|
570
830
|
# Get the campaign settings
|
571
831
|
campaign = get_campaign(@settings_file, campaign_key)
|
@@ -611,50 +871,77 @@ class VWO
|
|
611
871
|
|
612
872
|
# if campaign type is feature_test Send track call to server
|
613
873
|
if campaign_type == CampaignTypes::FEATURE_TEST
|
614
|
-
|
615
|
-
@
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
874
|
+
if is_eligible_to_send_impression(should_track_returning_user)
|
875
|
+
if defined?(@batch_events)
|
876
|
+
impression = create_bulk_event_impression(
|
877
|
+
@settings_file,
|
878
|
+
campaign['id'],
|
879
|
+
variation['id'],
|
880
|
+
user_id
|
881
|
+
)
|
882
|
+
@batch_events_queue.enqueue(impression)
|
883
|
+
else
|
884
|
+
impression = create_impression(
|
885
|
+
@settings_file,
|
886
|
+
campaign['id'],
|
887
|
+
variation['id'],
|
888
|
+
user_id,
|
889
|
+
@sdk_key,
|
890
|
+
goal_id: nil,
|
891
|
+
revenue: nil,
|
892
|
+
usage_stats: @usage_stats.usage_stats
|
893
|
+
)
|
620
894
|
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
)
|
633
|
-
result = variation['isFeatureEnabled']
|
634
|
-
if result
|
635
|
-
@logger.log(
|
636
|
-
LogLevelEnum::INFO,
|
637
|
-
format(
|
638
|
-
LogMessageEnum::InfoMessages::FEATURE_ENABLED_FOR_USER,
|
639
|
-
file: FILE,
|
640
|
-
user_id: user_id,
|
641
|
-
feature_key: campaign_key,
|
642
|
-
api_name: ApiMethods::IS_FEATURE_ENABLED
|
895
|
+
@event_dispatcher.dispatch(impression)
|
896
|
+
@logger.log(
|
897
|
+
LogLevelEnum::INFO,
|
898
|
+
format(
|
899
|
+
LogMessageEnum::InfoMessages::MAIN_KEYS_FOR_IMPRESSION,
|
900
|
+
file: FILE,
|
901
|
+
campaign_id: impression[:experiment_id],
|
902
|
+
sdk_key: @sdk_key,
|
903
|
+
account_id: impression[:account_id],
|
904
|
+
variation_id: impression[:combination]
|
905
|
+
)
|
643
906
|
)
|
644
|
-
|
907
|
+
end
|
908
|
+
result = variation['isFeatureEnabled']
|
909
|
+
if result
|
910
|
+
@logger.log(
|
911
|
+
LogLevelEnum::INFO,
|
912
|
+
format(
|
913
|
+
LogMessageEnum::InfoMessages::FEATURE_ENABLED_FOR_USER,
|
914
|
+
file: FILE,
|
915
|
+
user_id: user_id,
|
916
|
+
feature_key: campaign_key,
|
917
|
+
api_name: ApiMethods::IS_FEATURE_ENABLED
|
918
|
+
)
|
919
|
+
)
|
920
|
+
else
|
921
|
+
@logger.log(
|
922
|
+
LogLevelEnum::INFO,
|
923
|
+
format(
|
924
|
+
LogMessageEnum::InfoMessages::FEATURE_NOT_ENABLED_FOR_USER,
|
925
|
+
file: FILE,
|
926
|
+
user_id: user_id,
|
927
|
+
feature_key: campaign_key,
|
928
|
+
api_name: ApiMethods::IS_FEATURE_ENABLED
|
929
|
+
)
|
930
|
+
)
|
931
|
+
end
|
932
|
+
return result
|
645
933
|
else
|
646
934
|
@logger.log(
|
647
935
|
LogLevelEnum::INFO,
|
648
936
|
format(
|
649
|
-
LogMessageEnum::InfoMessages::
|
937
|
+
LogMessageEnum::InfoMessages::USER_ALREADY_TRACKED,
|
650
938
|
file: FILE,
|
651
939
|
user_id: user_id,
|
652
|
-
|
940
|
+
campaign_key: campaign_key,
|
653
941
|
api_name: ApiMethods::IS_FEATURE_ENABLED
|
654
942
|
)
|
655
943
|
)
|
656
944
|
end
|
657
|
-
return result
|
658
945
|
end
|
659
946
|
true
|
660
947
|
rescue StandardError => e
|
@@ -690,30 +977,30 @@ class VWO
|
|
690
977
|
#
|
691
978
|
|
692
979
|
def get_feature_variable_value(campaign_key, variable_key, user_id, options = {})
|
693
|
-
|
694
|
-
custom_variables = options['custom_variables'] || options[:custom_variables]
|
695
|
-
variation_targeting_variables = options['variation_targeting_variables'] || options[:variation_targeting_variables]
|
696
|
-
|
697
|
-
unless valid_string?(campaign_key) && valid_string?(variable_key) && valid_string?(user_id) &&
|
698
|
-
(custom_variables.nil? || valid_hash?(custom_variables)) && (variation_targeting_variables.nil? || valid_hash?(variation_targeting_variables))
|
980
|
+
unless @is_instance_valid
|
699
981
|
@logger.log(
|
700
982
|
LogLevelEnum::ERROR,
|
701
983
|
format(
|
702
|
-
LogMessageEnum::ErrorMessages::
|
984
|
+
LogMessageEnum::ErrorMessages::API_CONFIG_CORRUPTED,
|
703
985
|
file: FILE,
|
704
|
-
api_name: ApiMethods
|
986
|
+
api_name: ApiMethods.GET_FEATURE_VARIABLE_VALUE
|
705
987
|
)
|
706
988
|
)
|
707
989
|
return
|
708
990
|
end
|
709
991
|
|
710
|
-
|
992
|
+
# Retrieve custom variables
|
993
|
+
custom_variables = options['custom_variables'] || options[:custom_variables]
|
994
|
+
variation_targeting_variables = options['variation_targeting_variables'] || options[:variation_targeting_variables]
|
995
|
+
|
996
|
+
unless valid_string?(campaign_key) && valid_string?(variable_key) && valid_string?(user_id) &&
|
997
|
+
(custom_variables.nil? || valid_hash?(custom_variables)) && (variation_targeting_variables.nil? || valid_hash?(variation_targeting_variables))
|
711
998
|
@logger.log(
|
712
999
|
LogLevelEnum::ERROR,
|
713
1000
|
format(
|
714
|
-
LogMessageEnum::ErrorMessages::
|
1001
|
+
LogMessageEnum::ErrorMessages::GET_FEATURE_VARIABLE_VALUE_API_INVALID_PARAMS,
|
715
1002
|
file: FILE,
|
716
|
-
api_name: ApiMethods
|
1003
|
+
api_name: ApiMethods::GET_FEATURE_VARIABLE_VALUE
|
717
1004
|
)
|
718
1005
|
)
|
719
1006
|
return
|
@@ -843,6 +1130,18 @@ class VWO
|
|
843
1130
|
# @return true if call is made successfully, else false
|
844
1131
|
|
845
1132
|
def push(tag_key, tag_value, user_id)
|
1133
|
+
unless @is_instance_valid
|
1134
|
+
@logger.log(
|
1135
|
+
LogLevelEnum::ERROR,
|
1136
|
+
format(
|
1137
|
+
LogMessageEnum::ErrorMessages::API_CONFIG_CORRUPTED,
|
1138
|
+
file: FILE,
|
1139
|
+
api_name: ApiMethods.PUSH
|
1140
|
+
)
|
1141
|
+
)
|
1142
|
+
return
|
1143
|
+
end
|
1144
|
+
|
846
1145
|
unless valid_string?(tag_key) && valid_string?(tag_value) && valid_string?(user_id)
|
847
1146
|
@logger.log(
|
848
1147
|
LogLevelEnum::ERROR,
|
@@ -883,32 +1182,105 @@ class VWO
|
|
883
1182
|
return false
|
884
1183
|
end
|
885
1184
|
|
886
|
-
|
887
|
-
|
888
|
-
|
1185
|
+
if defined?(@batch_events)
|
1186
|
+
impression = get_batch_event_url_params(@settings_file, tag_key, tag_value, user_id)
|
1187
|
+
@batch_events_queue.enqueue(impression)
|
1188
|
+
else
|
1189
|
+
impression = get_url_params(@settings_file, tag_key, tag_value, user_id, @sdk_key)
|
1190
|
+
@event_dispatcher.dispatch(impression)
|
889
1191
|
|
1192
|
+
@logger.log(
|
1193
|
+
LogLevelEnum::INFO,
|
1194
|
+
format(
|
1195
|
+
LogMessageEnum::InfoMessages::MAIN_KEYS_FOR_PUSH_API,
|
1196
|
+
file: FILE,
|
1197
|
+
sdk_key: @sdk_key,
|
1198
|
+
u: impression['u'],
|
1199
|
+
account_id: impression['account_id'],
|
1200
|
+
tags: impression['tags']
|
1201
|
+
)
|
1202
|
+
)
|
1203
|
+
end
|
1204
|
+
true
|
1205
|
+
rescue StandardError => e
|
890
1206
|
@logger.log(
|
891
|
-
LogLevelEnum::
|
1207
|
+
LogLevelEnum::ERROR,
|
892
1208
|
format(
|
893
|
-
LogMessageEnum::
|
1209
|
+
LogMessageEnum::ErrorMessages::API_NOT_WORKING,
|
894
1210
|
file: FILE,
|
895
|
-
|
896
|
-
|
897
|
-
account_id: impression['account_id'],
|
898
|
-
tags: impression['tags']
|
1211
|
+
api_name: ApiMethods::PUSH,
|
1212
|
+
exception: e
|
899
1213
|
)
|
900
1214
|
)
|
901
|
-
|
1215
|
+
false
|
1216
|
+
end
|
1217
|
+
|
1218
|
+
def get_should_track_returning_user(options)
|
1219
|
+
if !options.key?(:should_track_returning_user)
|
1220
|
+
options[:should_track_returning_user] = @should_track_returning_user
|
1221
|
+
elsif ![true, false].include?(options[:should_track_returning_user])
|
1222
|
+
@logger.log(
|
1223
|
+
LogLevelEnum::ERROR,
|
1224
|
+
format(
|
1225
|
+
LogMessageEnum::ErrorMessages::INVALID_TRACK_RETURNING_USER_VALUE,
|
1226
|
+
file: FILE
|
1227
|
+
)
|
1228
|
+
)
|
1229
|
+
end
|
1230
|
+
options[:should_track_returning_user]
|
1231
|
+
end
|
1232
|
+
|
1233
|
+
def is_eligible_to_send_impression(should_track_returning_user = false)
|
1234
|
+
!@user_storage || !@variation_decider.has_stored_variation || should_track_returning_user
|
1235
|
+
end
|
1236
|
+
|
1237
|
+
def flush_events
|
1238
|
+
unless @is_instance_valid
|
1239
|
+
@logger.log(
|
1240
|
+
LogLevelEnum::ERROR,
|
1241
|
+
format(
|
1242
|
+
LogMessageEnum::ErrorMessages::API_CONFIG_CORRUPTED,
|
1243
|
+
file: FILE,
|
1244
|
+
api_name: ApiMethods::FLUSH_EVENTS
|
1245
|
+
)
|
1246
|
+
)
|
1247
|
+
return
|
1248
|
+
end
|
1249
|
+
result = @batch_events_queue.flush(manual: true)
|
1250
|
+
@batch_events_queue.kill_thread
|
1251
|
+
result
|
902
1252
|
rescue StandardError => e
|
903
1253
|
@logger.log(
|
904
1254
|
LogLevelEnum::ERROR,
|
905
1255
|
format(
|
906
1256
|
LogMessageEnum::ErrorMessages::API_NOT_WORKING,
|
907
1257
|
file: FILE,
|
908
|
-
api_name: ApiMethods::
|
1258
|
+
api_name: ApiMethods::FLUSH_EVENTS,
|
909
1259
|
exception: e
|
910
1260
|
)
|
911
1261
|
)
|
912
1262
|
false
|
913
1263
|
end
|
1264
|
+
|
1265
|
+
def get_goal_type_to_track(options)
|
1266
|
+
goal_type_to_track = nil
|
1267
|
+
if !options.key?(:goal_type_to_track)
|
1268
|
+
if @goal_type_to_track
|
1269
|
+
goal_type_to_track = @goal_type_to_track
|
1270
|
+
else
|
1271
|
+
goal_type_to_track = GOAL_TYPES['ALL']
|
1272
|
+
end
|
1273
|
+
elsif GOAL_TYPES.key? options[:goal_type_to_track]
|
1274
|
+
goal_type_to_track = options[:goal_type_to_track]
|
1275
|
+
else
|
1276
|
+
@logger.log(
|
1277
|
+
LogLevelEnum::ERROR,
|
1278
|
+
format(
|
1279
|
+
LogMessageEnum::ErrorMessages::INVALID_GOAL_TYPE,
|
1280
|
+
file: FILE
|
1281
|
+
)
|
1282
|
+
)
|
1283
|
+
end
|
1284
|
+
goal_type_to_track
|
1285
|
+
end
|
914
1286
|
end
|