kameleoon-client-ruby 3.3.0 → 3.4.0

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