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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 88beb4ef415731a07ac86d4d3ce19b9e3ea077d79d00e3e35e84f2b669c802eb
4
- data.tar.gz: 2748de038b879ca5c27e237455bcbd6a62178101eae5c9fe1b2c87f64d5b25d7
3
+ metadata.gz: 53bc0be49f50d280aac4478d6879a31dce269ed69596e5ff2d04b51cbcacbcbd
4
+ data.tar.gz: f28c84dc2ace873bf1c5fd748568d06c16583b466240df54fcf9dbd5dffec888
5
5
  SHA512:
6
- metadata.gz: 38889e01c758f1336042deda0c39e2a678860eff7431bb465885bf72e9032f2277f0ef52ed5837593fd49b9b76ca1d74a673eb7774ee970422f9588b3698e0c6
7
- data.tar.gz: 481f64eb7ff87622f12ff3122dcb57618d56add48ebe99524a8557930dd4e2bea85ee7c875024a45134d4948be47fa5da54e8c6e64463dae4f11d732fd00e2dd
6
+ metadata.gz: 63bf38a8c25c69e01cf7cbfdad204379c0dd03621e947c1848a1fdf24b432bd26672a8d3cb9efded0e45582208a73a0c69142911c900e53e477e06b2d06a069e
7
+ data.tar.gz: 0b9d9165ca2ae918a048537097f2de775fe13d35547c341aa45c7edc42206273775f18120c10ca06fc02821c24652bd25e513a05989889664285210b6fd57f02
data/lib/vwo.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2019-2020 Wingify Software Pvt. Ltd.
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
- @settings ||=
122
- settings_file || VWO::Services::SettingsFileManager.new(@account_id, @sdk_key).get_settings_file
123
- @settings
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] :options Options for custom variables required for segmentation
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
- (variation_targeting_variables.nil? || valid_hash?(variation_targeting_variables))
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
- # Variation found, dispatch it to server
238
- impression = create_impression(
239
- @settings_file,
240
- campaign['id'],
241
- variation['id'],
242
- user_id
243
- )
244
- @event_dispatcher.dispatch(impression)
245
- variation['name']
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
- (variation_targeting_variables.nil? || valid_hash?(variation_targeting_variables))
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
- # Get the campaign settings
423
- campaign = get_campaign(@settings_file, campaign_key)
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
- # Validate campaign
426
- if campaign.nil? || (campaign['status'] != STATUS_RUNNING)
427
- # log error
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::CAMPAIGN_NOT_RUNNING,
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
- campaign_type = campaign['type']
610
+ # Get campaigns settings
611
+ campaigns = get_campaigns(@settings_file, campaign_key, goal_identifier, goal_type_to_track)
441
612
 
442
- if campaign_type == CampaignTypes::FEATURE_ROLLOUT
443
- @logger.log(
444
- LogLevelEnum::ERROR,
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
- variation = @variation_decider.get_variation(user_id, campaign, ApiMethods::TRACK, campaign_key, custom_variables, variation_targeting_variables)
618
+ result = {}
619
+ campaigns.each do |campaign|
620
+ campaign_type = campaign['type']
458
621
 
459
- if variation
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::TRACK_API_GOAL_NOT_FOUND,
626
+ LogMessageEnum::ErrorMessages::INVALID_API,
466
627
  file: FILE,
467
- goal_identifier: goal_identifier,
628
+ api_name: ApiMethods::TRACK,
468
629
  user_id: user_id,
469
- campaign_key: campaign_key,
470
- api_name: ApiMethods::TRACK
630
+ campaign_key: campaign['key'],
631
+ campaign_type: campaign_type
471
632
  )
472
633
  )
473
- return false
474
- elsif goal['type'] == GoalTypes::REVENUE && !valid_value?(revenue_value)
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::INFO,
754
+ LogLevelEnum::ERROR,
502
755
  format(
503
- LogMessageEnum::InfoMessages::MAIN_KEYS_FOR_IMPRESSION,
756
+ e.message,
504
757
  file: FILE,
505
- campaign_id: impression[:experiment_id],
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
- false
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
- (variation_targeting_variables.nil? || valid_hash?(variation_targeting_variables))
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
- impression = create_impression(
615
- @settings_file,
616
- campaign['id'],
617
- variation['id'],
618
- user_id
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
- @event_dispatcher.dispatch(impression)
622
- @logger.log(
623
- LogLevelEnum::INFO,
624
- format(
625
- LogMessageEnum::InfoMessages::MAIN_KEYS_FOR_IMPRESSION,
626
- file: FILE,
627
- campaign_id: impression[:experiment_id],
628
- user_id: impression[:uId],
629
- account_id: impression[:account_id],
630
- variation_id: impression[:combination]
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::FEATURE_NOT_ENABLED_FOR_USER,
941
+ LogMessageEnum::InfoMessages::USER_ALREADY_TRACKED,
650
942
  file: FILE,
651
943
  user_id: user_id,
652
- feature_key: campaign_key,
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
- # Retrieve custom variables
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::GET_FEATURE_VARIABLE_VALUE_API_INVALID_PARAMS,
988
+ LogMessageEnum::ErrorMessages::API_CONFIG_CORRUPTED,
703
989
  file: FILE,
704
- api_name: ApiMethods::GET_FEATURE_VARIABLE_VALUE
990
+ api_name: ApiMethods.GET_FEATURE_VARIABLE_VALUE
705
991
  )
706
992
  )
707
993
  return
708
994
  end
709
995
 
710
- unless @is_instance_valid
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::API_CONFIG_CORRUPTED,
1005
+ LogMessageEnum::ErrorMessages::GET_FEATURE_VARIABLE_VALUE_API_INVALID_PARAMS,
715
1006
  file: FILE,
716
- api_name: ApiMethods.GET_FEATURE_VARIABLE_VALUE
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
- impression = get_url_params(@settings_file, tag_key, tag_value, user_id)
887
-
888
- @event_dispatcher.dispatch(impression)
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::INFO,
1211
+ LogLevelEnum::ERROR,
892
1212
  format(
893
- LogMessageEnum::InfoMessages::MAIN_KEYS_FOR_PUSH_API,
1213
+ LogMessageEnum::ErrorMessages::API_NOT_WORKING,
894
1214
  file: FILE,
895
- u: impression['u'],
896
- user_id: impression['uId'],
897
- account_id: impression['account_id'],
898
- tags: impression['tags']
1215
+ api_name: ApiMethods::PUSH,
1216
+ exception: e
899
1217
  )
900
1218
  )
901
- true
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::PUSH,
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