vwo-sdk 1.6.0 → 1.14.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.rb +571 -195
- data/lib/vwo/constants.rb +26 -3
- data/lib/vwo/core/bucketer.rb +1 -1
- data/lib/vwo/core/variation_decider.rb +155 -40
- data/lib/vwo/enums.rb +30 -5
- 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 +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 +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
- metadata +9 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53bc0be49f50d280aac4478d6879a31dce269ed69596e5ff2d04b51cbcacbcbd
|
4
|
+
data.tar.gz: f28c84dc2ace873bf1c5fd748568d06c16583b466240df54fcf9dbd5dffec888
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 63bf38a8c25c69e01cf7cbfdad204379c0dd03621e947c1848a1fdf24b432bd26672a8d3cb9efded0e45582208a73a0c69142911c900e53e477e06b2d06a069e
|
7
|
+
data.tar.gz: 0b9d9165ca2ae918a048537097f2de775fe13d35547c341aa45c7edc42206273775f18120c10ca06fc02821c24652bd25e513a05989889664285210b6fd57f02
|
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,
|
@@ -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)
|
@@ -383,30 +567,6 @@ class VWO
|
|
383
567
|
# @param[Numeric|String] :revenue_value It is the revenue generated on triggering the goal
|
384
568
|
#
|
385
569
|
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
|
-
|
410
570
|
unless @is_instance_valid
|
411
571
|
@logger.log(
|
412
572
|
LogLevelEnum::ERROR,
|
@@ -419,98 +579,192 @@ class VWO
|
|
419
579
|
return false
|
420
580
|
end
|
421
581
|
|
422
|
-
|
423
|
-
|
582
|
+
if args[0].is_a?(Hash)
|
583
|
+
revenue_value = args[0]['revenue_value'] || args[0][:revenue_value]
|
584
|
+
custom_variables = args[0]['custom_variables'] || args[0][:custom_variables]
|
585
|
+
variation_targeting_variables = args[0]['variation_targeting_variables'] || args[0][:variation_targeting_variables]
|
586
|
+
should_track_returning_user = get_should_track_returning_user(args[0])
|
587
|
+
goal_type_to_track = get_goal_type_to_track(args[0])
|
588
|
+
elsif args.is_a?(Array)
|
589
|
+
revenue_value = args[0]
|
590
|
+
custom_variables = nil
|
591
|
+
should_track_returning_user = @should_track_returning_user
|
592
|
+
goal_type_to_track = @goal_type_to_track
|
593
|
+
end
|
424
594
|
|
425
|
-
#
|
426
|
-
|
427
|
-
|
595
|
+
# Check for valid args
|
596
|
+
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)) &&
|
597
|
+
(variation_targeting_variables.nil? || valid_hash?(variation_targeting_variables)) && [true, false].include?(should_track_returning_user) && (GOAL_TYPES.key? (goal_type_to_track))
|
598
|
+
# log invalid params
|
428
599
|
@logger.log(
|
429
600
|
LogLevelEnum::ERROR,
|
430
601
|
format(
|
431
|
-
LogMessageEnum::ErrorMessages::
|
602
|
+
LogMessageEnum::ErrorMessages::TRACK_API_INVALID_PARAMS,
|
432
603
|
file: FILE,
|
433
|
-
campaign_key: campaign_key,
|
434
604
|
api_name: ApiMethods::TRACK
|
435
605
|
)
|
436
606
|
)
|
437
607
|
return false
|
438
608
|
end
|
439
609
|
|
440
|
-
|
610
|
+
# Get campaigns settings
|
611
|
+
campaigns = get_campaigns(@settings_file, campaign_key, goal_identifier, goal_type_to_track)
|
441
612
|
|
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
|
613
|
+
# Validate campaign
|
614
|
+
if campaigns.nil?
|
615
|
+
return nil
|
455
616
|
end
|
456
617
|
|
457
|
-
|
618
|
+
result = {}
|
619
|
+
campaigns.each do |campaign|
|
620
|
+
campaign_type = campaign['type']
|
458
621
|
|
459
|
-
|
460
|
-
goal = get_campaign_goal(campaign, goal_identifier)
|
461
|
-
if goal.nil?
|
622
|
+
if campaign_type == CampaignTypes::FEATURE_ROLLOUT
|
462
623
|
@logger.log(
|
463
624
|
LogLevelEnum::ERROR,
|
464
625
|
format(
|
465
|
-
LogMessageEnum::ErrorMessages::
|
626
|
+
LogMessageEnum::ErrorMessages::INVALID_API,
|
466
627
|
file: FILE,
|
467
|
-
|
628
|
+
api_name: ApiMethods::TRACK,
|
468
629
|
user_id: user_id,
|
469
|
-
campaign_key:
|
470
|
-
|
630
|
+
campaign_key: campaign['key'],
|
631
|
+
campaign_type: campaign_type
|
471
632
|
)
|
472
633
|
)
|
473
|
-
|
474
|
-
|
475
|
-
@logger.log(
|
476
|
-
LogLevelEnum::ERROR,
|
477
|
-
format(
|
478
|
-
LogMessageEnum::ErrorMessages::TRACK_API_REVENUE_NOT_PASSED_FOR_REVENUE_GOAL,
|
479
|
-
file: FILE,
|
480
|
-
user_id: user_id,
|
481
|
-
goal_identifier: goal_identifier,
|
482
|
-
campaign_key: campaign_key,
|
483
|
-
api_name: ApiMethods::TRACK
|
484
|
-
)
|
485
|
-
)
|
486
|
-
return false
|
487
|
-
elsif goal['type'] == GoalTypes::CUSTOM
|
488
|
-
revenue_value = nil
|
634
|
+
result[campaign['key']] = false
|
635
|
+
next
|
489
636
|
end
|
490
|
-
impression = create_impression(
|
491
|
-
@settings_file,
|
492
|
-
campaign['id'],
|
493
|
-
variation['id'],
|
494
|
-
user_id,
|
495
|
-
goal['id'],
|
496
|
-
revenue_value
|
497
|
-
)
|
498
|
-
@event_dispatcher.dispatch(impression)
|
499
637
|
|
638
|
+
variation = @variation_decider.get_variation(user_id, campaign, ApiMethods::TRACK, campaign['key'], custom_variables, variation_targeting_variables, goal_identifier)
|
639
|
+
|
640
|
+
if variation
|
641
|
+
goal = get_campaign_goal(campaign, goal_identifier)
|
642
|
+
if goal.nil? || !goal["id"]
|
643
|
+
@logger.log(
|
644
|
+
LogLevelEnum::ERROR,
|
645
|
+
format(
|
646
|
+
LogMessageEnum::ErrorMessages::TRACK_API_GOAL_NOT_FOUND,
|
647
|
+
file: FILE,
|
648
|
+
goal_identifier: goal_identifier,
|
649
|
+
user_id: user_id,
|
650
|
+
campaign_key: campaign['key'],
|
651
|
+
api_name: ApiMethods::TRACK
|
652
|
+
)
|
653
|
+
)
|
654
|
+
result[campaign['key']] = false
|
655
|
+
next
|
656
|
+
elsif goal['type'] == GoalTypes::REVENUE && !valid_value?(revenue_value)
|
657
|
+
@logger.log(
|
658
|
+
LogLevelEnum::ERROR,
|
659
|
+
format(
|
660
|
+
LogMessageEnum::ErrorMessages::TRACK_API_REVENUE_NOT_PASSED_FOR_REVENUE_GOAL,
|
661
|
+
file: FILE,
|
662
|
+
user_id: user_id,
|
663
|
+
goal_identifier: goal_identifier,
|
664
|
+
campaign_key: campaign['key'],
|
665
|
+
api_name: ApiMethods::TRACK
|
666
|
+
)
|
667
|
+
)
|
668
|
+
result[campaign['key']] = false
|
669
|
+
next
|
670
|
+
elsif goal['type'] == GoalTypes::CUSTOM
|
671
|
+
revenue_value = nil
|
672
|
+
end
|
673
|
+
|
674
|
+
if variation['goal_identifier']
|
675
|
+
identifiers = variation['goal_identifier'].split(VWO_DELIMITER)
|
676
|
+
else
|
677
|
+
variation['goal_identifier'] = ''
|
678
|
+
identifiers = []
|
679
|
+
end
|
680
|
+
|
681
|
+
if !identifiers.include? goal_identifier
|
682
|
+
updated_goal_identifier = variation['goal_identifier']
|
683
|
+
updated_goal_identifier += VWO_DELIMITER + goal_identifier
|
684
|
+
@variation_decider.save_user_storage(user_id, campaign['key'], variation['name'], updated_goal_identifier) if variation['name']
|
685
|
+
# set variation at user storage
|
686
|
+
elsif !should_track_returning_user
|
687
|
+
@logger.log(
|
688
|
+
LogLevelEnum::INFO,
|
689
|
+
format(
|
690
|
+
LogMessageEnum::InfoMessages::GOAL_ALREADY_TRACKED,
|
691
|
+
file: FILE,
|
692
|
+
user_id: user_id,
|
693
|
+
campaign_key: campaign['key'],
|
694
|
+
goal_identifier: goal_identifier,
|
695
|
+
api_name: ApiMethods::TRACK
|
696
|
+
)
|
697
|
+
)
|
698
|
+
result[campaign['key']] = false
|
699
|
+
next
|
700
|
+
end
|
701
|
+
|
702
|
+
if defined?(@batch_events)
|
703
|
+
impression = create_bulk_event_impression(
|
704
|
+
@settings_file,
|
705
|
+
campaign['id'],
|
706
|
+
variation['id'],
|
707
|
+
user_id,
|
708
|
+
goal['id'],
|
709
|
+
revenue_value
|
710
|
+
)
|
711
|
+
@batch_events_queue.enqueue(impression)
|
712
|
+
else
|
713
|
+
impression = create_impression(
|
714
|
+
@settings_file,
|
715
|
+
campaign['id'],
|
716
|
+
variation['id'],
|
717
|
+
user_id,
|
718
|
+
@sdk_key,
|
719
|
+
goal['id'],
|
720
|
+
revenue_value
|
721
|
+
)
|
722
|
+
if @event_dispatcher.dispatch(impression)
|
723
|
+
@logger.log(
|
724
|
+
LogLevelEnum::INFO,
|
725
|
+
format(
|
726
|
+
LogMessageEnum::InfoMessages::IMPRESSION_SUCCESS,
|
727
|
+
file: FILE,
|
728
|
+
sdk_key: @sdk_key,
|
729
|
+
account_id: @account_id,
|
730
|
+
campaign_id: campaign['id'],
|
731
|
+
variation_id: variation['id'],
|
732
|
+
end_point: EVENTS::TRACK_GOAL
|
733
|
+
)
|
734
|
+
)
|
735
|
+
@logger.log(
|
736
|
+
LogLevelEnum::INFO,
|
737
|
+
format(
|
738
|
+
LogMessageEnum::InfoMessages::MAIN_KEYS_FOR_IMPRESSION,
|
739
|
+
file: FILE,
|
740
|
+
sdk_key: @sdk_key,
|
741
|
+
campaign_id: impression[:experiment_id],
|
742
|
+
account_id: impression[:account_id],
|
743
|
+
variation_id: impression[:combination]
|
744
|
+
)
|
745
|
+
)
|
746
|
+
end
|
747
|
+
end
|
748
|
+
result[campaign['key']] = true
|
749
|
+
next
|
750
|
+
end
|
751
|
+
result[campaign['key']] = false
|
752
|
+
rescue StandardError => e
|
500
753
|
@logger.log(
|
501
|
-
LogLevelEnum::
|
754
|
+
LogLevelEnum::ERROR,
|
502
755
|
format(
|
503
|
-
|
756
|
+
e.message,
|
504
757
|
file: FILE,
|
505
|
-
|
506
|
-
user_id: impression[:uId],
|
507
|
-
account_id: impression[:account_id],
|
508
|
-
variation_id: impression[:combination]
|
758
|
+
exception: e
|
509
759
|
)
|
510
760
|
)
|
511
|
-
return true
|
512
761
|
end
|
513
|
-
|
762
|
+
|
763
|
+
if result.length() == 0
|
764
|
+
return nil
|
765
|
+
end
|
766
|
+
|
767
|
+
result
|
514
768
|
rescue StandardError => e
|
515
769
|
@logger.log(
|
516
770
|
LogLevelEnum::ERROR,
|
@@ -538,13 +792,34 @@ class VWO
|
|
538
792
|
# @return[Boolean] true if user becomes part of feature test/rollout, otherwise false.
|
539
793
|
|
540
794
|
def feature_enabled?(campaign_key, user_id, options = {})
|
795
|
+
unless @is_instance_valid
|
796
|
+
@logger.log(
|
797
|
+
LogLevelEnum::ERROR,
|
798
|
+
format(
|
799
|
+
LogMessageEnum::ErrorMessages::API_CONFIG_CORRUPTED,
|
800
|
+
file: FILE,
|
801
|
+
api_name: ApiMethods::IS_FEATURE_ENABLED
|
802
|
+
)
|
803
|
+
)
|
804
|
+
return false
|
805
|
+
end
|
806
|
+
|
541
807
|
# Retrieve custom variables
|
542
808
|
custom_variables = options['custom_variables'] || options[:custom_variables]
|
543
809
|
variation_targeting_variables = options['variation_targeting_variables'] || options[:variation_targeting_variables]
|
544
|
-
|
810
|
+
should_track_returning_user = get_should_track_returning_user(options)
|
811
|
+
@logger.log(
|
812
|
+
LogLevelEnum::INFO,
|
813
|
+
format(
|
814
|
+
LogMessageEnum::InfoMessages::API_CALLED,
|
815
|
+
file: FILE,
|
816
|
+
api_name: ApiMethods::IS_FEATURE_ENABLED,
|
817
|
+
user_id: user_id
|
818
|
+
)
|
819
|
+
)
|
545
820
|
# Validate input parameters
|
546
821
|
unless valid_string?(campaign_key) && valid_string?(user_id) && (custom_variables.nil? || valid_hash?(custom_variables)) &&
|
547
|
-
|
822
|
+
(variation_targeting_variables.nil? || valid_hash?(variation_targeting_variables)) && [true, false].include?(should_track_returning_user)
|
548
823
|
@logger.log(
|
549
824
|
LogLevelEnum::ERROR,
|
550
825
|
format(
|
@@ -555,17 +830,6 @@ class VWO
|
|
555
830
|
)
|
556
831
|
return false
|
557
832
|
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
833
|
|
570
834
|
# Get the campaign settings
|
571
835
|
campaign = get_campaign(@settings_file, campaign_key)
|
@@ -611,50 +875,77 @@ class VWO
|
|
611
875
|
|
612
876
|
# if campaign type is feature_test Send track call to server
|
613
877
|
if campaign_type == CampaignTypes::FEATURE_TEST
|
614
|
-
|
615
|
-
@
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
878
|
+
if is_eligible_to_send_impression(should_track_returning_user)
|
879
|
+
if defined?(@batch_events)
|
880
|
+
impression = create_bulk_event_impression(
|
881
|
+
@settings_file,
|
882
|
+
campaign['id'],
|
883
|
+
variation['id'],
|
884
|
+
user_id
|
885
|
+
)
|
886
|
+
@batch_events_queue.enqueue(impression)
|
887
|
+
else
|
888
|
+
impression = create_impression(
|
889
|
+
@settings_file,
|
890
|
+
campaign['id'],
|
891
|
+
variation['id'],
|
892
|
+
user_id,
|
893
|
+
@sdk_key,
|
894
|
+
goal_id: nil,
|
895
|
+
revenue: nil,
|
896
|
+
usage_stats: @usage_stats.usage_stats
|
897
|
+
)
|
620
898
|
|
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
|
899
|
+
@event_dispatcher.dispatch(impression)
|
900
|
+
@logger.log(
|
901
|
+
LogLevelEnum::INFO,
|
902
|
+
format(
|
903
|
+
LogMessageEnum::InfoMessages::MAIN_KEYS_FOR_IMPRESSION,
|
904
|
+
file: FILE,
|
905
|
+
campaign_id: impression[:experiment_id],
|
906
|
+
sdk_key: @sdk_key,
|
907
|
+
account_id: impression[:account_id],
|
908
|
+
variation_id: impression[:combination]
|
909
|
+
)
|
643
910
|
)
|
644
|
-
|
911
|
+
end
|
912
|
+
result = variation['isFeatureEnabled']
|
913
|
+
if result
|
914
|
+
@logger.log(
|
915
|
+
LogLevelEnum::INFO,
|
916
|
+
format(
|
917
|
+
LogMessageEnum::InfoMessages::FEATURE_ENABLED_FOR_USER,
|
918
|
+
file: FILE,
|
919
|
+
user_id: user_id,
|
920
|
+
feature_key: campaign_key,
|
921
|
+
api_name: ApiMethods::IS_FEATURE_ENABLED
|
922
|
+
)
|
923
|
+
)
|
924
|
+
else
|
925
|
+
@logger.log(
|
926
|
+
LogLevelEnum::INFO,
|
927
|
+
format(
|
928
|
+
LogMessageEnum::InfoMessages::FEATURE_NOT_ENABLED_FOR_USER,
|
929
|
+
file: FILE,
|
930
|
+
user_id: user_id,
|
931
|
+
feature_key: campaign_key,
|
932
|
+
api_name: ApiMethods::IS_FEATURE_ENABLED
|
933
|
+
)
|
934
|
+
)
|
935
|
+
end
|
936
|
+
return result
|
645
937
|
else
|
646
938
|
@logger.log(
|
647
939
|
LogLevelEnum::INFO,
|
648
940
|
format(
|
649
|
-
LogMessageEnum::InfoMessages::
|
941
|
+
LogMessageEnum::InfoMessages::USER_ALREADY_TRACKED,
|
650
942
|
file: FILE,
|
651
943
|
user_id: user_id,
|
652
|
-
|
944
|
+
campaign_key: campaign_key,
|
653
945
|
api_name: ApiMethods::IS_FEATURE_ENABLED
|
654
946
|
)
|
655
947
|
)
|
656
948
|
end
|
657
|
-
return result
|
658
949
|
end
|
659
950
|
true
|
660
951
|
rescue StandardError => e
|
@@ -690,30 +981,30 @@ class VWO
|
|
690
981
|
#
|
691
982
|
|
692
983
|
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))
|
984
|
+
unless @is_instance_valid
|
699
985
|
@logger.log(
|
700
986
|
LogLevelEnum::ERROR,
|
701
987
|
format(
|
702
|
-
LogMessageEnum::ErrorMessages::
|
988
|
+
LogMessageEnum::ErrorMessages::API_CONFIG_CORRUPTED,
|
703
989
|
file: FILE,
|
704
|
-
api_name: ApiMethods
|
990
|
+
api_name: ApiMethods.GET_FEATURE_VARIABLE_VALUE
|
705
991
|
)
|
706
992
|
)
|
707
993
|
return
|
708
994
|
end
|
709
995
|
|
710
|
-
|
996
|
+
# Retrieve custom variables
|
997
|
+
custom_variables = options['custom_variables'] || options[:custom_variables]
|
998
|
+
variation_targeting_variables = options['variation_targeting_variables'] || options[:variation_targeting_variables]
|
999
|
+
|
1000
|
+
unless valid_string?(campaign_key) && valid_string?(variable_key) && valid_string?(user_id) &&
|
1001
|
+
(custom_variables.nil? || valid_hash?(custom_variables)) && (variation_targeting_variables.nil? || valid_hash?(variation_targeting_variables))
|
711
1002
|
@logger.log(
|
712
1003
|
LogLevelEnum::ERROR,
|
713
1004
|
format(
|
714
|
-
LogMessageEnum::ErrorMessages::
|
1005
|
+
LogMessageEnum::ErrorMessages::GET_FEATURE_VARIABLE_VALUE_API_INVALID_PARAMS,
|
715
1006
|
file: FILE,
|
716
|
-
api_name: ApiMethods
|
1007
|
+
api_name: ApiMethods::GET_FEATURE_VARIABLE_VALUE
|
717
1008
|
)
|
718
1009
|
)
|
719
1010
|
return
|
@@ -843,6 +1134,18 @@ class VWO
|
|
843
1134
|
# @return true if call is made successfully, else false
|
844
1135
|
|
845
1136
|
def push(tag_key, tag_value, user_id)
|
1137
|
+
unless @is_instance_valid
|
1138
|
+
@logger.log(
|
1139
|
+
LogLevelEnum::ERROR,
|
1140
|
+
format(
|
1141
|
+
LogMessageEnum::ErrorMessages::API_CONFIG_CORRUPTED,
|
1142
|
+
file: FILE,
|
1143
|
+
api_name: ApiMethods.PUSH
|
1144
|
+
)
|
1145
|
+
)
|
1146
|
+
return
|
1147
|
+
end
|
1148
|
+
|
846
1149
|
unless valid_string?(tag_key) && valid_string?(tag_value) && valid_string?(user_id)
|
847
1150
|
@logger.log(
|
848
1151
|
LogLevelEnum::ERROR,
|
@@ -883,32 +1186,105 @@ class VWO
|
|
883
1186
|
return false
|
884
1187
|
end
|
885
1188
|
|
886
|
-
|
887
|
-
|
888
|
-
|
1189
|
+
if defined?(@batch_events)
|
1190
|
+
impression = get_batch_event_url_params(@settings_file, tag_key, tag_value, user_id)
|
1191
|
+
@batch_events_queue.enqueue(impression)
|
1192
|
+
else
|
1193
|
+
impression = get_url_params(@settings_file, tag_key, tag_value, user_id, @sdk_key)
|
1194
|
+
@event_dispatcher.dispatch(impression)
|
889
1195
|
|
1196
|
+
@logger.log(
|
1197
|
+
LogLevelEnum::INFO,
|
1198
|
+
format(
|
1199
|
+
LogMessageEnum::InfoMessages::MAIN_KEYS_FOR_PUSH_API,
|
1200
|
+
file: FILE,
|
1201
|
+
sdk_key: @sdk_key,
|
1202
|
+
u: impression['u'],
|
1203
|
+
account_id: impression['account_id'],
|
1204
|
+
tags: impression['tags']
|
1205
|
+
)
|
1206
|
+
)
|
1207
|
+
end
|
1208
|
+
true
|
1209
|
+
rescue StandardError => e
|
890
1210
|
@logger.log(
|
891
|
-
LogLevelEnum::
|
1211
|
+
LogLevelEnum::ERROR,
|
892
1212
|
format(
|
893
|
-
LogMessageEnum::
|
1213
|
+
LogMessageEnum::ErrorMessages::API_NOT_WORKING,
|
894
1214
|
file: FILE,
|
895
|
-
|
896
|
-
|
897
|
-
account_id: impression['account_id'],
|
898
|
-
tags: impression['tags']
|
1215
|
+
api_name: ApiMethods::PUSH,
|
1216
|
+
exception: e
|
899
1217
|
)
|
900
1218
|
)
|
901
|
-
|
1219
|
+
false
|
1220
|
+
end
|
1221
|
+
|
1222
|
+
def get_should_track_returning_user(options)
|
1223
|
+
if !options.key?(:should_track_returning_user)
|
1224
|
+
options[:should_track_returning_user] = @should_track_returning_user
|
1225
|
+
elsif ![true, false].include?(options[:should_track_returning_user])
|
1226
|
+
@logger.log(
|
1227
|
+
LogLevelEnum::ERROR,
|
1228
|
+
format(
|
1229
|
+
LogMessageEnum::ErrorMessages::INVALID_TRACK_RETURNING_USER_VALUE,
|
1230
|
+
file: FILE
|
1231
|
+
)
|
1232
|
+
)
|
1233
|
+
end
|
1234
|
+
options[:should_track_returning_user]
|
1235
|
+
end
|
1236
|
+
|
1237
|
+
def is_eligible_to_send_impression(should_track_returning_user = false)
|
1238
|
+
!@user_storage || !@variation_decider.has_stored_variation || should_track_returning_user
|
1239
|
+
end
|
1240
|
+
|
1241
|
+
def flush_events
|
1242
|
+
unless @is_instance_valid
|
1243
|
+
@logger.log(
|
1244
|
+
LogLevelEnum::ERROR,
|
1245
|
+
format(
|
1246
|
+
LogMessageEnum::ErrorMessages::API_CONFIG_CORRUPTED,
|
1247
|
+
file: FILE,
|
1248
|
+
api_name: ApiMethods::FLUSH_EVENTS
|
1249
|
+
)
|
1250
|
+
)
|
1251
|
+
return
|
1252
|
+
end
|
1253
|
+
result = @batch_events_queue.flush(manual: true)
|
1254
|
+
@batch_events_queue.kill_thread
|
1255
|
+
result
|
902
1256
|
rescue StandardError => e
|
903
1257
|
@logger.log(
|
904
1258
|
LogLevelEnum::ERROR,
|
905
1259
|
format(
|
906
1260
|
LogMessageEnum::ErrorMessages::API_NOT_WORKING,
|
907
1261
|
file: FILE,
|
908
|
-
api_name: ApiMethods::
|
1262
|
+
api_name: ApiMethods::FLUSH_EVENTS,
|
909
1263
|
exception: e
|
910
1264
|
)
|
911
1265
|
)
|
912
1266
|
false
|
913
1267
|
end
|
1268
|
+
|
1269
|
+
def get_goal_type_to_track(options)
|
1270
|
+
goal_type_to_track = nil
|
1271
|
+
if !options.key?(:goal_type_to_track)
|
1272
|
+
if @goal_type_to_track
|
1273
|
+
goal_type_to_track = @goal_type_to_track
|
1274
|
+
else
|
1275
|
+
goal_type_to_track = GOAL_TYPES['ALL']
|
1276
|
+
end
|
1277
|
+
elsif GOAL_TYPES.key? options[:goal_type_to_track]
|
1278
|
+
goal_type_to_track = options[:goal_type_to_track]
|
1279
|
+
else
|
1280
|
+
@logger.log(
|
1281
|
+
LogLevelEnum::ERROR,
|
1282
|
+
format(
|
1283
|
+
LogMessageEnum::ErrorMessages::INVALID_GOAL_TYPE,
|
1284
|
+
file: FILE
|
1285
|
+
)
|
1286
|
+
)
|
1287
|
+
end
|
1288
|
+
goal_type_to_track
|
1289
|
+
end
|
914
1290
|
end
|