kameleoon-client-ruby 3.2.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 (75) 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 +40 -17
  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 +16 -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 +198 -67
  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 +12 -1
  28. data/lib/kameleoon/hybrid/manager.rb +13 -4
  29. data/lib/kameleoon/kameleoon_client.rb +348 -146
  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 +33 -18
  37. data/lib/kameleoon/managers/remote_data/remote_visitor_data.rb +42 -16
  38. data/lib/kameleoon/managers/tracking/tracking_builder.rb +149 -0
  39. data/lib/kameleoon/managers/tracking/tracking_manager.rb +97 -0
  40. data/lib/kameleoon/managers/tracking/visitor_tracking_registry.rb +94 -0
  41. data/lib/kameleoon/managers/warehouse/warehouse_manager.rb +22 -5
  42. data/lib/kameleoon/network/access_token_source.rb +46 -14
  43. data/lib/kameleoon/network/cookie/cookie_manager.rb +45 -7
  44. data/lib/kameleoon/network/net_provider.rb +2 -3
  45. data/lib/kameleoon/network/network_manager.rb +16 -21
  46. data/lib/kameleoon/network/request.rb +14 -3
  47. data/lib/kameleoon/network/response.rb +4 -0
  48. data/lib/kameleoon/network/url_provider.rb +11 -10
  49. data/lib/kameleoon/real_time/real_time_configuration_service.rb +10 -11
  50. data/lib/kameleoon/sdk_version.rb +31 -0
  51. data/lib/kameleoon/targeting/condition.rb +6 -2
  52. data/lib/kameleoon/targeting/condition_factory.rb +3 -0
  53. data/lib/kameleoon/targeting/conditions/browser_condition.rb +3 -3
  54. data/lib/kameleoon/targeting/conditions/cookie_condition.rb +10 -10
  55. data/lib/kameleoon/targeting/conditions/geolocation_condition.rb +0 -1
  56. data/lib/kameleoon/targeting/conditions/kcs_heat_range_condition.rb +37 -0
  57. data/lib/kameleoon/targeting/conditions/number_condition.rb +4 -4
  58. data/lib/kameleoon/targeting/conditions/operating_system_condition.rb +1 -2
  59. data/lib/kameleoon/targeting/conditions/sdk_language_condition.rb +2 -1
  60. data/lib/kameleoon/targeting/conditions/segment_condition.rb +3 -3
  61. data/lib/kameleoon/targeting/conditions/string_value_condition.rb +2 -1
  62. data/lib/kameleoon/targeting/conditions/target_feature_flag_condition.rb +7 -11
  63. data/lib/kameleoon/targeting/conditions/time_elapsed_since_visit_condition.rb +1 -2
  64. data/lib/kameleoon/targeting/conditions/visit_number_today_condition.rb +4 -4
  65. data/lib/kameleoon/targeting/conditions/visit_number_total_condition.rb +5 -3
  66. data/lib/kameleoon/targeting/conditions/visitor_new_return_condition.rb +7 -6
  67. data/lib/kameleoon/targeting/models.rb +0 -14
  68. data/lib/kameleoon/targeting/targeting_manager.rb +37 -7
  69. data/lib/kameleoon/targeting/tree_builder.rb +10 -5
  70. data/lib/kameleoon/types/remote_visitor_data_filter.rb +21 -3
  71. data/lib/kameleoon/types/variable.rb +21 -0
  72. data/lib/kameleoon/types/variation.rb +22 -0
  73. data/lib/kameleoon/utils.rb +18 -0
  74. data/lib/kameleoon/version.rb +1 -27
  75. metadata +16 -2
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kameleoon/data/custom_data'
4
+ require 'kameleoon/logging/kameleoon_logger'
5
+ require 'kameleoon/network/activity_event'
6
+ require 'kameleoon/network/uri_helper'
7
+
8
+ module Kameleoon
9
+ module Managers
10
+ module Tracking
11
+ class TrackingBuilder
12
+ attr_reader :visitor_codes_to_send, :visitor_codes_to_keep, :tracking_lines, :unsent_visitor_data
13
+
14
+ def initialize(visitor_codes, data_file, visitor_manager, request_size_limit)
15
+ @visitor_codes = visitor_codes
16
+ @data_file = data_file
17
+ @visitor_manager = visitor_manager
18
+ @request_size_limit = request_size_limit
19
+ @built = false
20
+ @total_size = 0
21
+ # result
22
+ @visitor_codes_to_send = []
23
+ @visitor_codes_to_keep = []
24
+ @tracking_lines = []
25
+ @unsent_visitor_data = []
26
+ end
27
+
28
+ # Not thread-safe
29
+ def build
30
+ return if @built
31
+
32
+ @visitor_codes.each do |visitor_code|
33
+ if @total_size <= @request_size_limit
34
+ visitor = @visitor_manager.get_visitor(visitor_code)
35
+ is_consent_given = consent_given?(visitor)
36
+ data = collect_tracking_data(visitor_code, visitor, is_consent_given)
37
+ if !data.empty?
38
+ log_visitor_track_sending(visitor_code, is_consent_given, data)
39
+ @visitor_codes_to_send.push(visitor_code)
40
+ @unsent_visitor_data.concat(data)
41
+ else
42
+ log_visitor_track_no_data(visitor_code, is_consent_given)
43
+ end
44
+ else
45
+ @visitor_codes_to_keep.push(visitor_code)
46
+ end
47
+ end
48
+ @built = true
49
+ end
50
+
51
+ private
52
+
53
+ def log_visitor_track_sending(visitor_code, is_consent_given, data)
54
+ Logging::KameleoonLogger.debug(
55
+ "Sending tracking request for unsent data %s of visitor '%s' with given (or not required) consent %s",
56
+ data, visitor_code, is_consent_given
57
+ )
58
+ end
59
+
60
+ def log_visitor_track_no_data(visitor_code, is_consent_given)
61
+ Logging::KameleoonLogger.debug(
62
+ "No data to send for visitor '%s' with given (or not required) consent %s",
63
+ visitor_code, is_consent_given
64
+ )
65
+ end
66
+
67
+ def consent_given?(visitor)
68
+ !@data_file.settings.is_consent_required || visitor&.legal_consent
69
+ end
70
+
71
+ def collect_tracking_data(visitor_code, visitor, is_consent_given)
72
+ use_mapping_value, visitor = create_self_visitor_link_if_required(visitor_code, visitor)
73
+ Logging::KameleoonLogger.info(
74
+ "'%s' was used as a %s for visitor data tracking.",
75
+ visitor_code, (use_mapping_value ? 'mapping value' : 'visitor code')
76
+ )
77
+ unsent_data = get_unsent_visitor_data(visitor, is_consent_given)
78
+ collect_tracking_lines(visitor_code, visitor, unsent_data, use_mapping_value)
79
+ unsent_data
80
+ end
81
+
82
+ def create_self_visitor_link_if_required(visitor_code, visitor)
83
+ is_mapped = !visitor&.mapping_identifier.nil?
84
+ is_unique_identifier = visitor&.is_unique_identifier
85
+ # need to find if anonymous visitor is behind unique (anonym doesn't exist if MappingIdentifier == null)
86
+ if is_unique_identifier && !is_mapped
87
+ # We haven't anonymous behind, in this case we should create "fake" anonymous with id == visitorCode
88
+ # and link it with with mapping value == visitorCode (like we do as we have real anonymous visitor)
89
+ visitor = @visitor_manager.add_data(
90
+ visitor_code, CustomData.new(@data_file.custom_data_info.mapping_identifier_index, visitor_code)
91
+ )
92
+ end
93
+ use_mapping_value = is_unique_identifier && (visitor_code != visitor&.mapping_identifier)
94
+ [use_mapping_value, visitor]
95
+ end
96
+
97
+ def get_unsent_visitor_data(visitor, is_consent_given)
98
+ unsent_data = []
99
+ unless visitor.nil?
100
+ if is_consent_given
101
+ visitor.enumerate_sendable_data do |data|
102
+ unsent_data.push(data) if data.unsent
103
+ next true
104
+ end
105
+ else
106
+ visitor.conversions.enumerate do |c|
107
+ unsent_data.push(c) if c.unsent
108
+ next true
109
+ end
110
+ if @data_file.has_any_targeted_delivery_rule
111
+ visitor.variations.enumerate do |av|
112
+ unsent_data.push(av) if av.unsent && (av.rule_type == Configuration::RuleType::TARGETED_DELIVERY)
113
+ next true
114
+ end
115
+ end
116
+ end
117
+ end
118
+ unsent_data.push(Network::ActivityEvent.new) if unsent_data.empty? && is_consent_given
119
+ unsent_data
120
+ end
121
+
122
+ def collect_tracking_lines(visitor_code, visitor, unsent_data, use_mapping_value)
123
+ visitor_code_param = Network::UriHelper.encode_query(
124
+ { (use_mapping_value ? :mappingValue : :visitorCode) => visitor_code }
125
+ )
126
+ user_agent = visitor&.user_agent
127
+ unsent_data.each do |data|
128
+ line = data.obtain_full_post_text_line
129
+ next if line.empty?
130
+
131
+ line = add_line_params(line, visitor_code_param, user_agent)
132
+ @tracking_lines.push(line)
133
+ @total_size += line.size
134
+ user_agent = nil
135
+ end
136
+ end
137
+
138
+ def add_line_params(tracking_line, visitor_code_param, user_agent)
139
+ tracking_line += "&#{visitor_code_param}"
140
+ unless user_agent.nil?
141
+ user_agent_param = Network::UriHelper.encode_query({ userAgent: user_agent })
142
+ tracking_line += "&#{user_agent_param}"
143
+ end
144
+ tracking_line
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
@@ -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