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.
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,
@@ -253,7 +437,7 @@ class VWO
253
437
  exception: e
254
438
  )
255
439
  )
256
- nil
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
- (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)
@@ -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[Array|Hash] :args Contains revenue value and custom variables
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
- # Get the campaign settings
423
- campaign = get_campaign(@settings_file, campaign_key)
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
- # Validate campaign
426
- if campaign.nil? || (campaign['status'] != STATUS_RUNNING)
427
- # log error
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::CAMPAIGN_NOT_RUNNING,
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
- campaign_type = campaign['type']
604
+ # Get campaigns settings
605
+ campaigns = get_campaigns(@settings_file, campaign_key, goal_identifier, goal_type_to_track)
441
606
 
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
607
+ # Validate campaign
608
+ if campaigns.nil?
609
+ return nil
455
610
  end
456
611
 
457
- variation = @variation_decider.get_variation(user_id, campaign, ApiMethods::TRACK, campaign_key, custom_variables, variation_targeting_variables)
458
-
459
- if variation
460
- goal = get_campaign_goal(campaign, goal_identifier)
461
- if goal.nil?
462
- @logger.log(
463
- LogLevelEnum::ERROR,
464
- format(
465
- LogMessageEnum::ErrorMessages::TRACK_API_GOAL_NOT_FOUND,
466
- file: FILE,
467
- goal_identifier: goal_identifier,
468
- user_id: user_id,
469
- campaign_key: campaign_key,
470
- api_name: ApiMethods::TRACK
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
- return false
474
- elsif goal['type'] == GoalTypes::REVENUE && !valid_value?(revenue_value)
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
- LogMessageEnum::ErrorMessages::TRACK_API_REVENUE_NOT_PASSED_FOR_REVENUE_GOAL,
751
+ e.message,
479
752
  file: FILE,
480
- user_id: user_id,
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
- 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)
757
+ end
499
758
 
500
- @logger.log(
501
- LogLevelEnum::INFO,
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
- false
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
- (variation_targeting_variables.nil? || valid_hash?(variation_targeting_variables))
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
- impression = create_impression(
615
- @settings_file,
616
- campaign['id'],
617
- variation['id'],
618
- user_id
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
- @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
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::FEATURE_NOT_ENABLED_FOR_USER,
937
+ LogMessageEnum::InfoMessages::USER_ALREADY_TRACKED,
650
938
  file: FILE,
651
939
  user_id: user_id,
652
- feature_key: campaign_key,
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
- # 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))
980
+ unless @is_instance_valid
699
981
  @logger.log(
700
982
  LogLevelEnum::ERROR,
701
983
  format(
702
- LogMessageEnum::ErrorMessages::GET_FEATURE_VARIABLE_VALUE_API_INVALID_PARAMS,
984
+ LogMessageEnum::ErrorMessages::API_CONFIG_CORRUPTED,
703
985
  file: FILE,
704
- api_name: ApiMethods::GET_FEATURE_VARIABLE_VALUE
986
+ api_name: ApiMethods.GET_FEATURE_VARIABLE_VALUE
705
987
  )
706
988
  )
707
989
  return
708
990
  end
709
991
 
710
- unless @is_instance_valid
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::API_CONFIG_CORRUPTED,
1001
+ LogMessageEnum::ErrorMessages::GET_FEATURE_VARIABLE_VALUE_API_INVALID_PARAMS,
715
1002
  file: FILE,
716
- api_name: ApiMethods.GET_FEATURE_VARIABLE_VALUE
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
- impression = get_url_params(@settings_file, tag_key, tag_value, user_id)
887
-
888
- @event_dispatcher.dispatch(impression)
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::INFO,
1207
+ LogLevelEnum::ERROR,
892
1208
  format(
893
- LogMessageEnum::InfoMessages::MAIN_KEYS_FOR_PUSH_API,
1209
+ LogMessageEnum::ErrorMessages::API_NOT_WORKING,
894
1210
  file: FILE,
895
- u: impression['u'],
896
- user_id: impression['uId'],
897
- account_id: impression['account_id'],
898
- tags: impression['tags']
1211
+ api_name: ApiMethods::PUSH,
1212
+ exception: e
899
1213
  )
900
1214
  )
901
- true
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::PUSH,
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