vwo-sdk 1.6.0 → 1.16.0

Sign up to get free protection for your applications and to get access to all the features.
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