kameleoon-client-ruby 3.3.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/lib/kameleoon/configuration/custom_data_info.rb +16 -8
  3. data/lib/kameleoon/configuration/data_file.rb +37 -15
  4. data/lib/kameleoon/configuration/feature_flag.rb +10 -0
  5. data/lib/kameleoon/configuration/rule.rb +4 -0
  6. data/lib/kameleoon/configuration/settings.rb +13 -8
  7. data/lib/kameleoon/configuration/variation_exposition.rb +4 -0
  8. data/lib/kameleoon/data/browser.rb +4 -0
  9. data/lib/kameleoon/data/conversion.rb +4 -0
  10. data/lib/kameleoon/data/cookie.rb +4 -0
  11. data/lib/kameleoon/data/custom_data.rb +11 -3
  12. data/lib/kameleoon/data/data.rb +30 -4
  13. data/lib/kameleoon/data/device.rb +4 -0
  14. data/lib/kameleoon/data/geolocation.rb +5 -0
  15. data/lib/kameleoon/data/kcs_heat.rb +4 -0
  16. data/lib/kameleoon/data/manager/assigned_variation.rb +5 -0
  17. data/lib/kameleoon/data/manager/data_array_storage.rb +7 -0
  18. data/lib/kameleoon/data/manager/data_map_storage.rb +7 -0
  19. data/lib/kameleoon/data/manager/page_view_visit.rb +4 -0
  20. data/lib/kameleoon/data/manager/visitor.rb +197 -73
  21. data/lib/kameleoon/data/manager/visitor_manager.rb +54 -17
  22. data/lib/kameleoon/data/mapping_identifier.rb +33 -0
  23. data/lib/kameleoon/data/operating_system.rb +4 -0
  24. data/lib/kameleoon/data/page_view.rb +6 -1
  25. data/lib/kameleoon/data/unique_identifier.rb +11 -0
  26. data/lib/kameleoon/data/user_agent.rb +4 -0
  27. data/lib/kameleoon/data/visitor_visits.rb +4 -0
  28. data/lib/kameleoon/hybrid/manager.rb +13 -4
  29. data/lib/kameleoon/kameleoon_client.rb +303 -148
  30. data/lib/kameleoon/kameleoon_client_config.rb +64 -17
  31. data/lib/kameleoon/kameleoon_client_factory.rb +15 -2
  32. data/lib/kameleoon/logging/default_logger.rb +20 -0
  33. data/lib/kameleoon/logging/kameleoon_logger.rb +77 -0
  34. data/lib/kameleoon/logging/logger.rb +12 -0
  35. data/lib/kameleoon/managers/data/data_manager.rb +36 -0
  36. data/lib/kameleoon/managers/remote_data/remote_data_manager.rb +32 -15
  37. data/lib/kameleoon/managers/tracking/tracking_builder.rb +149 -0
  38. data/lib/kameleoon/managers/tracking/tracking_manager.rb +97 -0
  39. data/lib/kameleoon/managers/tracking/visitor_tracking_registry.rb +94 -0
  40. data/lib/kameleoon/managers/warehouse/warehouse_manager.rb +22 -5
  41. data/lib/kameleoon/network/access_token_source.rb +46 -14
  42. data/lib/kameleoon/network/cookie/cookie_manager.rb +45 -7
  43. data/lib/kameleoon/network/net_provider.rb +2 -3
  44. data/lib/kameleoon/network/network_manager.rb +16 -21
  45. data/lib/kameleoon/network/request.rb +14 -3
  46. data/lib/kameleoon/network/response.rb +4 -0
  47. data/lib/kameleoon/network/url_provider.rb +4 -4
  48. data/lib/kameleoon/real_time/real_time_configuration_service.rb +10 -11
  49. data/lib/kameleoon/sdk_version.rb +31 -0
  50. data/lib/kameleoon/targeting/condition.rb +4 -2
  51. data/lib/kameleoon/targeting/conditions/browser_condition.rb +3 -3
  52. data/lib/kameleoon/targeting/conditions/cookie_condition.rb +10 -10
  53. data/lib/kameleoon/targeting/conditions/geolocation_condition.rb +0 -1
  54. data/lib/kameleoon/targeting/conditions/number_condition.rb +4 -4
  55. data/lib/kameleoon/targeting/conditions/operating_system_condition.rb +1 -2
  56. data/lib/kameleoon/targeting/conditions/sdk_language_condition.rb +2 -1
  57. data/lib/kameleoon/targeting/conditions/segment_condition.rb +3 -3
  58. data/lib/kameleoon/targeting/conditions/string_value_condition.rb +2 -1
  59. data/lib/kameleoon/targeting/models.rb +0 -14
  60. data/lib/kameleoon/targeting/targeting_manager.rb +35 -7
  61. data/lib/kameleoon/targeting/tree_builder.rb +10 -5
  62. data/lib/kameleoon/types/remote_visitor_data_filter.rb +13 -0
  63. data/lib/kameleoon/types/variable.rb +4 -0
  64. data/lib/kameleoon/types/variation.rb +4 -0
  65. data/lib/kameleoon/utils.rb +18 -0
  66. data/lib/kameleoon/version.rb +1 -27
  67. metadata +12 -2
@@ -0,0 +1,97 @@
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.debug('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.debug('Failed request for tracking visitors: %s, data: %s',
88
+ visitor_codes, visitor_data)
89
+ visitor_data.each { |d| d.mark_as_unsent if d.is_a?(Kameleoon::Data) }
90
+ @tracking_visitors.add_all(visitor_codes)
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ 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, log_func = nil)
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
- @log_func = log_func
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
- return nil unless response.is_a?(String)
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
- visitor = @visitor_manager.get_or_create_visitor(visitor_code)
29
- visitor.add_data(@log_func, warehouse_audiences_data)
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, log_func)
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
- @log_func = log_func
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
- @log_func&.call("Failed to call access token fetching: #{e}")
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
- @log_func&.call("Failed to run access token fetching: #{e}")
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
- @log_func&.call('Failed to fetch access JWT')
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
- @log_func&.call("Failed to parse access JWT: #{e}")
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
- @log_func&.call('Failed to read access JWT')
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
- unless @log_func.nil?
87
- issue = expires_in <= TOKEN_EXPIRATION_GAP ? 'cache the token' : 'refresh cached token in background'
88
- @log_func.call("Access token life time (#{expires_in}s) is not long enough to #{issue}")
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, log_func)
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
- @log_func = log_func
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, @log_func)
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,75 @@ module Kameleoon
10
11
  COOKIE_TTL_SECONDS = 380 * 86_400 # 380 days in seconds
11
12
 
12
13
  class CookieManager
13
- attr_accessor :consent_required
14
-
15
- def initialize(top_level_domain)
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 @consent_required
34
+ add(visitor_code, cookies) unless @data_manager.consent_required?
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
- add(visitor_code, cookies) unless @consent_required
44
+ Logging::KameleoonLogger.debug("Generated new visitor code '%s'", visitor_code)
45
+ add(visitor_code, cookies) unless @data_manager.consent_required?
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)
40
60
  visitor_code
41
61
  end
42
62
 
43
63
  def update(visitor_code, consent, cookies)
44
64
  return if cookies.nil?
45
65
 
66
+ Logging::KameleoonLogger.debug("CALL: CookieManager.update(visitor_code: '%s', consent: %s, cookies: %s)",
67
+ visitor_code, consent, cookies)
68
+
46
69
  if consent
47
70
  add(visitor_code, cookies)
48
71
  else
49
72
  remove(cookies)
50
73
  end
74
+ Logging::KameleoonLogger.debug("RETURN: CookieManager.update(visitor_code: '%s', consent: %s, cookies: %s)",
75
+ visitor_code, consent, cookies)
51
76
  end
52
77
 
53
78
  private
54
79
 
55
80
  def add(visitor_code, cookies)
81
+ Logging::KameleoonLogger.debug("CALL: CookieManager.add(visitor_code: '%s', cookies: %s)",
82
+ visitor_code, cookies)
56
83
  cookie = {
57
84
  value: visitor_code,
58
85
  expires: Time.now + COOKIE_TTL_SECONDS,
@@ -60,13 +87,20 @@ module Kameleoon
60
87
  domain: @top_level_domain
61
88
  }
62
89
  cookies[VISITOR_CODE_COOKIE] = cookie
90
+ Logging::KameleoonLogger.debug("RETURN: CookieManager.add(visitor_code: '%s', cookies: %s)",
91
+ visitor_code, cookies)
92
+ cookies
63
93
  end
64
94
 
65
95
  def remove(cookies)
66
- cookies[VISITOR_CODE_COOKIE] = nil if @consent_required
96
+ Logging::KameleoonLogger.debug('CALL: CookieManager.remove(cookies: %s)', cookies)
97
+ cookies[VISITOR_CODE_COOKIE] = nil if @data_manager.consent_required?
98
+ Logging::KameleoonLogger.debug('RETURN: CookieManager.remove(cookies: %s)', cookies)
99
+ cookies
67
100
  end
68
101
 
69
102
  def get_visitor_code_from_cookies(cookies)
103
+ Logging::KameleoonLogger.debug('CALL: CookieManager.get_visitor_code_from_cookies(cookies: %s)', cookies)
70
104
  cookie = cookies[VISITOR_CODE_COOKIE]
71
105
  case cookie
72
106
  when String
@@ -76,6 +110,10 @@ module Kameleoon
76
110
  end
77
111
  visitor_code = visitor_code[COOKIE_KEY_JS.size..] if visitor_code&.start_with?(COOKIE_KEY_JS)
78
112
  visitor_code = nil if visitor_code&.empty?
113
+ Logging::KameleoonLogger.debug(
114
+ "RETURN: CookieManager.get_visitor_code_from_cookies(cookies: %s) -> (visitor_code: '%s')",
115
+ cookies, visitor_code
116
+ )
79
117
  visitor_code
80
118
  end
81
119
  end
@@ -8,7 +8,7 @@ require 'kameleoon/exceptions'
8
8
  module Kameleoon
9
9
  module Network
10
10
  class NetProvider
11
- def make_request(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, log_func = nil)
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(visitor_code, lines, user_agent, is_unique_identifier, timeout = nil)
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(visitor_code, is_unique_identifier)
58
+ url = @url_provider.make_tracking_url
59
59
  timeout = ensure_timeout(timeout)
60
- data = (lines.map(&:obtain_full_post_text_line).join("\n") || '').encode('UTF-8')
61
- request = Request.new(Method::POST, url, ContentType::TEXT, timeout, user_agent: user_agent, data: data)
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,30 @@ module Kameleoon
90
88
  if response.success?
91
89
  success = true
92
90
  elsif !response.error.nil?
93
- log_failure(request, "Error occurred during request: #{response.error}")
91
+ Logging::KameleoonLogger.warning("%s call '%s' failed: Error occurred during request: %s",
92
+ request.method, request.url, response.error)
94
93
  else
95
- log_failure(request, "Received unexpected status code '#{response.code}'")
94
+ Logging::KameleoonLogger.warning("%s call '%s' failed: Received unexpected status code '%s'",
95
+ request.method, request.url, response.code)
96
96
  if response.code == 401 && request.access_token
97
- @log_func&.call("Unexpected rejection of access token '#{request.access_token}'")
97
+ Logging::KameleoonLogger.warning("Unexpected rejection of access token '#{request.access_token}'")
98
98
  @access_token_source.discard_token(request.access_token)
99
99
  if attempt == retry_limit
100
100
  try_access_token_auth = false
101
101
  retry_delay = 0
102
102
  request.authorize(nil)
103
103
  attempt -= 1
104
+ Logging::KameleoonLogger.error("Wrong Kameleoon API access token slows down the SDK's requests")
104
105
  end
105
106
  end
106
107
  end
107
108
  attempt += 1
108
109
  end
110
+ Logging::KameleoonLogger.debug('Fetched response %s for request %s', response, request)
111
+ Logging::KameleoonLogger.error("Failed %s request '%s'", request.method, request.url) unless success
109
112
  success ? response.body : false
110
113
  end
111
114
 
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
115
  def delay(period)
121
116
  sleep(period)
122
117
  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, :user_agent, :extra_headers, :data, :access_token
8
+ attr_reader :method, :url, :content_type, :timeout, :extra_headers, :data, :access_token
9
9
 
10
- def initialize(method, url, content_type, timeout, user_agent: nil, extra_headers: nil, data: nil)
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
@@ -7,6 +7,10 @@ module Kameleoon
7
7
  class Response
8
8
  attr_reader :error, :code, :body, :request
9
9
 
10
+ def to_s
11
+ "HttpResponse{Code:'#{@code}',Reason:'#{@error}',Body:#{@body}}"
12
+ end
13
+
10
14
  def initialize(error, code, body, request)
11
15
  @error = error
12
16
  @code = code