kameleoon-client-ruby 2.1.0 → 2.2.0

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