kameleoon-client-ruby 3.3.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/lib/kameleoon/configuration/custom_data_info.rb +16 -8
 - data/lib/kameleoon/configuration/data_file.rb +52 -16
 - data/lib/kameleoon/configuration/feature_flag.rb +11 -1
 - data/lib/kameleoon/configuration/rule.rb +4 -0
 - data/lib/kameleoon/configuration/settings.rb +13 -8
 - data/lib/kameleoon/configuration/variation_exposition.rb +4 -0
 - data/lib/kameleoon/data/browser.rb +4 -0
 - data/lib/kameleoon/data/conversion.rb +4 -0
 - data/lib/kameleoon/data/cookie.rb +4 -0
 - data/lib/kameleoon/data/custom_data.rb +11 -3
 - data/lib/kameleoon/data/data.rb +30 -4
 - data/lib/kameleoon/data/device.rb +4 -0
 - data/lib/kameleoon/data/geolocation.rb +5 -0
 - data/lib/kameleoon/data/kcs_heat.rb +4 -0
 - data/lib/kameleoon/data/manager/assigned_variation.rb +8 -1
 - data/lib/kameleoon/data/manager/data_array_storage.rb +7 -0
 - data/lib/kameleoon/data/manager/data_map_storage.rb +7 -0
 - data/lib/kameleoon/data/manager/page_view_visit.rb +4 -0
 - data/lib/kameleoon/data/manager/visitor.rb +197 -73
 - data/lib/kameleoon/data/manager/visitor_manager.rb +54 -17
 - data/lib/kameleoon/data/mapping_identifier.rb +33 -0
 - data/lib/kameleoon/data/operating_system.rb +4 -0
 - data/lib/kameleoon/data/page_view.rb +6 -1
 - data/lib/kameleoon/data/unique_identifier.rb +11 -0
 - data/lib/kameleoon/data/user_agent.rb +4 -0
 - data/lib/kameleoon/data/visitor_visits.rb +4 -0
 - data/lib/kameleoon/hybrid/manager.rb +19 -7
 - data/lib/kameleoon/kameleoon_client.rb +477 -178
 - data/lib/kameleoon/kameleoon_client_config.rb +65 -18
 - data/lib/kameleoon/kameleoon_client_factory.rb +15 -2
 - data/lib/kameleoon/logging/default_logger.rb +20 -0
 - data/lib/kameleoon/logging/kameleoon_logger.rb +77 -0
 - data/lib/kameleoon/logging/logger.rb +12 -0
 - data/lib/kameleoon/managers/data/data_manager.rb +36 -0
 - data/lib/kameleoon/managers/remote_data/remote_data_manager.rb +32 -15
 - data/lib/kameleoon/managers/tracking/tracking_builder.rb +149 -0
 - data/lib/kameleoon/managers/tracking/tracking_manager.rb +98 -0
 - data/lib/kameleoon/managers/tracking/visitor_tracking_registry.rb +94 -0
 - data/lib/kameleoon/managers/warehouse/warehouse_manager.rb +22 -5
 - data/lib/kameleoon/network/access_token_source.rb +46 -14
 - data/lib/kameleoon/network/cookie/cookie_manager.rb +46 -7
 - data/lib/kameleoon/network/net_provider.rb +2 -3
 - data/lib/kameleoon/network/network_manager.rb +17 -21
 - data/lib/kameleoon/network/request.rb +14 -3
 - data/lib/kameleoon/network/response.rb +4 -0
 - data/lib/kameleoon/network/url_provider.rb +3 -3
 - data/lib/kameleoon/real_time/real_time_configuration_service.rb +10 -11
 - data/lib/kameleoon/sdk_version.rb +31 -0
 - data/lib/kameleoon/targeting/condition.rb +4 -2
 - data/lib/kameleoon/targeting/conditions/browser_condition.rb +3 -3
 - data/lib/kameleoon/targeting/conditions/cookie_condition.rb +10 -10
 - data/lib/kameleoon/targeting/conditions/geolocation_condition.rb +0 -1
 - data/lib/kameleoon/targeting/conditions/number_condition.rb +4 -4
 - data/lib/kameleoon/targeting/conditions/operating_system_condition.rb +1 -2
 - data/lib/kameleoon/targeting/conditions/sdk_language_condition.rb +2 -1
 - data/lib/kameleoon/targeting/conditions/segment_condition.rb +3 -3
 - data/lib/kameleoon/targeting/conditions/string_value_condition.rb +2 -1
 - data/lib/kameleoon/targeting/models.rb +0 -14
 - data/lib/kameleoon/targeting/targeting_manager.rb +35 -7
 - data/lib/kameleoon/targeting/tree_builder.rb +10 -5
 - data/lib/kameleoon/types/remote_visitor_data_filter.rb +13 -0
 - data/lib/kameleoon/types/variable.rb +4 -0
 - data/lib/kameleoon/types/variation.rb +8 -0
 - data/lib/kameleoon/utils.rb +49 -0
 - data/lib/kameleoon/version.rb +1 -27
 - metadata +12 -2
 
| 
         @@ -0,0 +1,98 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'kameleoon/data/data'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'kameleoon/logging/kameleoon_logger'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'kameleoon/managers/tracking/tracking_builder'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'kameleoon/managers/tracking/visitor_tracking_registry'
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            module Kameleoon
         
     | 
| 
      
 9 
     | 
    
         
            +
              module Managers
         
     | 
| 
      
 10 
     | 
    
         
            +
                module Tracking
         
     | 
| 
      
 11 
     | 
    
         
            +
                  class TrackingManager
         
     | 
| 
      
 12 
     | 
    
         
            +
                    LINES_DELIMETER = "\n"
         
     | 
| 
      
 13 
     | 
    
         
            +
                    REQUEST_SIZE_LIMIT = 2560 * 1024 # 2.5 * 1024^2 characters
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                    def initialize(
         
     | 
| 
      
 16 
     | 
    
         
            +
                      data_manager, network_manager, visitor_manager, track_interval_seconds, scheduler
         
     | 
| 
      
 17 
     | 
    
         
            +
                    )
         
     | 
| 
      
 18 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug(
         
     | 
| 
      
 19 
     | 
    
         
            +
                        'CALL: TrackingManager.new(data_manager, network_manager. visitor_manager, ' \
         
     | 
| 
      
 20 
     | 
    
         
            +
                          'track_interval_seconds: %s, scheduler)', track_interval_seconds
         
     | 
| 
      
 21 
     | 
    
         
            +
                      )
         
     | 
| 
      
 22 
     | 
    
         
            +
                      @tracking_visitors = LockVisitorTrackingRegistry.new(visitor_manager)
         
     | 
| 
      
 23 
     | 
    
         
            +
                      @data_manager = data_manager
         
     | 
| 
      
 24 
     | 
    
         
            +
                      @network_manager = network_manager
         
     | 
| 
      
 25 
     | 
    
         
            +
                      @visitor_manager = visitor_manager
         
     | 
| 
      
 26 
     | 
    
         
            +
                      @track_all_job = scheduler.schedule_every(track_interval_seconds, method(:track_all)) unless scheduler.nil?
         
     | 
| 
      
 27 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug(
         
     | 
| 
      
 28 
     | 
    
         
            +
                        'RETURN: TrackingManager.new(data_manager, network_manager. visitor_manager, ' \
         
     | 
| 
      
 29 
     | 
    
         
            +
                          'track_interval_seconds: %s, scheduler)', track_interval_seconds
         
     | 
| 
      
 30 
     | 
    
         
            +
                      )
         
     | 
| 
      
 31 
     | 
    
         
            +
                    end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                    def stop
         
     | 
| 
      
 34 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug('CALL: TrackingManager.stop')
         
     | 
| 
      
 35 
     | 
    
         
            +
                      @track_all_job&.unschedule
         
     | 
| 
      
 36 
     | 
    
         
            +
                      @track_all_job = nil
         
     | 
| 
      
 37 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug('RETURN: TrackingManager.stop')
         
     | 
| 
      
 38 
     | 
    
         
            +
                    end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                    def add_visitor_code(visitor_code)
         
     | 
| 
      
 41 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug("CALL: TrackingManager.add_visitor_code(visitor_code: '%s')", visitor_code)
         
     | 
| 
      
 42 
     | 
    
         
            +
                      @tracking_visitors.add(visitor_code)
         
     | 
| 
      
 43 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug("RETURN: TrackingManager.add_visitor_code(visitor_code: '%s')", visitor_code)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                    def track_all
         
     | 
| 
      
 47 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug('CALL: TrackingManager.track_all')
         
     | 
| 
      
 48 
     | 
    
         
            +
                      track(@tracking_visitors.extract)
         
     | 
| 
      
 49 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug('RETURN: TrackingManager.track_all')
         
     | 
| 
      
 50 
     | 
    
         
            +
                    end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                    def track_visitor(visitor_code)
         
     | 
| 
      
 53 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug("CALL: TrackingManager.track_visitor(visitor_code: '%s')", visitor_code)
         
     | 
| 
      
 54 
     | 
    
         
            +
                      track([visitor_code])
         
     | 
| 
      
 55 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug("RETURN: TrackingManager.track_visitor(visitor_code: '%s')", visitor_code)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                    private
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                    def track(visitor_codes)
         
     | 
| 
      
 61 
     | 
    
         
            +
                      builder = TrackingBuilder.new(visitor_codes, @data_manager.data_file, @visitor_manager, REQUEST_SIZE_LIMIT)
         
     | 
| 
      
 62 
     | 
    
         
            +
                      builder.build
         
     | 
| 
      
 63 
     | 
    
         
            +
                      unless builder.visitor_codes_to_keep.empty?
         
     | 
| 
      
 64 
     | 
    
         
            +
                        Logging::KameleoonLogger.warning(
         
     | 
| 
      
 65 
     | 
    
         
            +
                          'Visitor data to be tracked exceeded the request size limit. ' \
         
     | 
| 
      
 66 
     | 
    
         
            +
                          'Some visitor data is kept to be sent later. ' \
         
     | 
| 
      
 67 
     | 
    
         
            +
                          'If it is not caused by the peak load, decreasing the tracking interval is recommended.'
         
     | 
| 
      
 68 
     | 
    
         
            +
                        )
         
     | 
| 
      
 69 
     | 
    
         
            +
                        @tracking_visitors.add_all(builder.visitor_codes_to_keep)
         
     | 
| 
      
 70 
     | 
    
         
            +
                      end
         
     | 
| 
      
 71 
     | 
    
         
            +
                      perform_tracking_request(builder.visitor_codes_to_send, builder.unsent_visitor_data, builder.tracking_lines)
         
     | 
| 
      
 72 
     | 
    
         
            +
                    end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                    def perform_tracking_request(visitor_codes, visitor_data, tracking_lines)
         
     | 
| 
      
 75 
     | 
    
         
            +
                      return if visitor_data.empty?
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                      lines = tracking_lines.join(LINES_DELIMETER)
         
     | 
| 
      
 78 
     | 
    
         
            +
                      # mark unsent data as transmitted
         
     | 
| 
      
 79 
     | 
    
         
            +
                      visitor_data.each { |d| d.mark_as_transmitting if d.is_a?(Kameleoon::Data) }
         
     | 
| 
      
 80 
     | 
    
         
            +
                      Thread.new do
         
     | 
| 
      
 81 
     | 
    
         
            +
                        result = @network_manager.send_tracking_data(lines)
         
     | 
| 
      
 82 
     | 
    
         
            +
                        if result != false
         
     | 
| 
      
 83 
     | 
    
         
            +
                          Logging::KameleoonLogger.info('Successful request for tracking visitors: %s, data: %s',
         
     | 
| 
      
 84 
     | 
    
         
            +
                                                        visitor_codes, visitor_data)
         
     | 
| 
      
 85 
     | 
    
         
            +
                          visitor_data.each { |d| d.mark_as_sent if d.is_a?(Kameleoon::Data) }
         
     | 
| 
      
 86 
     | 
    
         
            +
                        else
         
     | 
| 
      
 87 
     | 
    
         
            +
                          Logging::KameleoonLogger.error('Tracking request failed')
         
     | 
| 
      
 88 
     | 
    
         
            +
                          Logging::KameleoonLogger.info('Failed request for tracking visitors: %s, data: %s',
         
     | 
| 
      
 89 
     | 
    
         
            +
                                                        visitor_codes, visitor_data)
         
     | 
| 
      
 90 
     | 
    
         
            +
                          visitor_data.each { |d| d.mark_as_unsent if d.is_a?(Kameleoon::Data) }
         
     | 
| 
      
 91 
     | 
    
         
            +
                          @tracking_visitors.add_all(visitor_codes)
         
     | 
| 
      
 92 
     | 
    
         
            +
                        end
         
     | 
| 
      
 93 
     | 
    
         
            +
                      end
         
     | 
| 
      
 94 
     | 
    
         
            +
                    end
         
     | 
| 
      
 95 
     | 
    
         
            +
                  end
         
     | 
| 
      
 96 
     | 
    
         
            +
                end
         
     | 
| 
      
 97 
     | 
    
         
            +
              end
         
     | 
| 
      
 98 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,94 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'set'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Kameleoon
         
     | 
| 
      
 6 
     | 
    
         
            +
              module Managers
         
     | 
| 
      
 7 
     | 
    
         
            +
                module Tracking
         
     | 
| 
      
 8 
     | 
    
         
            +
                  class LockVisitorTrackingRegistry
         
     | 
| 
      
 9 
     | 
    
         
            +
                    LIMITED_EXTRACTION_THRESHOLD_COEFFICIENT = 2
         
     | 
| 
      
 10 
     | 
    
         
            +
                    REMOVAL_FACTOR = 0.8
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                    def initialize(visitor_manager, storage_limit = 1_000_000, extraction_limit = 20_000)
         
     | 
| 
      
 13 
     | 
    
         
            +
                      @visitor_manager = visitor_manager
         
     | 
| 
      
 14 
     | 
    
         
            +
                      @storage_limit = storage_limit
         
     | 
| 
      
 15 
     | 
    
         
            +
                      @extraction_limit = extraction_limit
         
     | 
| 
      
 16 
     | 
    
         
            +
                      @visitors = Set.new
         
     | 
| 
      
 17 
     | 
    
         
            +
                      @mutex = Mutex.new
         
     | 
| 
      
 18 
     | 
    
         
            +
                    end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                    def add(visitor_code)
         
     | 
| 
      
 21 
     | 
    
         
            +
                      @mutex.synchronize { @visitors.add(visitor_code) }
         
     | 
| 
      
 22 
     | 
    
         
            +
                    end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                    def add_all(visitor_codes)
         
     | 
| 
      
 25 
     | 
    
         
            +
                      @mutex.synchronize do
         
     | 
| 
      
 26 
     | 
    
         
            +
                        @visitors.merge(visitor_codes)
         
     | 
| 
      
 27 
     | 
    
         
            +
                        if @visitors.size > @storage_limit
         
     | 
| 
      
 28 
     | 
    
         
            +
                          erase_nonexistent_visitors
         
     | 
| 
      
 29 
     | 
    
         
            +
                          erase_to_storage_limit
         
     | 
| 
      
 30 
     | 
    
         
            +
                        end
         
     | 
| 
      
 31 
     | 
    
         
            +
                      end
         
     | 
| 
      
 32 
     | 
    
         
            +
                    end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                    def extract
         
     | 
| 
      
 35 
     | 
    
         
            +
                      should_extract_all_be_used ? extract_all : extract_limited
         
     | 
| 
      
 36 
     | 
    
         
            +
                    end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                    private
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                    def should_extract_all_be_used
         
     | 
| 
      
 41 
     | 
    
         
            +
                      @visitors.size < @extraction_limit * LIMITED_EXTRACTION_THRESHOLD_COEFFICIENT
         
     | 
| 
      
 42 
     | 
    
         
            +
                    end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                    def extract_all
         
     | 
| 
      
 45 
     | 
    
         
            +
                      old_visitors = nil
         
     | 
| 
      
 46 
     | 
    
         
            +
                      new_visitors = Set.new
         
     | 
| 
      
 47 
     | 
    
         
            +
                      @mutex.synchronize do
         
     | 
| 
      
 48 
     | 
    
         
            +
                        old_visitors = @visitors
         
     | 
| 
      
 49 
     | 
    
         
            +
                        @visitors = new_visitors
         
     | 
| 
      
 50 
     | 
    
         
            +
                      end
         
     | 
| 
      
 51 
     | 
    
         
            +
                      old_visitors
         
     | 
| 
      
 52 
     | 
    
         
            +
                    end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                    def extract_limited
         
     | 
| 
      
 55 
     | 
    
         
            +
                      extracted = nil
         
     | 
| 
      
 56 
     | 
    
         
            +
                      @mutex.synchronize do
         
     | 
| 
      
 57 
     | 
    
         
            +
                        if should_extract_all_be_used
         
     | 
| 
      
 58 
     | 
    
         
            +
                          extracted = extract_all
         
     | 
| 
      
 59 
     | 
    
         
            +
                          next
         
     | 
| 
      
 60 
     | 
    
         
            +
                        end
         
     | 
| 
      
 61 
     | 
    
         
            +
                        i = 0
         
     | 
| 
      
 62 
     | 
    
         
            +
                        extracted = []
         
     | 
| 
      
 63 
     | 
    
         
            +
                        @visitors.each do |vc|
         
     | 
| 
      
 64 
     | 
    
         
            +
                          break if i >= @extraction_limit
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                          extracted.push(vc)
         
     | 
| 
      
 67 
     | 
    
         
            +
                          i += 1
         
     | 
| 
      
 68 
     | 
    
         
            +
                          @visitors.delete(vc)
         
     | 
| 
      
 69 
     | 
    
         
            +
                        end
         
     | 
| 
      
 70 
     | 
    
         
            +
                      end
         
     | 
| 
      
 71 
     | 
    
         
            +
                      extracted
         
     | 
| 
      
 72 
     | 
    
         
            +
                    end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                    # Not thread-safe
         
     | 
| 
      
 75 
     | 
    
         
            +
                    def erase_nonexistent_visitors
         
     | 
| 
      
 76 
     | 
    
         
            +
                      @visitors.delete_if { |vc| @visitor_manager.get_visitor(vc).nil? }
         
     | 
| 
      
 77 
     | 
    
         
            +
                    end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                    # Not thread-safe
         
     | 
| 
      
 80 
     | 
    
         
            +
                    def erase_to_storage_limit
         
     | 
| 
      
 81 
     | 
    
         
            +
                      visitors_to_remove_count = @visitors.size - (@storage_limit * REMOVAL_FACTOR).to_i
         
     | 
| 
      
 82 
     | 
    
         
            +
                      return if visitors_to_remove_count <= 0
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                      @visitors.each do |vc|
         
     | 
| 
      
 85 
     | 
    
         
            +
                        break if visitors_to_remove_count.zero?
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                        visitors_to_remove_count -= 1
         
     | 
| 
      
 88 
     | 
    
         
            +
                        @visitors.delete(vc)
         
     | 
| 
      
 89 
     | 
    
         
            +
                      end
         
     | 
| 
      
 90 
     | 
    
         
            +
                    end
         
     | 
| 
      
 91 
     | 
    
         
            +
                  end
         
     | 
| 
      
 92 
     | 
    
         
            +
                end
         
     | 
| 
      
 93 
     | 
    
         
            +
              end
         
     | 
| 
      
 94 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -1,6 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            require 'kameleoon/data/custom_data'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'kameleoon/logging/kameleoon_logger'
         
     | 
| 
       4 
5 
     | 
    
         
             
            require 'kameleoon/utils'
         
     | 
| 
       5 
6 
     | 
    
         | 
| 
       6 
7 
     | 
    
         
             
            module Kameleoon
         
     | 
| 
         @@ -9,24 +10,40 @@ module Kameleoon 
     | 
|
| 
       9 
10 
     | 
    
         
             
                  class WarehouseManager
         
     | 
| 
       10 
11 
     | 
    
         
             
                    WAREHOUSE_AUDIENCES_FIELD_NAME = 'warehouseAudiences'
         
     | 
| 
       11 
12 
     | 
    
         | 
| 
       12 
     | 
    
         
            -
                    def initialize(network_manager, visitor_manager 
     | 
| 
      
 13 
     | 
    
         
            +
                    def initialize(network_manager, visitor_manager)
         
     | 
| 
      
 14 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug('CALL: WarehouseManager.new(networkManager, visitorManager)')
         
     | 
| 
       13 
15 
     | 
    
         
             
                      @network_manager = network_manager
         
     | 
| 
       14 
16 
     | 
    
         
             
                      @visitor_manager = visitor_manager
         
     | 
| 
       15 
     | 
    
         
            -
                       
     | 
| 
      
 17 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug('RETURN: WarehouseManager.new(networkManager, visitorManager)')
         
     | 
| 
       16 
18 
     | 
    
         
             
                    end
         
     | 
| 
       17 
19 
     | 
    
         | 
| 
       18 
20 
     | 
    
         
             
                    def get_visitor_warehouse_audience(visitor_code, custom_data_index, warehouse_key = nil, timeout = nil)
         
     | 
| 
      
 21 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug(
         
     | 
| 
      
 22 
     | 
    
         
            +
                        "CALL: WarehouseManager.get_visitor_warehouse_audience(visitor_code: '%s', custom_data_index: %s," \
         
     | 
| 
      
 23 
     | 
    
         
            +
                          " warehouse_key: '%s', timeout: %s)", visitor_code, custom_data_index, warehouse_key, timeout
         
     | 
| 
      
 24 
     | 
    
         
            +
                      )
         
     | 
| 
       19 
25 
     | 
    
         
             
                      Utils::VisitorCode.validate(visitor_code)
         
     | 
| 
       20 
26 
     | 
    
         
             
                      remote_data_key = warehouse_key.nil? || warehouse_key.empty? ? visitor_code : warehouse_key
         
     | 
| 
       21 
27 
     | 
    
         
             
                      response = @network_manager.get_remote_data(remote_data_key, timeout)
         
     | 
| 
       22 
     | 
    
         
            -
                       
     | 
| 
      
 28 
     | 
    
         
            +
                      unless response.is_a?(String)
         
     | 
| 
      
 29 
     | 
    
         
            +
                        Logging::KameleoonLogger.debug(
         
     | 
| 
      
 30 
     | 
    
         
            +
                          "RETURN: WarehouseManager.get_visitor_warehouse_audience(visitor_code: '%s', custom_data_index: %s," \
         
     | 
| 
      
 31 
     | 
    
         
            +
                            " warehouse_key: '%s', timeout: %s) -> (custom_data: nil)",
         
     | 
| 
      
 32 
     | 
    
         
            +
                          visitor_code, custom_data_index, warehouse_key, timeout
         
     | 
| 
      
 33 
     | 
    
         
            +
                        )
         
     | 
| 
      
 34 
     | 
    
         
            +
                        return nil
         
     | 
| 
      
 35 
     | 
    
         
            +
                      end
         
     | 
| 
       23 
36 
     | 
    
         | 
| 
       24 
37 
     | 
    
         
             
                      remote_data = JSON.parse(response)
         
     | 
| 
       25 
38 
     | 
    
         
             
                      warehouse_audiences = remote_data.is_a?(Hash) ? remote_data[WAREHOUSE_AUDIENCES_FIELD_NAME] : nil
         
     | 
| 
       26 
39 
     | 
    
         
             
                      data_values = warehouse_audiences.is_a?(Hash) ? warehouse_audiences.keys : []
         
     | 
| 
       27 
40 
     | 
    
         
             
                      warehouse_audiences_data = CustomData.new(custom_data_index, *data_values)
         
     | 
| 
       28 
     | 
    
         
            -
                       
     | 
| 
       29 
     | 
    
         
            -
                       
     | 
| 
      
 41 
     | 
    
         
            +
                      @visitor_manager.add_data(visitor_code, warehouse_audiences_data)
         
     | 
| 
      
 42 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug(
         
     | 
| 
      
 43 
     | 
    
         
            +
                        "RETURN: WarehouseManager.get_visitor_warehouse_audience(visitor_code: '%s', custom_data_index: %s," \
         
     | 
| 
      
 44 
     | 
    
         
            +
                          " warehouse_key: '%s', timeout: %s) -> (custom_data: %s)",
         
     | 
| 
      
 45 
     | 
    
         
            +
                        visitor_code, custom_data_index, warehouse_key, timeout, warehouse_audiences_data
         
     | 
| 
      
 46 
     | 
    
         
            +
                      )
         
     | 
| 
       30 
47 
     | 
    
         
             
                      warehouse_audiences_data
         
     | 
| 
       31 
48 
     | 
    
         
             
                    end
         
     | 
| 
       32 
49 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -3,6 +3,8 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            require 'json'
         
     | 
| 
       4 
4 
     | 
    
         
             
            require 'net/http'
         
     | 
| 
       5 
5 
     | 
    
         
             
            require 'time'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'kameleoon/utils'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require 'kameleoon/logging/kameleoon_logger'
         
     | 
| 
       6 
8 
     | 
    
         | 
| 
       7 
9 
     | 
    
         
             
            module Kameleoon
         
     | 
| 
       8 
10 
     | 
    
         
             
              module Network
         
     | 
| 
         @@ -12,25 +14,38 @@ module Kameleoon 
     | 
|
| 
       12 
14 
     | 
    
         
             
                  JWT_ACCESS_TOKEN_FIELD = 'access_token'
         
     | 
| 
       13 
15 
     | 
    
         
             
                  JWT_EXPIRES_IN_FIELD = 'expires_in'
         
     | 
| 
       14 
16 
     | 
    
         | 
| 
       15 
     | 
    
         
            -
                  def initialize(network_manager, client_id, client_secret 
     | 
| 
      
 17 
     | 
    
         
            +
                  def initialize(network_manager, client_id, client_secret)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    Logging::KameleoonLogger.debug(lambda {
         
     | 
| 
      
 19 
     | 
    
         
            +
                      format("CALL: AccessTokenSource.new(network_manager, client_id: '%s', client_secret: '%s')",
         
     | 
| 
      
 20 
     | 
    
         
            +
                             Utils::Strval.secret(client_id), Utils::Strval.secret(client_secret))
         
     | 
| 
      
 21 
     | 
    
         
            +
                    })
         
     | 
| 
       16 
22 
     | 
    
         
             
                    @network_manager = network_manager
         
     | 
| 
       17 
23 
     | 
    
         
             
                    @client_id = client_id
         
     | 
| 
       18 
24 
     | 
    
         
             
                    @client_secret = client_secret
         
     | 
| 
       19 
25 
     | 
    
         
             
                    @fetching = false
         
     | 
| 
       20 
     | 
    
         
            -
                     
     | 
| 
      
 26 
     | 
    
         
            +
                    Logging::KameleoonLogger.debug(lambda {
         
     | 
| 
      
 27 
     | 
    
         
            +
                      format("RETURN: AccessTokenSource.new(network_manager, client_id: '%s', client_secret: '%s')",
         
     | 
| 
      
 28 
     | 
    
         
            +
                             Utils::Strval.secret(client_id), Utils::Strval.secret(client_secret))
         
     | 
| 
      
 29 
     | 
    
         
            +
                    })
         
     | 
| 
       21 
30 
     | 
    
         
             
                  end
         
     | 
| 
       22 
31 
     | 
    
         | 
| 
       23 
32 
     | 
    
         
             
                  def get_token(timeout = nil)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    Logging::KameleoonLogger.debug('CALL: AccessTokenSource.getToken(timeout: %s)', timeout)
         
     | 
| 
       24 
34 
     | 
    
         
             
                    now = Time.new.to_i
         
     | 
| 
       25 
35 
     | 
    
         
             
                    token = @cached_token
         
     | 
| 
       26 
36 
     | 
    
         
             
                    return call_fetch_token(timeout) if token.nil? || token.expired?(now)
         
     | 
| 
       27 
37 
     | 
    
         | 
| 
       28 
38 
     | 
    
         
             
                    run_fetch_token if !@fetching && token.obsolete?(now)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    Logging::KameleoonLogger.debug("RETURN: AccessTokenSource.getToken(timeout: %s) -> (token: '%s')",
         
     | 
| 
      
 40 
     | 
    
         
            +
                                                   timeout, token.value)
         
     | 
| 
       29 
41 
     | 
    
         
             
                    token.value
         
     | 
| 
       30 
42 
     | 
    
         
             
                  end
         
     | 
| 
       31 
43 
     | 
    
         | 
| 
       32 
44 
     | 
    
         
             
                  def discard_token(token)
         
     | 
| 
      
 45 
     | 
    
         
            +
                    Logging::KameleoonLogger.debug("CALL: AccessTokenSource.discard_token(token: '%s')", token)
         
     | 
| 
       33 
46 
     | 
    
         
             
                    @cached_token = nil if @cached_token&.value == token
         
     | 
| 
      
 47 
     | 
    
         
            +
                    Logging::KameleoonLogger.debug("RETURN: AccessTokenSource.discard_token(token: '%s')", token)
         
     | 
| 
      
 48 
     | 
    
         
            +
                    @cached_token
         
     | 
| 
       34 
49 
     | 
    
         
             
                  end
         
     | 
| 
       35 
50 
     | 
    
         | 
| 
       36 
51 
     | 
    
         
             
                  private
         
     | 
| 
         @@ -40,7 +55,7 @@ module Kameleoon 
     | 
|
| 
       40 
55 
     | 
    
         
             
                    fetch_token(timeout)
         
     | 
| 
       41 
56 
     | 
    
         
             
                  rescue StandardError => e
         
     | 
| 
       42 
57 
     | 
    
         
             
                    @fetching = false
         
     | 
| 
       43 
     | 
    
         
            -
                     
     | 
| 
      
 58 
     | 
    
         
            +
                    Logging::KameleoonLogger.error("Failed to call access token fetching: #{e}")
         
     | 
| 
       44 
59 
     | 
    
         
             
                    nil
         
     | 
| 
       45 
60 
     | 
    
         
             
                  end
         
     | 
| 
       46 
61 
     | 
    
         | 
| 
         @@ -50,13 +65,14 @@ module Kameleoon 
     | 
|
| 
       50 
65 
     | 
    
         
             
                    Thread.new { fetch_token }
         
     | 
| 
       51 
66 
     | 
    
         
             
                  rescue StandardError => e
         
     | 
| 
       52 
67 
     | 
    
         
             
                    @fetching = false
         
     | 
| 
       53 
     | 
    
         
            -
                     
     | 
| 
      
 68 
     | 
    
         
            +
                    Logging::KameleoonLogger.error("Failed to run access token fetching: #{e}")
         
     | 
| 
       54 
69 
     | 
    
         
             
                  end
         
     | 
| 
       55 
70 
     | 
    
         | 
| 
       56 
71 
     | 
    
         
             
                  def fetch_token(timeout = nil)
         
     | 
| 
      
 72 
     | 
    
         
            +
                    Logging::KameleoonLogger.debug('CALL: AccessTokenSource.fetch_token(timeout: %s)', timeout)
         
     | 
| 
       57 
73 
     | 
    
         
             
                    response_content = @network_manager.fetch_access_jwtoken(@client_id, @client_secret, timeout)
         
     | 
| 
       58 
74 
     | 
    
         
             
                    unless response_content
         
     | 
| 
       59 
     | 
    
         
            -
                       
     | 
| 
      
 75 
     | 
    
         
            +
                      Logging::KameleoonLogger.error('Failed to fetch access JWT')
         
     | 
| 
       60 
76 
     | 
    
         
             
                      return nil
         
     | 
| 
       61 
77 
     | 
    
         
             
                    end
         
     | 
| 
       62 
78 
     | 
    
         
             
                    begin
         
     | 
| 
         @@ -64,14 +80,18 @@ module Kameleoon 
     | 
|
| 
       64 
80 
     | 
    
         
             
                      token = jwt[JWT_ACCESS_TOKEN_FIELD]
         
     | 
| 
       65 
81 
     | 
    
         
             
                      expires_in = jwt[JWT_EXPIRES_IN_FIELD]
         
     | 
| 
       66 
82 
     | 
    
         
             
                    rescue JSON::ParserError => e
         
     | 
| 
       67 
     | 
    
         
            -
                       
     | 
| 
      
 83 
     | 
    
         
            +
                      Logging::KameleoonLogger.error("Failed to parse access JWT: #{e}")
         
     | 
| 
       68 
84 
     | 
    
         
             
                      return nil
         
     | 
| 
       69 
85 
     | 
    
         
             
                    end
         
     | 
| 
       70 
86 
     | 
    
         
             
                    unless token.is_a?(String) && !token.empty? && expires_in.is_a?(Integer) && expires_in.positive?
         
     | 
| 
       71 
     | 
    
         
            -
                       
     | 
| 
      
 87 
     | 
    
         
            +
                      Logging::KameleoonLogger.error('Failed to read access JWT')
         
     | 
| 
       72 
88 
     | 
    
         
             
                      return nil
         
     | 
| 
       73 
89 
     | 
    
         
             
                    end
         
     | 
| 
       74 
     | 
    
         
            -
                    handle_fetched_token(token, expires_in)
         
     | 
| 
      
 90 
     | 
    
         
            +
                    token = handle_fetched_token(token, expires_in)
         
     | 
| 
      
 91 
     | 
    
         
            +
                    Logging::KameleoonLogger.debug(
         
     | 
| 
      
 92 
     | 
    
         
            +
                      "RETURN: AccessTokenSource.fetch_token(timeout: %s) -> (token: '%s')", timeout, token
         
     | 
| 
      
 93 
     | 
    
         
            +
                    )
         
     | 
| 
      
 94 
     | 
    
         
            +
                    token
         
     | 
| 
       75 
95 
     | 
    
         
             
                  ensure
         
     | 
| 
       76 
96 
     | 
    
         
             
                    @fetching = false
         
     | 
| 
       77 
97 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -83,9 +103,14 @@ module Kameleoon 
     | 
|
| 
       83 
103 
     | 
    
         
             
                      obs_time = now + expires_in - TOKEN_OBSOLESCENCE_GAP
         
     | 
| 
       84 
104 
     | 
    
         
             
                    else
         
     | 
| 
       85 
105 
     | 
    
         
             
                      obs_time = exp_time
         
     | 
| 
       86 
     | 
    
         
            -
                       
     | 
| 
       87 
     | 
    
         
            -
                         
     | 
| 
       88 
     | 
    
         
            -
             
     | 
| 
      
 106 
     | 
    
         
            +
                      if expires_in <= TOKEN_EXPIRATION_GAP
         
     | 
| 
      
 107 
     | 
    
         
            +
                        Logging::KameleoonLogger.error(
         
     | 
| 
      
 108 
     | 
    
         
            +
                          'Access token life time (%ss) is not long enough to cache the token', expires_in
         
     | 
| 
      
 109 
     | 
    
         
            +
                        )
         
     | 
| 
      
 110 
     | 
    
         
            +
                      else
         
     | 
| 
      
 111 
     | 
    
         
            +
                        Logging::KameleoonLogger.warning(
         
     | 
| 
      
 112 
     | 
    
         
            +
                          'Access token life time (%ss) is not long enough to refresh cached token in background', expires_in
         
     | 
| 
      
 113 
     | 
    
         
            +
                        )
         
     | 
| 
       89 
114 
     | 
    
         
             
                      end
         
     | 
| 
       90 
115 
     | 
    
         
             
                    end
         
     | 
| 
       91 
116 
     | 
    
         
             
                    @cached_token = ExpiringToken.new(token, exp_time, obs_time)
         
     | 
| 
         @@ -112,14 +137,21 @@ module Kameleoon 
     | 
|
| 
       112 
137 
     | 
    
         
             
                end
         
     | 
| 
       113 
138 
     | 
    
         | 
| 
       114 
139 
     | 
    
         
             
                class AccessTokenSourceFactory
         
     | 
| 
       115 
     | 
    
         
            -
                  def initialize(client_id, client_secret 
     | 
| 
      
 140 
     | 
    
         
            +
                  def initialize(client_id, client_secret)
         
     | 
| 
      
 141 
     | 
    
         
            +
                    Logging::KameleoonLogger.debug(lambda {
         
     | 
| 
      
 142 
     | 
    
         
            +
                      format("CALL: AccessTokenSourceFactory.new(client_id: '%s', client_secret: '%s')",
         
     | 
| 
      
 143 
     | 
    
         
            +
                             Utils::Strval.secret(client_id), Utils::Strval.secret(client_secret))
         
     | 
| 
      
 144 
     | 
    
         
            +
                    })
         
     | 
| 
       116 
145 
     | 
    
         
             
                    @client_id = client_id
         
     | 
| 
       117 
146 
     | 
    
         
             
                    @client_secret = client_secret
         
     | 
| 
       118 
     | 
    
         
            -
                     
     | 
| 
      
 147 
     | 
    
         
            +
                    Logging::KameleoonLogger.debug(lambda {
         
     | 
| 
      
 148 
     | 
    
         
            +
                      format("RETURN: AccessTokenSourceFactory.new(client_id: '%s', client_secret: '%s')",
         
     | 
| 
      
 149 
     | 
    
         
            +
                             Utils::Strval.secret(client_id), Utils::Strval.secret(client_secret))
         
     | 
| 
      
 150 
     | 
    
         
            +
                    })
         
     | 
| 
       119 
151 
     | 
    
         
             
                  end
         
     | 
| 
       120 
152 
     | 
    
         | 
| 
       121 
153 
     | 
    
         
             
                  def create(network_manager)
         
     | 
| 
       122 
     | 
    
         
            -
                    AccessTokenSource.new(network_manager, @client_id, @client_secret 
     | 
| 
      
 154 
     | 
    
         
            +
                    AccessTokenSource.new(network_manager, @client_id, @client_secret)
         
     | 
| 
       123 
155 
     | 
    
         
             
                  end
         
     | 
| 
       124 
156 
     | 
    
         
             
                end
         
     | 
| 
       125 
157 
     | 
    
         
             
              end
         
     | 
| 
         @@ -1,6 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            require 'kameleoon/utils'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'kameleoon/logging/kameleoon_logger'
         
     | 
| 
       4 
5 
     | 
    
         | 
| 
       5 
6 
     | 
    
         
             
            module Kameleoon
         
     | 
| 
       6 
7 
     | 
    
         
             
              module Network
         
     | 
| 
         @@ -10,49 +11,76 @@ module Kameleoon 
     | 
|
| 
       10 
11 
     | 
    
         
             
                  COOKIE_TTL_SECONDS = 380 * 86_400 # 380 days in seconds
         
     | 
| 
       11 
12 
     | 
    
         | 
| 
       12 
13 
     | 
    
         
             
                  class CookieManager
         
     | 
| 
       13 
     | 
    
         
            -
                     
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
                      @consent_required = false
         
     | 
| 
      
 14 
     | 
    
         
            +
                    def initialize(data_manager, top_level_domain)
         
     | 
| 
      
 15 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug("CALL: CookieManager.new(top_level_domain: '%s')", top_level_domain)
         
     | 
| 
      
 16 
     | 
    
         
            +
                      @data_manager = data_manager
         
     | 
| 
       17 
17 
     | 
    
         
             
                      @top_level_domain = top_level_domain
         
     | 
| 
      
 18 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug("RETURN: CookieManager.new(top_level_domain: '%s')", top_level_domain)
         
     | 
| 
       18 
19 
     | 
    
         
             
                    end
         
     | 
| 
       19 
20 
     | 
    
         | 
| 
       20 
21 
     | 
    
         
             
                    def get_or_add(cookies, default_visitor_code = nil)
         
     | 
| 
       21 
22 
     | 
    
         
             
                      return if cookies.nil?
         
     | 
| 
       22 
23 
     | 
    
         | 
| 
      
 24 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug(
         
     | 
| 
      
 25 
     | 
    
         
            +
                        "CALL: CookieManager.get_or_add(cookies: %s, default_visitor_code: '%s')",
         
     | 
| 
      
 26 
     | 
    
         
            +
                        cookies, default_visitor_code
         
     | 
| 
      
 27 
     | 
    
         
            +
                      )
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
       23 
29 
     | 
    
         
             
                      visitor_code = get_visitor_code_from_cookies(cookies)
         
     | 
| 
       24 
30 
     | 
    
         
             
                      unless visitor_code.nil?
         
     | 
| 
       25 
31 
     | 
    
         
             
                        Utils::VisitorCode.validate(visitor_code)
         
     | 
| 
      
 32 
     | 
    
         
            +
                        Logging::KameleoonLogger.debug("Read visitor code '%s' from cookies %s", visitor_code, cookies)
         
     | 
| 
       26 
33 
     | 
    
         
             
                        # Remove adding cookies when we will be sure that it doesn't break anything
         
     | 
| 
       27 
     | 
    
         
            -
                        add(visitor_code, cookies) unless @ 
     | 
| 
      
 34 
     | 
    
         
            +
                        add(visitor_code, cookies) unless @data_manager.visitor_code_managed?
         
     | 
| 
      
 35 
     | 
    
         
            +
                        Logging::KameleoonLogger.debug(
         
     | 
| 
      
 36 
     | 
    
         
            +
                          "RETURN: CookieManager.get_or_add(cookies: %s, default_visitor_code: '%s') -> (visitor_code: '%s')",
         
     | 
| 
      
 37 
     | 
    
         
            +
                          cookies, default_visitor_code, visitor_code
         
     | 
| 
      
 38 
     | 
    
         
            +
                        )
         
     | 
| 
       28 
39 
     | 
    
         
             
                        return visitor_code
         
     | 
| 
       29 
40 
     | 
    
         
             
                      end
         
     | 
| 
       30 
41 
     | 
    
         | 
| 
       31 
42 
     | 
    
         
             
                      if default_visitor_code.nil?
         
     | 
| 
       32 
43 
     | 
    
         
             
                        visitor_code = Utils::VisitorCode.generate
         
     | 
| 
       33 
     | 
    
         
            -
                         
     | 
| 
      
 44 
     | 
    
         
            +
                        Logging::KameleoonLogger.debug("Generated new visitor code '%s'", visitor_code)
         
     | 
| 
      
 45 
     | 
    
         
            +
                        add(visitor_code, cookies) unless @data_manager.visitor_code_managed?
         
     | 
| 
      
 46 
     | 
    
         
            +
                        Logging::KameleoonLogger.debug(
         
     | 
| 
      
 47 
     | 
    
         
            +
                          "RETURN: CookieManager.get_or_add(cookies: %s, default_visitor_code: '%s') -> (visitor_code: '%s')",
         
     | 
| 
      
 48 
     | 
    
         
            +
                          cookies, default_visitor_code, visitor_code
         
     | 
| 
      
 49 
     | 
    
         
            +
                        )
         
     | 
| 
       34 
50 
     | 
    
         
             
                        return visitor_code
         
     | 
| 
       35 
51 
     | 
    
         
             
                      end
         
     | 
| 
       36 
52 
     | 
    
         | 
| 
       37 
53 
     | 
    
         
             
                      visitor_code = default_visitor_code
         
     | 
| 
       38 
54 
     | 
    
         
             
                      Utils::VisitorCode.validate(visitor_code)
         
     | 
| 
      
 55 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug("Used default visitor code '{%s}'", default_visitor_code)
         
     | 
| 
       39 
56 
     | 
    
         
             
                      add(visitor_code, cookies)
         
     | 
| 
      
 57 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug(
         
     | 
| 
      
 58 
     | 
    
         
            +
                        "RETURN: CookieManager.get_or_add(cookies: %s, default_visitor_code: '%s') -> (visitor_code: '%s')",
         
     | 
| 
      
 59 
     | 
    
         
            +
                        cookies, default_visitor_code, visitor_code
         
     | 
| 
      
 60 
     | 
    
         
            +
                      )
         
     | 
| 
       40 
61 
     | 
    
         
             
                      visitor_code
         
     | 
| 
       41 
62 
     | 
    
         
             
                    end
         
     | 
| 
       42 
63 
     | 
    
         | 
| 
       43 
64 
     | 
    
         
             
                    def update(visitor_code, consent, cookies)
         
     | 
| 
       44 
65 
     | 
    
         
             
                      return if cookies.nil?
         
     | 
| 
       45 
66 
     | 
    
         | 
| 
      
 67 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug("CALL: CookieManager.update(visitor_code: '%s', consent: %s, cookies: %s)",
         
     | 
| 
      
 68 
     | 
    
         
            +
                                                     visitor_code, consent, cookies)
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
       46 
70 
     | 
    
         
             
                      if consent
         
     | 
| 
       47 
71 
     | 
    
         
             
                        add(visitor_code, cookies)
         
     | 
| 
       48 
72 
     | 
    
         
             
                      else
         
     | 
| 
       49 
73 
     | 
    
         
             
                        remove(cookies)
         
     | 
| 
       50 
74 
     | 
    
         
             
                      end
         
     | 
| 
      
 75 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug("RETURN: CookieManager.update(visitor_code: '%s', consent: %s, cookies: %s)",
         
     | 
| 
      
 76 
     | 
    
         
            +
                                                     visitor_code, consent, cookies)
         
     | 
| 
       51 
77 
     | 
    
         
             
                    end
         
     | 
| 
       52 
78 
     | 
    
         | 
| 
       53 
79 
     | 
    
         
             
                    private
         
     | 
| 
       54 
80 
     | 
    
         | 
| 
       55 
81 
     | 
    
         
             
                    def add(visitor_code, cookies)
         
     | 
| 
      
 82 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug("CALL: CookieManager.add(visitor_code: '%s', cookies: %s)",
         
     | 
| 
      
 83 
     | 
    
         
            +
                                                     visitor_code, cookies)
         
     | 
| 
       56 
84 
     | 
    
         
             
                      cookie = {
         
     | 
| 
       57 
85 
     | 
    
         
             
                        value: visitor_code,
         
     | 
| 
       58 
86 
     | 
    
         
             
                        expires: Time.now + COOKIE_TTL_SECONDS,
         
     | 
| 
         @@ -60,13 +88,20 @@ module Kameleoon 
     | 
|
| 
       60 
88 
     | 
    
         
             
                        domain: @top_level_domain
         
     | 
| 
       61 
89 
     | 
    
         
             
                      }
         
     | 
| 
       62 
90 
     | 
    
         
             
                      cookies[VISITOR_CODE_COOKIE] = cookie
         
     | 
| 
      
 91 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug("RETURN: CookieManager.add(visitor_code: '%s', cookies: %s)",
         
     | 
| 
      
 92 
     | 
    
         
            +
                                                     visitor_code, cookies)
         
     | 
| 
      
 93 
     | 
    
         
            +
                      cookies
         
     | 
| 
       63 
94 
     | 
    
         
             
                    end
         
     | 
| 
       64 
95 
     | 
    
         | 
| 
       65 
96 
     | 
    
         
             
                    def remove(cookies)
         
     | 
| 
       66 
     | 
    
         
            -
                      cookies 
     | 
| 
      
 97 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug('CALL: CookieManager.remove(cookies: %s)', cookies)
         
     | 
| 
      
 98 
     | 
    
         
            +
                      cookies[VISITOR_CODE_COOKIE] = nil if @data_manager.visitor_code_managed?
         
     | 
| 
      
 99 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug('RETURN: CookieManager.remove(cookies: %s)', cookies)
         
     | 
| 
      
 100 
     | 
    
         
            +
                      cookies
         
     | 
| 
       67 
101 
     | 
    
         
             
                    end
         
     | 
| 
       68 
102 
     | 
    
         | 
| 
       69 
103 
     | 
    
         
             
                    def get_visitor_code_from_cookies(cookies)
         
     | 
| 
      
 104 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug('CALL: CookieManager.get_visitor_code_from_cookies(cookies: %s)', cookies)
         
     | 
| 
       70 
105 
     | 
    
         
             
                      cookie = cookies[VISITOR_CODE_COOKIE]
         
     | 
| 
       71 
106 
     | 
    
         
             
                      case cookie
         
     | 
| 
       72 
107 
     | 
    
         
             
                      when String
         
     | 
| 
         @@ -76,6 +111,10 @@ module Kameleoon 
     | 
|
| 
       76 
111 
     | 
    
         
             
                      end
         
     | 
| 
       77 
112 
     | 
    
         
             
                      visitor_code = visitor_code[COOKIE_KEY_JS.size..] if visitor_code&.start_with?(COOKIE_KEY_JS)
         
     | 
| 
       78 
113 
     | 
    
         
             
                      visitor_code = nil if visitor_code&.empty?
         
     | 
| 
      
 114 
     | 
    
         
            +
                      Logging::KameleoonLogger.debug(
         
     | 
| 
      
 115 
     | 
    
         
            +
                        "RETURN: CookieManager.get_visitor_code_from_cookies(cookies: %s) -> (visitor_code: '%s')",
         
     | 
| 
      
 116 
     | 
    
         
            +
                        cookies, visitor_code
         
     | 
| 
      
 117 
     | 
    
         
            +
                      )
         
     | 
| 
       79 
118 
     | 
    
         
             
                      visitor_code
         
     | 
| 
       80 
119 
     | 
    
         
             
                    end
         
     | 
| 
       81 
120 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -8,7 +8,7 @@ require 'kameleoon/exceptions' 
     | 
|
| 
       8 
8 
     | 
    
         
             
            module Kameleoon
         
     | 
| 
       9 
9 
     | 
    
         
             
              module Network
         
     | 
| 
       10 
10 
     | 
    
         
             
                class NetProvider
         
     | 
| 
       11 
     | 
    
         
            -
                  def make_request( 
     | 
| 
      
 11 
     | 
    
         
            +
                  def make_request(_request)
         
     | 
| 
       12 
12 
     | 
    
         
             
                    raise KameleoonError, 'Call of not implemented method!'
         
     | 
| 
       13 
13 
     | 
    
         
             
                  end
         
     | 
| 
       14 
14 
     | 
    
         | 
| 
         @@ -18,7 +18,6 @@ module Kameleoon 
     | 
|
| 
       18 
18 
     | 
    
         
             
                    headers = { 'Content-Type' => request.content_type }
         
     | 
| 
       19 
19 
     | 
    
         
             
                    headers.merge!(request.extra_headers) unless request.extra_headers.nil?
         
     | 
| 
       20 
20 
     | 
    
         
             
                    headers['Authorization'] = "Bearer #{request.access_token}" unless request.access_token.nil?
         
     | 
| 
       21 
     | 
    
         
            -
                    headers['User-Agent'] = request.user_agent unless request.user_agent.nil?
         
     | 
| 
       22 
21 
     | 
    
         
             
                    headers
         
     | 
| 
       23 
22 
     | 
    
         
             
                  end
         
     | 
| 
       24 
23 
     | 
    
         | 
| 
         @@ -50,7 +49,7 @@ module Kameleoon 
     | 
|
| 
       50 
49 
     | 
    
         
             
                    body = resp.body
         
     | 
| 
       51 
50 
     | 
    
         
             
                    body = nil if body&.empty?
         
     | 
| 
       52 
51 
     | 
    
         
             
                    Response.new(nil, resp.code.to_i, body, request)
         
     | 
| 
       53 
     | 
    
         
            -
                  rescue => e
         
     | 
| 
      
 52 
     | 
    
         
            +
                  rescue StandardError => e
         
     | 
| 
       54 
53 
     | 
    
         
             
                    Response.new(e, nil, nil, request)
         
     | 
| 
       55 
54 
     | 
    
         
             
                  end
         
     | 
| 
       56 
55 
     | 
    
         
             
                end
         
     | 
| 
         @@ -1,5 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            require 'kameleoon/logging/kameleoon_logger'
         
     | 
| 
       3 
4 
     | 
    
         
             
            require 'kameleoon/network/content_type'
         
     | 
| 
       4 
5 
     | 
    
         
             
            require 'kameleoon/network/method'
         
     | 
| 
       5 
6 
     | 
    
         
             
            require 'kameleoon/network/request'
         
     | 
| 
         @@ -21,13 +22,12 @@ module Kameleoon 
     | 
|
| 
       21 
22 
     | 
    
         | 
| 
       22 
23 
     | 
    
         
             
                  attr_reader :environment, :default_timeout, :access_token_source, :url_provider
         
     | 
| 
       23 
24 
     | 
    
         | 
| 
       24 
     | 
    
         
            -
                  def initialize(environment, default_timeout, access_token_source_factory, url_provider 
     | 
| 
      
 25 
     | 
    
         
            +
                  def initialize(environment, default_timeout, access_token_source_factory, url_provider)
         
     | 
| 
       25 
26 
     | 
    
         
             
                    @environment = environment
         
     | 
| 
       26 
27 
     | 
    
         
             
                    @default_timeout = default_timeout
         
     | 
| 
       27 
28 
     | 
    
         
             
                    @access_token_source = access_token_source_factory.create(self)
         
     | 
| 
       28 
29 
     | 
    
         
             
                    @url_provider = url_provider
         
     | 
| 
       29 
30 
     | 
    
         
             
                    @sync_net_provider = SyncNetProvider.new
         
     | 
| 
       30 
     | 
    
         
            -
                    @log_func = log_func
         
     | 
| 
       31 
31 
     | 
    
         
             
                  end
         
     | 
| 
       32 
32 
     | 
    
         | 
| 
       33 
33 
     | 
    
         
             
                  def fetch_configuration(timestamp = nil, timeout = nil)
         
     | 
| 
         @@ -52,17 +52,13 @@ module Kameleoon 
     | 
|
| 
       52 
52 
     | 
    
         
             
                    make_call(request, true)
         
     | 
| 
       53 
53 
     | 
    
         
             
                  end
         
     | 
| 
       54 
54 
     | 
    
         | 
| 
       55 
     | 
    
         
            -
                  def send_tracking_data( 
     | 
| 
      
 55 
     | 
    
         
            +
                  def send_tracking_data(lines, timeout = nil)
         
     | 
| 
       56 
56 
     | 
    
         
             
                    return if lines.nil? || lines.empty?
         
     | 
| 
       57 
57 
     | 
    
         | 
| 
       58 
     | 
    
         
            -
                    url = @url_provider.make_tracking_url 
     | 
| 
      
 58 
     | 
    
         
            +
                    url = @url_provider.make_tracking_url
         
     | 
| 
       59 
59 
     | 
    
         
             
                    timeout = ensure_timeout(timeout)
         
     | 
| 
       60 
     | 
    
         
            -
                     
     | 
| 
       61 
     | 
    
         
            -
                    request 
     | 
| 
       62 
     | 
    
         
            -
                    Thread.new do
         
     | 
| 
       63 
     | 
    
         
            -
                      result = make_call(request, true, TRACKING_CALL_ATTEMPT_NUMBER - 1, TRACKING_CALL_RETRY_DELAY)
         
     | 
| 
       64 
     | 
    
         
            -
                      lines.each(&:mark_as_sent) if result != false
         
     | 
| 
       65 
     | 
    
         
            -
                    end
         
     | 
| 
      
 60 
     | 
    
         
            +
                    request = Request.new(Method::POST, url, ContentType::TEXT, timeout, data: lines)
         
     | 
| 
      
 61 
     | 
    
         
            +
                    make_call(request, true, TRACKING_CALL_ATTEMPT_NUMBER - 1, TRACKING_CALL_RETRY_DELAY)
         
     | 
| 
       66 
62 
     | 
    
         
             
                  end
         
     | 
| 
       67 
63 
     | 
    
         | 
| 
       68 
64 
     | 
    
         
             
                  def fetch_access_jwtoken(client_id, client_secret, timeout = nil)
         
     | 
| 
         @@ -81,6 +77,8 @@ module Kameleoon 
     | 
|
| 
       81 
77 
     | 
    
         
             
                  private
         
     | 
| 
       82 
78 
     | 
    
         | 
| 
       83 
79 
     | 
    
         
             
                  def make_call(request, try_access_token_auth, retry_limit = 0, retry_delay = 0)
         
     | 
| 
      
 80 
     | 
    
         
            +
                    Logging::KameleoonLogger.debug('Running request %s with access token %s, retry limit %s, retry delay %s ms',
         
     | 
| 
      
 81 
     | 
    
         
            +
                                                   request, try_access_token_auth, retry_limit, retry_delay)
         
     | 
| 
       84 
82 
     | 
    
         
             
                    attempt = 0
         
     | 
| 
       85 
83 
     | 
    
         
             
                    success = false
         
     | 
| 
       86 
84 
     | 
    
         
             
                    while !success && (attempt <= retry_limit)
         
     | 
| 
         @@ -90,33 +88,31 @@ module Kameleoon 
     | 
|
| 
       90 
88 
     | 
    
         
             
                      if response.success?
         
     | 
| 
       91 
89 
     | 
    
         
             
                        success = true
         
     | 
| 
       92 
90 
     | 
    
         
             
                      elsif !response.error.nil?
         
     | 
| 
       93 
     | 
    
         
            -
                         
     | 
| 
      
 91 
     | 
    
         
            +
                        log_level = attempt < retry_limit ? Logging::LogLevel::WARNING : Logging::LogLevel::ERROR
         
     | 
| 
      
 92 
     | 
    
         
            +
                        Logging::KameleoonLogger.log(log_level, "%s call '%s' failed: Error occurred during request: %s",
         
     | 
| 
      
 93 
     | 
    
         
            +
                                                     request.method, request.url, response.error)
         
     | 
| 
       94 
94 
     | 
    
         
             
                      else
         
     | 
| 
       95 
     | 
    
         
            -
                         
     | 
| 
      
 95 
     | 
    
         
            +
                        log_level = attempt < retry_limit ? Logging::LogLevel::WARNING : Logging::LogLevel::ERROR
         
     | 
| 
      
 96 
     | 
    
         
            +
                        Logging::KameleoonLogger.log(log_level, "%s call '%s' failed: Received unexpected status code '%s'",
         
     | 
| 
      
 97 
     | 
    
         
            +
                                                     request.method, request.url, response.code)
         
     | 
| 
       96 
98 
     | 
    
         
             
                        if response.code == 401 && request.access_token
         
     | 
| 
       97 
     | 
    
         
            -
                           
     | 
| 
      
 99 
     | 
    
         
            +
                          Logging::KameleoonLogger.warning("Unexpected rejection of access token '#{request.access_token}'")
         
     | 
| 
       98 
100 
     | 
    
         
             
                          @access_token_source.discard_token(request.access_token)
         
     | 
| 
       99 
101 
     | 
    
         
             
                          if attempt == retry_limit
         
     | 
| 
       100 
102 
     | 
    
         
             
                            try_access_token_auth = false
         
     | 
| 
       101 
103 
     | 
    
         
             
                            retry_delay = 0
         
     | 
| 
       102 
104 
     | 
    
         
             
                            request.authorize(nil)
         
     | 
| 
       103 
105 
     | 
    
         
             
                            attempt -= 1
         
     | 
| 
      
 106 
     | 
    
         
            +
                            Logging::KameleoonLogger.error("Wrong Kameleoon API access token slows down the SDK's requests")
         
     | 
| 
       104 
107 
     | 
    
         
             
                          end
         
     | 
| 
       105 
108 
     | 
    
         
             
                        end
         
     | 
| 
       106 
109 
     | 
    
         
             
                      end
         
     | 
| 
       107 
110 
     | 
    
         
             
                      attempt += 1
         
     | 
| 
       108 
111 
     | 
    
         
             
                    end
         
     | 
| 
      
 112 
     | 
    
         
            +
                    Logging::KameleoonLogger.debug('Fetched response %s for request %s', response, request)
         
     | 
| 
       109 
113 
     | 
    
         
             
                    success ? response.body : false
         
     | 
| 
       110 
114 
     | 
    
         
             
                  end
         
     | 
| 
       111 
115 
     | 
    
         | 
| 
       112 
     | 
    
         
            -
                  def log_failure(request, message)
         
     | 
| 
       113 
     | 
    
         
            -
                    return if @log_func.nil?
         
     | 
| 
       114 
     | 
    
         
            -
             
     | 
| 
       115 
     | 
    
         
            -
                    premsg = "#{request.method} call '#{request.url}' "
         
     | 
| 
       116 
     | 
    
         
            -
                    premsg += request.data.nil? ? 'failed' : "(data '#{request.data}') failed"
         
     | 
| 
       117 
     | 
    
         
            -
                    @log_func.call("#{premsg}: #{message}")
         
     | 
| 
       118 
     | 
    
         
            -
                  end
         
     | 
| 
       119 
     | 
    
         
            -
             
     | 
| 
       120 
116 
     | 
    
         
             
                  def delay(period)
         
     | 
| 
       121 
117 
     | 
    
         
             
                    sleep(period)
         
     | 
| 
       122 
118 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -5,14 +5,25 @@ module Kameleoon 
     | 
|
| 
       5 
5 
     | 
    
         
             
                ##
         
     | 
| 
       6 
6 
     | 
    
         
             
                # Request represents HTTP request.
         
     | 
| 
       7 
7 
     | 
    
         
             
                class Request
         
     | 
| 
       8 
     | 
    
         
            -
                  attr_reader :method, :url, :content_type, :timeout, : 
     | 
| 
      
 8 
     | 
    
         
            +
                  attr_reader :method, :url, :content_type, :timeout, :extra_headers, :data, :access_token
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
                  def  
     | 
| 
      
 10 
     | 
    
         
            +
                  def to_s
         
     | 
| 
      
 11 
     | 
    
         
            +
                    body = 'null'
         
     | 
| 
      
 12 
     | 
    
         
            +
                    unless @data.nil?
         
     | 
| 
      
 13 
     | 
    
         
            +
                      if @data.is_a?(String)
         
     | 
| 
      
 14 
     | 
    
         
            +
                        body = @data.start_with?('grant_type=client_credentials') ? '****' : @data
         
     | 
| 
      
 15 
     | 
    
         
            +
                      else
         
     | 
| 
      
 16 
     | 
    
         
            +
                        body = @data
         
     | 
| 
      
 17 
     | 
    
         
            +
                      end
         
     | 
| 
      
 18 
     | 
    
         
            +
                    end
         
     | 
| 
      
 19 
     | 
    
         
            +
                    "HttpRequest{Method:'#{@method}',Url:'#{@url}',Headers:#{@extra_headers},Body:'#{body}'}"
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  def initialize(method, url, content_type, timeout, extra_headers: nil, data: nil)
         
     | 
| 
       11 
23 
     | 
    
         
             
                    @method = method
         
     | 
| 
       12 
24 
     | 
    
         
             
                    @url = url
         
     | 
| 
       13 
25 
     | 
    
         
             
                    @content_type = content_type
         
     | 
| 
       14 
26 
     | 
    
         
             
                    @timeout = timeout
         
     | 
| 
       15 
     | 
    
         
            -
                    @user_agent = user_agent
         
     | 
| 
       16 
27 
     | 
    
         
             
                    @extra_headers = extra_headers
         
     | 
| 
       17 
28 
     | 
    
         
             
                    @data = !data.nil? && data.is_a?(String) ? data.encode('UTF-8') : data
         
     | 
| 
       18 
29 
     | 
    
         
             
                  end
         
     |