kameleoon-client-ruby 2.1.0 → 2.2.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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/lib/kameleoon/client.rb +105 -151
  3. data/lib/kameleoon/configuration/rule.rb +1 -1
  4. data/lib/kameleoon/data/browser.rb +39 -0
  5. data/lib/kameleoon/data/conversion.rb +33 -0
  6. data/lib/kameleoon/data/custom_data.rb +60 -0
  7. data/lib/kameleoon/data/data.rb +38 -0
  8. data/lib/kameleoon/data/device.rb +31 -0
  9. data/lib/kameleoon/data/page_view.rb +34 -0
  10. data/lib/kameleoon/data/user_agent.rb +14 -0
  11. data/lib/kameleoon/network/activity_event.rb +31 -0
  12. data/lib/kameleoon/network/content_type.rb +11 -0
  13. data/lib/kameleoon/network/experiment_event.rb +35 -0
  14. data/lib/kameleoon/network/method.rb +10 -0
  15. data/lib/kameleoon/network/net_provider.rb +91 -0
  16. data/lib/kameleoon/network/network_manager.rb +114 -0
  17. data/lib/kameleoon/network/request.rb +20 -0
  18. data/lib/kameleoon/network/response.rb +30 -0
  19. data/lib/kameleoon/network/uri_helper.rb +37 -0
  20. data/lib/kameleoon/network/url_provider.rb +71 -0
  21. data/lib/kameleoon/targeting/condition.rb +40 -11
  22. data/lib/kameleoon/targeting/condition_factory.rb +35 -12
  23. data/lib/kameleoon/targeting/conditions/browser_condition.rb +71 -0
  24. data/lib/kameleoon/targeting/conditions/conversion_condition.rb +21 -0
  25. data/lib/kameleoon/targeting/conditions/custom_datum.rb +60 -65
  26. data/lib/kameleoon/targeting/conditions/device_condition.rb +21 -0
  27. data/lib/kameleoon/targeting/conditions/exclusive_experiment.rb +0 -12
  28. data/lib/kameleoon/targeting/conditions/page_title_condition.rb +21 -0
  29. data/lib/kameleoon/targeting/conditions/page_url_condition.rb +21 -0
  30. data/lib/kameleoon/targeting/conditions/sdk_language_condition.rb +65 -0
  31. data/lib/kameleoon/targeting/conditions/string_value_condition.rb +40 -0
  32. data/lib/kameleoon/targeting/conditions/target_experiment.rb +4 -8
  33. data/lib/kameleoon/targeting/conditions/unknown_condition.rb +15 -0
  34. data/lib/kameleoon/targeting/conditions/visitor_code_condition.rb +16 -0
  35. data/lib/kameleoon/targeting/models.rb +0 -24
  36. data/lib/kameleoon/utils.rb +1 -1
  37. data/lib/kameleoon/version.rb +28 -1
  38. metadata +28 -4
  39. data/lib/kameleoon/data.rb +0 -175
  40. data/lib/kameleoon/request.rb +0 -90
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ba4f943be2e686ba2cca810ef8f456622e25733eda03808c7b3d81e2d72b1ee3
4
- data.tar.gz: cef2edf8ef8e449f3150f73c5df9d76477342a52e49fbf90885b458fd6aa1b57
3
+ metadata.gz: c3ada1f2651143c67ac458a8adab629e1d6ad17e95a204809a0a2b44f0ee3a28
4
+ data.tar.gz: 306fab877c00913cc4b13cecfdf9e773c2bb735be110747df37d509b537d2e01
5
5
  SHA512:
6
- metadata.gz: 5b4e8f09c36222e08e9b426e90db6fb05476aa0e9bcd53036de31a8d7819db7b95ea5960adc6cc059494eb5355d9d912dd73a507923904723cf9ca63887adb4a
7
- data.tar.gz: 7f7f9d409053f873d9f7daa9b281b7c926d3b314fe5a2e7bd0814d3a7b5fdf4f8c5f61c6eed9f65a5fd4c3d11665a0efc5464164636e9affd0bac96f95d3798f
6
+ metadata.gz: 38ff8932ce2f9a0e9d72644ae47de0d0f34434943f33328f0459607e34769c83e64ff6325989f9722cc8cb202d440d5d8891dea073179d269e01d6b49fa361f9
7
+ data.tar.gz: b3ee0f99356eac881b01970269cde022435872c270268ffc6ec8c0dc0d31abe3816914a497edb15e744810f60190165c84424f4799876ff56d1e24e1590d56f8
@@ -1,12 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'kameleoon/targeting/models'
4
- require 'kameleoon/request'
5
4
  require 'kameleoon/exceptions'
6
5
  require 'kameleoon/cookie'
6
+ require 'kameleoon/data/custom_data'
7
+ require 'kameleoon/data/user_agent'
7
8
  require 'kameleoon/configuration/feature_flag'
8
9
  require 'kameleoon/configuration/variation'
9
10
  require 'kameleoon/configuration/settings'
11
+ require 'kameleoon/network/activity_event'
12
+ require 'kameleoon/network/experiment_event'
13
+ require 'kameleoon/network/url_provider'
14
+ require 'kameleoon/network/network_manager'
10
15
  require 'kameleoon/real_time/real_time_configuration_service'
11
16
  require 'kameleoon/storage/variation_storage'
12
17
  require 'kameleoon/hybrid/manager'
@@ -26,7 +31,6 @@ module Kameleoon
26
31
  # Client for Kameleoon
27
32
  #
28
33
  class Client
29
- include Request
30
34
  include Cookie
31
35
  include Exception
32
36
 
@@ -39,9 +43,6 @@ module Kameleoon
39
43
  @default_timeout = config['default_timeout'] || default_timeout # in ms
40
44
  refresh_interval = config['actions_configuration_refresh_interval']
41
45
  @interval = refresh_interval.nil? ? interval : "#{refresh_interval}m"
42
- @tracking_url = config['tracking_url'] || API_SSX_URL
43
- @api_data_url = 'https://api-data.kameleoon.com'
44
- @events_url = 'https://events.kameleoon.com:8110/'
45
46
  @real_time_configuration_service = nil
46
47
  @update_configuration_handler = nil
47
48
  @fetch_configuration_update_job = nil
@@ -62,6 +63,12 @@ module Kameleoon
62
63
  Kameleoon::Storage::CacheFactoryImpl.new,
63
64
  method(:log)
64
65
  )
66
+ @network_manager = Network::NetworkManager.new(
67
+ @environment,
68
+ @default_timeout,
69
+ Network::UrlProvider.new(@site_code, Network::UrlProvider::DEFAULT_DATA_API_URL),
70
+ method(:log)
71
+ )
65
72
  end
66
73
 
67
74
  ##
@@ -129,22 +136,22 @@ module Kameleoon
129
136
  "Experiment #{experiment_id} is not found"
130
137
  end
131
138
  check_site_code_enable(experiment)
132
- if check_targeting(visitor_code, experiment_id, experiment)
139
+ targeted = check_targeting(visitor_code, experiment_id, experiment)
140
+ if targeted
133
141
  # saved_variation = get_valid_saved_variation(visitor_code, experiment)
134
142
  variation_id = calculate_variation_for_experiment(visitor_code, experiment)
135
- if !variation_id.nil?
136
- track_experiment(visitor_code, experiment_id, variation_id)
137
- save_variation(visitor_code, experiment_id, variation_id)
138
- variation_id
139
- else
140
- track_experiment(visitor_code, experiment_id, REFERENCE, true)
141
- raise Exception::NotAllocated.new(visitor_code),
142
- "Experiment #{experiment_id} is not active for visitor #{visitor_code}"
143
- end
144
- else
143
+ save_variation(visitor_code, experiment_id, variation_id)
144
+ end
145
+ _send_tracking_request(visitor_code, experiment_id, variation_id)
146
+ unless targeted
145
147
  raise Exception::NotTargeted.new(visitor_code),
146
148
  "Experiment #{experiment_id} is not targeted for visitor #{visitor_code}"
147
149
  end
150
+ if variation_id.nil?
151
+ raise Exception::NotAllocated.new(visitor_code),
152
+ "Experiment #{experiment_id} is not active for visitor #{visitor_code}"
153
+ end
154
+ variation_id
148
155
  end
149
156
 
150
157
  ##
@@ -208,7 +215,7 @@ module Kameleoon
208
215
  def flush(visitor_code = nil)
209
216
  check_visitor_code(visitor_code) unless visitor_code.nil?
210
217
  if !visitor_code.nil?
211
- track_data(visitor_code)
218
+ _send_tracking_request(visitor_code)
212
219
  else
213
220
  @data.select { |_, values| values.any? { |data| !data.sent } }.each_key { |key| flush(key) }
214
221
  end
@@ -265,7 +272,7 @@ module Kameleoon
265
272
  # associated targeting segment conditions were not fulfilled. He should see the reference variation
266
273
  # @raise [Kameleoon::Exception::VisitorCodeNotValid] If the visitor code is empty or longer than 255 chars
267
274
  #
268
- # DEPRECATED. Please use `is_feature_active` instead.
275
+ # DEPRECATED. Please use `feature_active?` instead.
269
276
  def activate_feature(visitor_code, feature_key)
270
277
  warn '[DEPRECATION] `activate_feature` is deprecated. Please use `feature_active?` instead.'
271
278
  feature_active?(visitor_code, feature_key)
@@ -384,7 +391,7 @@ module Kameleoon
384
391
  end
385
392
 
386
393
  ##
387
- # The get_remote_date method allows you to retrieve data (according to a key passed as argument)
394
+ # The get_remote_data method allows you to retrieve data (according to a key passed as argument)
388
395
  # stored on a remote Kameleoon server. Usually data will be stored on our remote
389
396
  # servers via the use of our Data API.
390
397
  # This method, along with the availability of our highly scalable servers for this purpose, provides a convenient
@@ -395,21 +402,36 @@ module Kameleoon
395
402
  # This field is optional.
396
403
  #
397
404
  # @return [Hash] Hash object of the json object.
398
- def get_remote_date(key, timeout = @default_timeout)
399
- connexion_options = { connect_timeout: (timeout.to_f / 1000.0) }
400
- path = get_api_data_request_url(key)
401
- log "Retrieve API Data connexion: #{connexion_options.inspect}"
402
- response = get_sync(@api_data_url + path, connexion_options)
403
- return nil unless successful_sync?(response)
404
-
405
- JSON.parse(response.body) unless response.nil?
405
+ def get_remote_data(key, timeout = @default_timeout)
406
+ response = @network_manager.get_remote_data(key, timeout)
407
+ JSON.parse(response) if response
406
408
  end
407
409
 
408
410
  ##
409
411
  # DEPRECATED. Please use `get_feature_variable` instead.
410
412
  def retrieve_data_from_remote_source(key, timeout = @default_timeout)
411
413
  warn '[DEPRECATION] `retrieve_data_from_remote_source` is deprecated. Please use `get_remote_date` instead.'
412
- get_remote_date(key, timeout)
414
+ get_remote_data(key, timeout)
415
+ end
416
+
417
+ ##
418
+ # The get_remote_visitor_data is a method for retrieving custom data for
419
+ # the latest visit of `visitor_code` from Kameleoon Data API and optionally adding it
420
+ # to the storage so that other methods could decide whether the current visitor is targeted or not.
421
+ #
422
+ # @param [String] visitor_code The visitor code for which you want to retrieve the assigned data.
423
+ # This field is mandatory.
424
+ # @param [Bool] add_data A boolean indicating whether the method should automatically add retrieved data
425
+ # for a visitor. If not specified, the default value is `True`. This field is optional.
426
+ # @param [Int] timeout Timeout for request (in milliseconds). Equals default_timeout in a config file.
427
+ # This field is optional.
428
+ #
429
+ # @return [Array] An array of data assigned to the given visitor.
430
+ def get_remote_visitor_data(visitor_code, timeout = nil, add_data: true)
431
+ response = @network_manager.get_remote_visitor_data(visitor_code, timeout)
432
+ data_array = parse_custom_data_array(visitor_code, response)
433
+ add_data(visitor_code, *data_array) if add_data && !data_array.empty?
434
+ data_array
413
435
  end
414
436
 
415
437
  ##
@@ -490,7 +512,6 @@ module Kameleoon
490
512
 
491
513
  private
492
514
 
493
- API_SSX_URL = 'https://api-ssx.kameleoon.com'
494
515
  REFERENCE = 0
495
516
  DEFAULT_ENVIRONMENT = 'production'
496
517
  CACHE_EXPIRATION_TIMEOUT = 5
@@ -507,7 +528,7 @@ module Kameleoon
507
528
  def fetch_configuration_job(time_stamp = nil)
508
529
  EM.synchrony do
509
530
  begin
510
- ok = obtain_configuration(@site_code, @environment, time_stamp)
531
+ ok = obtain_configuration(time_stamp)
511
532
  if !ok && @settings.real_time_update
512
533
  @settings.real_time_update = false
513
534
  log('Switching to polling mode due to failed fetch')
@@ -540,10 +561,10 @@ module Kameleoon
540
561
  def start_real_time_configuration_service_if_needed
541
562
  return unless @real_time_configuration_service.nil?
542
563
 
543
- events_url = "#{@events_url}sse?siteCode=#{@site_code}"
564
+ url = @network_manager.url_provider.make_real_time_url
544
565
  fetch_func = proc { |real_time_event| fetch_configuration_job(real_time_event.time_stamp) }
545
566
  @real_time_configuration_service =
546
- Kameleoon::RealTime::RealTimeConfigurationService.new(events_url, fetch_func, method(:log))
567
+ Kameleoon::RealTime::RealTimeConfigurationService.new(url, fetch_func, method(:log))
547
568
  end
548
569
 
549
570
  def stop_real_time_configuration_service_if_needed
@@ -577,15 +598,12 @@ module Kameleoon
577
598
  # { 'field' => field, 'operator' => operator, 'parameters' => parameters }
578
599
  # end
579
600
 
580
- def obtain_configuration(site_code, environment = @environment, time_stamp = nil)
601
+ def obtain_configuration(time_stamp = nil)
581
602
  log 'Fetching configuration from Client-Config service'
582
- request_path = "mobile?siteCode=#{site_code}"
583
- request_path += "&environment=#{environment}" unless environment.nil?
584
- request_path += "&ts=#{time_stamp}" unless time_stamp.nil?
585
- request = request_configuration(request_path)
586
- return false unless request
603
+ response = @network_manager.fetch_configuration(time_stamp)
604
+ return false unless response
587
605
 
588
- configuration = JSON.parse(request.response)
606
+ configuration = JSON.parse(response)
589
607
  @experiments = Kameleoon::Configuration::Experiment.create_from_array(configuration['experiments']) ||
590
608
  @experiments
591
609
  @feature_flags = Kameleoon::Configuration::FeatureFlag.create_from_array(
@@ -593,7 +611,7 @@ module Kameleoon
593
611
  )
594
612
  @settings.update(configuration['configuration'])
595
613
  call_update_handler_if_needed(!time_stamp.nil?)
596
- log "Feature flags are fetched: #{request.inspect}"
614
+ log "Feature flags are fetched: #{response.inspect}"
597
615
  true
598
616
  end
599
617
 
@@ -616,46 +634,6 @@ module Kameleoon
616
634
  nil
617
635
  end
618
636
 
619
- def request_configuration(path)
620
- request = EM::Synchrony.sync get({ path: path }, CLIENT_CONFIG_URL)
621
- unless successful?(request)
622
- log "Failed to fetch #{request.inspect}"
623
- return false
624
- end
625
- request
626
- end
627
-
628
- def get_common_ssx_parameters(visitor_code)
629
- {
630
- nonce: Kameleoon::Utils.generate_random_string(16),
631
- siteCode: @site_code,
632
- visitorCode: visitor_code
633
- }
634
- end
635
-
636
- def get_experiment_register_url(visitor_code, experiment_id, variation_id = nil, none_variation = false)
637
- url = "/experimentTracking?#{URI.encode_www_form(get_common_ssx_parameters(visitor_code))}"
638
- url += "&experimentId=#{experiment_id}"
639
- return url if variation_id.nil?
640
-
641
- url += "&variationId=#{variation_id}"
642
- url += '&noneVariation=true' if none_variation
643
-
644
- url
645
- end
646
-
647
- def get_data_register_url(visitor_code)
648
- "/dataTracking?#{URI.encode_www_form(get_common_ssx_parameters(visitor_code))}"
649
- end
650
-
651
- def get_api_data_request_url(key)
652
- mapKey = {
653
- siteCode: site_code,
654
- key: key
655
- }
656
- "/data?#{URI.encode_www_form(mapKey)}"
657
- end
658
-
659
637
  def find_feature_flag(feature_key)
660
638
  if feature_key.is_a?(String)
661
639
  feature_flag = @feature_flags.select { |ff| ff.feature_key == feature_key }.first
@@ -669,67 +647,6 @@ module Kameleoon
669
647
  feature_flag
670
648
  end
671
649
 
672
- def track_experiment(visitor_code, experiment_id, variation_id = nil, none_variation: false)
673
- data_not_sent = data_not_sent(visitor_code)
674
- options = {
675
- path: get_experiment_register_url(visitor_code, experiment_id, variation_id, none_variation),
676
- body: (data_not_sent.map(&:obtain_full_post_text_line).join("\n") || '').encode('UTF-8'),
677
- head: { 'Content-Type': 'text/plain' }
678
- }
679
- set_user_agent_to_headers(visitor_code, options[:head])
680
- trial = 0
681
- success = false
682
- log "Start post tracking experiment: #{data_not_sent.inspect}"
683
- Thread.new do
684
- while trial < 10
685
- log "Send Experiment Tracking #{options.inspect}"
686
- response = post_sync(options, @tracking_url)
687
- log "Response #{response.inspect}"
688
- if successful_sync?(response)
689
- data_not_sent.each { |it| it.sent = true }
690
- success = true
691
- break
692
- end
693
- trial += 1
694
- end
695
- if success
696
- log "Post to experiment tracking is done after #{trial + 1} trials"
697
- else
698
- log "Post to experiment tracking is failed after #{trial} trials"
699
- end
700
- end
701
- end
702
-
703
- def track_data(visitor_code)
704
- Thread.new do
705
- trials = 0
706
- data_not_sent = data_not_sent(visitor_code)
707
- log "Start post tracking data: #{data_not_sent.inspect}"
708
- while trials < 10 && !data_not_sent.empty?
709
- options = {
710
- path: get_data_register_url(visitor_code),
711
- body: (data_not_sent.map(&:obtain_full_post_text_line).join("\n") || '').encode('UTF-8'),
712
- head: { 'Content-Type': 'text/plain' }
713
- }
714
- set_user_agent_to_headers(visitor_code, options[:head])
715
- log "Post tracking data for visitor_code: #{visitor_code} with options: #{options.inspect}"
716
- response = post_sync(options, @tracking_url)
717
- log "Response #{response.inspect}"
718
- if successful_sync?(response)
719
- data_not_sent.each { |it| it.sent = true }
720
- success = true
721
- break
722
- end
723
- trials += 1
724
- end
725
- if success
726
- log "Post to data tracking is done after #{trials + 1} trials"
727
- else
728
- log "Post to data tracking is failed after #{trials} trials"
729
- end
730
- end
731
- end
732
-
733
650
  def check_site_code_enable(campaign)
734
651
  raise Exception::SiteCodeDisabled.new(site_code), site_code unless campaign.site_enabled
735
652
  end
@@ -770,8 +687,17 @@ module Kameleoon
770
687
  def get_condition_data(type, visitor_code, campaign_id)
771
688
  condition_data = nil
772
689
  case type
773
- when Kameleoon::Targeting::ConditionType::CUSTOM_DATUM
690
+ when Kameleoon::Targeting::ConditionType::CUSTOM_DATUM,
691
+ Kameleoon::Targeting::ConditionType::PAGE_TITLE,
692
+ Kameleoon::Targeting::ConditionType::PAGE_URL,
693
+ Kameleoon::Targeting::ConditionType::DEVICE_TYPE,
694
+ Kameleoon::Targeting::ConditionType::BROWSER,
695
+ Kameleoon::Targeting::ConditionType::CONVERSIONS
774
696
  condition_data = (@data[visitor_code] || []).flatten
697
+ when Kameleoon::Targeting::ConditionType::SDK_LANGUAGE
698
+ condition_data = Kameleoon::Targeting::SdkInfo.new(Kameleoon::SDK_NAME, Kameleoon::SDK_VERSION)
699
+ when Kameleoon::Targeting::ConditionType::VISITOR_CODE
700
+ condition_data = visitor_code
775
701
  when Kameleoon::Targeting::ConditionType::TARGET_EXPERIMENT
776
702
  condition_data = @variation_storage.get_hash_saved_variation_id(visitor_code)
777
703
  when Kameleoon::Targeting::ConditionType::EXCLUSIVE_EXPERIMENT
@@ -790,10 +716,11 @@ module Kameleoon
790
716
  variation, rule = _calculate_variation_key_for_feature(visitor_code, feature_flag)
791
717
  variation_key = _get_variation_key(variation, rule, feature_flag)
792
718
  unless rule.nil?
793
- save_variation(visitor_code, rule.experiment_id, variation.variation_id) unless variation.nil?
719
+ experiment_id = rule.experiment_id
794
720
  variation_id = variation.variation_id unless variation.nil?
795
- _send_tracking_request(visitor_code, rule.experiment_id, variation_id)
721
+ save_variation(visitor_code, experiment_id, variation_id)
796
722
  end
723
+ _send_tracking_request(visitor_code, experiment_id, variation_id)
797
724
  [feature_flag, variation_key]
798
725
  end
799
726
 
@@ -809,11 +736,12 @@ module Kameleoon
809
736
  hash_rule = obtain_hash_double_rule(visitor_code, rule.id, rule.respool_time)
810
737
  # check main expostion for rule with hashRule
811
738
  if hash_rule <= rule.exposition
739
+ return [rule.variation_by_exposition[0], rule] if rule.targeted_delivery_type?
740
+
812
741
  # uses for variation's expositions
813
742
  hash_variation = obtain_hash_double_rule(visitor_code, rule.experiment_id, rule.respool_time)
814
743
  # get variation key with new hashVariation
815
744
  variation = rule.get_variation(hash_variation)
816
- # variation_key can be nil for experiment rules only, for targeted rule will be always exist
817
745
  return [variation, rule] unless variation.nil?
818
746
  # if visitor is targeted for targeted rule then break cycle -> return default
819
747
  elsif rule.targeted_delivery_type?
@@ -833,20 +761,23 @@ module Kameleoon
833
761
  def _get_variation_key(var_by_exp, rule, feature_flag)
834
762
  return var_by_exp.variation_key unless var_by_exp.nil?
835
763
 
836
- return Kameleoon::Configuration::VariationType::VARIATION_OFF if !rule.nil? && rule.experiment_type?
764
+ return Kameleoon::Configuration::VariationType::VARIATION_OFF if !rule.nil? && rule.experimentation_type?
837
765
 
838
766
  feature_flag.default_variation_key
839
767
  end
840
768
 
841
769
  ##
842
770
  # helper method for sending tracking requests for new FF
843
- def _send_tracking_request(visitor_code, experiment_id, variation_id)
844
- if !experiment_id.nil?
845
- variation_reference_id = variation_id || 0
846
- track_experiment(visitor_code, experiment_id, variation_reference_id)
771
+ def _send_tracking_request(visitor_code, experiment_id = nil, variation_id = nil)
772
+ user_agent = @user_agents[visitor_code]&.value
773
+ data_not_sent = data_not_sent(visitor_code)
774
+ if experiment_id.nil? || variation_id.nil?
775
+ data_not_sent.append(Network::ActivityEvent.new) if data_not_sent.empty?
847
776
  else
848
- log 'An attempt to send a request with null experimentId was blocked'
777
+ data_not_sent.append(Network::ExperimentEvent.new(experiment_id, variation_id))
849
778
  end
779
+ log "Start post tracking: #{data_not_sent.inspect}"
780
+ @network_manager.send_tracking_data(visitor_code, data_not_sent, user_agent)
850
781
  end
851
782
 
852
783
  ##
@@ -862,5 +793,28 @@ module Kameleoon
862
793
  'Unknown type for feature variable'
863
794
  end
864
795
  end
796
+
797
+ ##
798
+ # helper method used by `get_remote_visitor_data`
799
+ def parse_custom_data_array(visitor_code, response)
800
+ return [] unless response
801
+
802
+ raw = JSON.parse(response)
803
+ latest_record = raw['currentVisit']
804
+ if latest_record.nil?
805
+ previous_visits = raw['previousVisits']
806
+ return [] unless previous_visits
807
+
808
+ latest_record = previous_visits.first
809
+ end
810
+ events = latest_record['customDataEvents']
811
+ return [] if events.nil?
812
+
813
+ events.map { |ev| ev['data'] }.compact
814
+ .map { |jcd| CustomData.new(jcd['index'] || -1, *(jcd['valuesCountMap'] || {}).keys) }
815
+ rescue StandardError => e
816
+ log("Parsing of visitor data of '#{visitor_code}' failed: #{e}")
817
+ []
818
+ end
865
819
  end
866
820
  end
@@ -45,7 +45,7 @@ module Kameleoon
45
45
  variation_by_exposition.select { |v| v.variation_key == key }.first&.variation_id
46
46
  end
47
47
 
48
- def experiment_type?
48
+ def experimentation_type?
49
49
  @type == RuleType::EXPERIMENTATION
50
50
  end
51
51
 
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'kameleoon/network/uri_helper'
5
+ require_relative 'data'
6
+
7
+ module Kameleoon
8
+ module BrowserType
9
+ CHROME = 0
10
+ INTERNET_EXPLORER = 1
11
+ FIREFOX = 2
12
+ SAFARI = 3
13
+ OPERA = 4
14
+ OTHER = 5
15
+ end
16
+
17
+ # Represents browser data for tracking calls
18
+ class Browser < Data
19
+ attr_reader :type, :version
20
+
21
+ # @param [BrowserType] browser_type Browser type, can be: CHROME, INTERNET_EXPLORER, FIREFOX, SAFARI, OPERA, OTHER
22
+ # @param [float] version Version of browser
23
+ def initialize(browser_type, version = Float::NAN)
24
+ super(DataType::BROWSER)
25
+ @type = browser_type
26
+ @version = version
27
+ end
28
+
29
+ def obtain_full_post_text_line
30
+ params = {
31
+ eventType: 'staticData',
32
+ browserIndex: @type,
33
+ nonce: nonce
34
+ }
35
+ params[:browserVersion] = @version if @version.is_a?(Integer) || (@version.is_a?(Float) && !@version.nan?)
36
+ Kameleoon::Network::UriHelper.encode_query(params)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'kameleoon/network/uri_helper'
5
+ require_relative 'data'
6
+
7
+ module Kameleoon
8
+ # Conversion class uses for tracking conversion
9
+ class Conversion < Data
10
+ attr_reader :goal_id, :revenue, :negative
11
+
12
+ # @param [Integer] goal_id Id of the goal associated to the conversion
13
+ # @param [Float] revenue Optional field - Revenue associated to the conversion.
14
+ # @param [Boolean] negative Optional field - If the revenue is negative. By default it's positive.
15
+ def initialize(goal_id, revenue = 0.0, negative = false)
16
+ super(DataType::CONVERSION)
17
+ @goal_id = goal_id
18
+ @revenue = revenue || 0.0
19
+ @negative = negative || false
20
+ end
21
+
22
+ def obtain_full_post_text_line
23
+ params = {
24
+ eventType: 'conversion',
25
+ goalId: @goal_id,
26
+ revenue: @revenue,
27
+ negative: @negative,
28
+ nonce: nonce
29
+ }
30
+ Kameleoon::Network::UriHelper.encode_query(params)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'kameleoon/network/uri_helper'
5
+ require_relative 'data'
6
+
7
+ module Kameleoon
8
+ # Represents any custom data for targeting conditions
9
+ class CustomData < Data
10
+ attr_reader :id, :values
11
+
12
+ # @param [Integer] id Id of the custom data
13
+ # @param [String] value Value of the custom data
14
+ # @param [Array] values Array of values of the custom data
15
+ #
16
+ # @overload
17
+ # @param [Hash] hash Json value encoded in a hash.
18
+ # rubocop:disable Metrics/MethodLength
19
+ def initialize(arg0, *args)
20
+ super(DataType::CUSTOM)
21
+ if arg0.is_a?(Hash)
22
+ hash = arg0
23
+ id = hash['id']
24
+ raise Kameleoon::Exception::NotFound.new('id'), '"id" is mandatory' if id.nil?
25
+
26
+ @id = id.to_s
27
+ value = hash['value']
28
+ values = hash['values']
29
+ if values.nil? && value.nil?
30
+ raise Kameleoon::Exception::NotFound.new('value or values'), '"value" or "values" is mandatory'
31
+ end
32
+
33
+ if values.nil?
34
+ @values = [value]
35
+ else
36
+ @values = values.is_a?(Array) ? values.dup : [values]
37
+ @values.append(value) unless value.nil?
38
+ end
39
+ else
40
+ @id = arg0.to_s
41
+ @values = args
42
+ end
43
+ end
44
+ # rubocop:enable Metrics/MethodLength
45
+
46
+ def obtain_full_post_text_line
47
+ return '' if @values.empty?
48
+
49
+ str_values = JSON.generate(Hash[@values.collect { |k| [k, 1] }])
50
+ params = {
51
+ eventType: 'customData',
52
+ index: @id,
53
+ valuesCountMap: str_values,
54
+ overwrite: 'true',
55
+ nonce: nonce
56
+ }
57
+ Kameleoon::Network::UriHelper.encode_query(params)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'kameleoon/exceptions'
5
+
6
+ module Kameleoon
7
+ NONCE_LENGTH = 16
8
+
9
+ module DataType
10
+ CUSTOM = 'CUSTOM'
11
+ BROWSER = 'BROWSER'
12
+ CONVERSION = 'CONVERSION'
13
+ DEVICE = 'DEVICE'
14
+ PAGE_VIEW = 'PAGE_VIEW'
15
+ end
16
+
17
+ # Represents base class for any Kameleoon data
18
+ class Data
19
+ attr_reader :instance
20
+ attr_accessor :sent
21
+
22
+ def initialize(data_type)
23
+ @instance = data_type
24
+ @sent = false
25
+ end
26
+
27
+ def obtain_full_post_text_line
28
+ raise KameleoonError.new('ToDo: implement this method.'), 'ToDo: implement this method.'
29
+ end
30
+
31
+ private
32
+
33
+ def nonce
34
+ @nonce = Kameleoon::Utils.generate_random_string(NONCE_LENGTH) if @nonce.nil?
35
+ @nonce
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'kameleoon/network/uri_helper'
5
+ require_relative 'data'
6
+
7
+ module Kameleoon
8
+ module DeviceType
9
+ PHONE = 'PHONE'
10
+ TABLET = 'TABLET'
11
+ DESKTOP = 'DESKTOP'
12
+ end
13
+
14
+ # Device uses for sending deviceType parameter for tracking calls
15
+ class Device < Data
16
+ attr_reader :device_type
17
+ def initialize(device_type)
18
+ super(DataType::DEVICE)
19
+ @device_type = device_type
20
+ end
21
+
22
+ def obtain_full_post_text_line
23
+ params = {
24
+ eventType: 'staticData',
25
+ deviceType: @device_type,
26
+ nonce: nonce
27
+ }
28
+ Kameleoon::Network::UriHelper.encode_query(params)
29
+ end
30
+ end
31
+ end