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.
- checksums.yaml +4 -4
- data/lib/kameleoon/configuration/custom_data_info.rb +16 -8
- data/lib/kameleoon/configuration/data_file.rb +40 -17
- data/lib/kameleoon/configuration/feature_flag.rb +10 -0
- 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 +16 -0
- data/lib/kameleoon/data/manager/assigned_variation.rb +5 -0
- 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 +198 -67
- 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 +12 -1
- data/lib/kameleoon/hybrid/manager.rb +13 -4
- data/lib/kameleoon/kameleoon_client.rb +348 -146
- data/lib/kameleoon/kameleoon_client_config.rb +64 -17
- 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 +33 -18
- data/lib/kameleoon/managers/remote_data/remote_visitor_data.rb +42 -16
- data/lib/kameleoon/managers/tracking/tracking_builder.rb +149 -0
- data/lib/kameleoon/managers/tracking/tracking_manager.rb +97 -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 +45 -7
- data/lib/kameleoon/network/net_provider.rb +2 -3
- data/lib/kameleoon/network/network_manager.rb +16 -21
- data/lib/kameleoon/network/request.rb +14 -3
- data/lib/kameleoon/network/response.rb +4 -0
- data/lib/kameleoon/network/url_provider.rb +11 -10
- 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 +6 -2
- data/lib/kameleoon/targeting/condition_factory.rb +3 -0
- 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/kcs_heat_range_condition.rb +37 -0
- 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/conditions/target_feature_flag_condition.rb +7 -11
- data/lib/kameleoon/targeting/conditions/time_elapsed_since_visit_condition.rb +1 -2
- data/lib/kameleoon/targeting/conditions/visit_number_today_condition.rb +4 -4
- data/lib/kameleoon/targeting/conditions/visit_number_total_condition.rb +5 -3
- data/lib/kameleoon/targeting/conditions/visitor_new_return_condition.rb +7 -6
- data/lib/kameleoon/targeting/models.rb +0 -14
- data/lib/kameleoon/targeting/targeting_manager.rb +37 -7
- data/lib/kameleoon/targeting/tree_builder.rb +10 -5
- data/lib/kameleoon/types/remote_visitor_data_filter.rb +21 -3
- data/lib/kameleoon/types/variable.rb +21 -0
- data/lib/kameleoon/types/variation.rb +22 -0
- data/lib/kameleoon/utils.rb +18 -0
- data/lib/kameleoon/version.rb +1 -27
- metadata +16 -2
|
@@ -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,30 @@ module Kameleoon
|
|
|
90
88
|
if response.success?
|
|
91
89
|
success = true
|
|
92
90
|
elsif !response.error.nil?
|
|
93
|
-
|
|
91
|
+
Logging::KameleoonLogger.warning("%s call '%s' failed: Error occurred during request: %s",
|
|
92
|
+
request.method, request.url, response.error)
|
|
94
93
|
else
|
|
95
|
-
|
|
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
|
-
|
|
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, :
|
|
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
|
|
@@ -19,7 +19,7 @@ module Kameleoon
|
|
|
19
19
|
DEFAULT_DATA_API_DOMAIN = 'data.kameleoon.io'
|
|
20
20
|
TEST_DATA_API_DOMAIN = 'data.kameleoon.net'
|
|
21
21
|
DEFAULT_AUTOMATION_API_DOMAIN = 'api.kameleoon.com'
|
|
22
|
-
TEST_AUTOMATION_API_DOMAIN = 'api.kameleoon.net'
|
|
22
|
+
TEST_AUTOMATION_API_DOMAIN = 'api.master.preview.kameleoon.net'
|
|
23
23
|
|
|
24
24
|
attr_reader :site_code, :data_api_domain, :automation_api_domain
|
|
25
25
|
|
|
@@ -37,13 +37,13 @@ module Kameleoon
|
|
|
37
37
|
@data_api_domain = domain if domain.is_a?(String)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
def make_tracking_url
|
|
40
|
+
def make_tracking_url
|
|
41
41
|
params = {
|
|
42
42
|
sdkName: SDK_NAME,
|
|
43
43
|
sdkVersion: SDK_VERSION,
|
|
44
|
-
siteCode: @site_code
|
|
44
|
+
siteCode: @site_code,
|
|
45
|
+
bodyUa: true
|
|
45
46
|
}
|
|
46
|
-
params[is_unique_identifier ? :mappingValue : :visitorCode] = visitor_code
|
|
47
47
|
"https://#{@data_api_domain}#{TRACKING_PATH}?#{UriHelper.encode_query(params)}"
|
|
48
48
|
end
|
|
49
49
|
|
|
@@ -52,12 +52,13 @@ module Kameleoon
|
|
|
52
52
|
params[is_unique_identifier ? :mappingValue : :visitorCode] = visitor_code
|
|
53
53
|
params[:maxNumberPreviousVisits] = filter.previous_visit_amount
|
|
54
54
|
params[:version] = 0
|
|
55
|
-
params[:
|
|
56
|
-
params[:
|
|
57
|
-
params[:
|
|
58
|
-
params[:
|
|
59
|
-
params[:
|
|
60
|
-
params[:
|
|
55
|
+
params[:kcs] = true if filter.kcs
|
|
56
|
+
params[:currentVisit] = true if filter.current_visit
|
|
57
|
+
params[:customData] = true if filter.custom_data
|
|
58
|
+
params[:conversion] = true if filter.conversions
|
|
59
|
+
params[:geolocation] = true if filter.geolocation
|
|
60
|
+
params[:experiment] = true if filter.experiments
|
|
61
|
+
params[:page] = true if filter.page_views
|
|
61
62
|
params[:staticData] = true if filter.device || filter.browser || filter.operating_system
|
|
62
63
|
"https://#{@data_api_domain}#{VISITOR_DATA_PATH}?#{UriHelper.encode_query(params)}"
|
|
63
64
|
end
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
# Kameleoon Real Time Configuration Service
|
|
4
4
|
|
|
5
5
|
require 'json'
|
|
6
|
+
require 'kameleoon/logging/kameleoon_logger'
|
|
6
7
|
require 'kameleoon/real_time/real_time_event'
|
|
7
8
|
require 'kameleoon/real_time/sse_request'
|
|
8
9
|
require 'kameleoon/real_time/sse_client'
|
|
@@ -21,8 +22,7 @@ module Kameleoon
|
|
|
21
22
|
# @param url [String]
|
|
22
23
|
# @param update_handler [Callable[Kameleoon::RealTime::RealTimeEvent] | NilClass] Handler which
|
|
23
24
|
# is synchronously called for gotten RealTimeEvent objects.
|
|
24
|
-
|
|
25
|
-
def initialize(url, update_handler, log_func, sse_request_source = nil)
|
|
25
|
+
def initialize(url, update_handler, sse_request_source = nil)
|
|
26
26
|
@url = url
|
|
27
27
|
@update_handler = update_handler
|
|
28
28
|
@need_close = false
|
|
@@ -31,7 +31,6 @@ module Kameleoon
|
|
|
31
31
|
'Cache-Control': 'no-cache',
|
|
32
32
|
'Connection': 'Keep-Alive'
|
|
33
33
|
}
|
|
34
|
-
@log_func = log_func
|
|
35
34
|
@sse_request_source = sse_request_source
|
|
36
35
|
@sse_thread = nil
|
|
37
36
|
@sse_client = nil
|
|
@@ -43,13 +42,13 @@ module Kameleoon
|
|
|
43
42
|
def close
|
|
44
43
|
return if @need_close
|
|
45
44
|
|
|
46
|
-
|
|
45
|
+
Logging::KameleoonLogger.info('Real-time configuration service is shutting down')
|
|
47
46
|
@need_close = true
|
|
48
47
|
return if @sse_thread.nil?
|
|
49
48
|
|
|
50
49
|
@sse_thread.kill
|
|
51
50
|
@sse_thread = nil
|
|
52
|
-
@sse_client
|
|
51
|
+
@sse_client&.call_close_handler
|
|
53
52
|
end
|
|
54
53
|
|
|
55
54
|
private
|
|
@@ -60,7 +59,7 @@ module Kameleoon
|
|
|
60
59
|
|
|
61
60
|
def init_sse_client
|
|
62
61
|
message_handler = proc do |message|
|
|
63
|
-
|
|
62
|
+
Logging::KameleoonLogger.debug("Got SSE event: #{message.event}")
|
|
64
63
|
if message.event == CONFIGURATION_UPDATE_EVENT
|
|
65
64
|
event_dict = JSON.parse(message.data)
|
|
66
65
|
@update_handler&.call(RealTimeEvent.new(event_dict))
|
|
@@ -68,14 +67,14 @@ module Kameleoon
|
|
|
68
67
|
end
|
|
69
68
|
sse_request = make_sse_request
|
|
70
69
|
@sse_client = SseClient.new(sse_request, message_handler)
|
|
71
|
-
|
|
70
|
+
Logging::KameleoonLogger.info('Created SSE client')
|
|
72
71
|
end
|
|
73
72
|
|
|
74
73
|
def make_sse_request
|
|
75
|
-
open_handler = proc {
|
|
76
|
-
close_handler = proc {
|
|
74
|
+
open_handler = proc { Logging::KameleoonLogger.info('SSE connection open') }
|
|
75
|
+
close_handler = proc { Logging::KameleoonLogger.info('SSE connection closed') }
|
|
77
76
|
unexpected_status_code_handler =
|
|
78
|
-
proc { |resp|
|
|
77
|
+
proc { |resp| Logging::KameleoonLogger.error("Unexpected status code of SSE response: #{resp.code}") }
|
|
79
78
|
if @sse_request_source.nil?
|
|
80
79
|
return SseRequest.new(@url, @headers, open_handler, close_handler, unexpected_status_code_handler)
|
|
81
80
|
end
|
|
@@ -89,7 +88,7 @@ module Kameleoon
|
|
|
89
88
|
begin
|
|
90
89
|
@sse_client.start
|
|
91
90
|
rescue StandardError => e
|
|
92
|
-
|
|
91
|
+
Logging::KameleoonLogger.error("Error occurred within SSE client: #{e}")
|
|
93
92
|
end
|
|
94
93
|
end
|
|
95
94
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'kameleoon/logging/kameleoon_logger'
|
|
4
|
+
|
|
5
|
+
module Kameleoon
|
|
6
|
+
# SdkManager is a helper method for fetching / obtaining version of SDK from string
|
|
7
|
+
class SdkVersion
|
|
8
|
+
def self.get_version_components(version_string)
|
|
9
|
+
versions = [0, 0, 0]
|
|
10
|
+
|
|
11
|
+
version_parts = version_string.split('.')
|
|
12
|
+
version_parts.each_with_index do |part, i|
|
|
13
|
+
versions[i] = Integer(part)
|
|
14
|
+
rescue ArgumentError
|
|
15
|
+
Logging::KameleoonLogger.error('Invalid version component, index: %s, value: %s', i, part)
|
|
16
|
+
return nil
|
|
17
|
+
end
|
|
18
|
+
versions
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.get_float_version(version_string)
|
|
22
|
+
version_components = get_version_components(version_string)
|
|
23
|
+
|
|
24
|
+
return Float::NAN if version_components.nil?
|
|
25
|
+
|
|
26
|
+
major = version_components[0]
|
|
27
|
+
minor = version_components[1]
|
|
28
|
+
"#{major}.#{minor}".to_f
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
require 'kameleoon/exceptions'
|
|
4
|
+
require 'kameleoon/logging/kameleoon_logger'
|
|
3
5
|
|
|
4
6
|
module Kameleoon
|
|
5
7
|
# @api private
|
|
@@ -26,6 +28,7 @@ module Kameleoon
|
|
|
26
28
|
VISITS = 'VISITS'
|
|
27
29
|
SAME_DAY_VISITS = 'SAME_DAY_VISITS'
|
|
28
30
|
NEW_VISITORS = 'NEW_VISITORS'
|
|
31
|
+
HEAT_SLICE = 'HEAT_SLICE'
|
|
29
32
|
end
|
|
30
33
|
|
|
31
34
|
module Operator
|
|
@@ -45,12 +48,13 @@ module Kameleoon
|
|
|
45
48
|
|
|
46
49
|
# Base class for all targeting conditions
|
|
47
50
|
class Condition
|
|
48
|
-
attr_reader :type, :include
|
|
51
|
+
attr_reader :type, :include, :id
|
|
49
52
|
|
|
50
53
|
def initialize(json_condition)
|
|
51
54
|
raise Exception::NotFound.new('targetingType'), 'targetingType missed' if json_condition['targetingType'].nil?
|
|
52
55
|
|
|
53
56
|
@type = json_condition['targetingType']
|
|
57
|
+
@id = json_condition['id']
|
|
54
58
|
@include = json_condition['isInclude'].nil? ? true : json_condition['isInclude']
|
|
55
59
|
end
|
|
56
60
|
|
|
@@ -68,7 +72,7 @@ module Kameleoon
|
|
|
68
72
|
begin
|
|
69
73
|
return pattern.match? value
|
|
70
74
|
rescue StandardError => e
|
|
71
|
-
|
|
75
|
+
Logging::KameleoonLogger.error("Failed to match with regex (targeting): #{e}")
|
|
72
76
|
end
|
|
73
77
|
false
|
|
74
78
|
end
|
|
@@ -11,6 +11,7 @@ require_relative 'conditions/conversion_condition'
|
|
|
11
11
|
require_relative 'conditions/browser_condition'
|
|
12
12
|
require_relative 'conditions/sdk_language_condition'
|
|
13
13
|
require_relative 'conditions/geolocation_condition'
|
|
14
|
+
require_relative 'conditions/kcs_heat_range_condition'
|
|
14
15
|
require_relative 'conditions/operating_system_condition'
|
|
15
16
|
require_relative 'conditions/cookie_condition'
|
|
16
17
|
require_relative 'conditions/segment_condition'
|
|
@@ -67,6 +68,8 @@ module Kameleoon
|
|
|
67
68
|
VisitorNewReturnCondition.new(condition_json)
|
|
68
69
|
when ConditionType::FIRST_VISIT, ConditionType::LAST_VISIT
|
|
69
70
|
TimeElapsedSinceVisitCondition.new(condition_json)
|
|
71
|
+
when ConditionType::HEAT_SLICE
|
|
72
|
+
KcsHeatRangeCondition.new(condition_json)
|
|
70
73
|
else
|
|
71
74
|
UnknownCondition.new(condition_json)
|
|
72
75
|
end
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'kameleoon/data/browser'
|
|
4
|
-
require 'kameleoon/
|
|
4
|
+
require 'kameleoon/logging/kameleoon_logger'
|
|
5
|
+
require 'kameleoon/sdk_version'
|
|
5
6
|
|
|
6
7
|
module Kameleoon
|
|
7
8
|
# @api private
|
|
8
9
|
module Targeting
|
|
9
|
-
|
|
10
10
|
CHROME = 'CHROME'
|
|
11
11
|
INTERNET_EXPLORER = 'IE'
|
|
12
12
|
FIREFOX = 'FIREFOX'
|
|
@@ -44,7 +44,7 @@ module Kameleoon
|
|
|
44
44
|
when Operator::LOWER
|
|
45
45
|
browser.version < version_number
|
|
46
46
|
else
|
|
47
|
-
|
|
47
|
+
Logging::KameleoonLogger.error("Unexpected comparing operation for 'Browser' condition: '#{@version_match_type}'")
|
|
48
48
|
false
|
|
49
49
|
end
|
|
50
50
|
end
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'kameleoon/data/cookie'
|
|
4
|
+
require 'kameleoon/logging/kameleoon_logger'
|
|
4
5
|
require 'kameleoon/version'
|
|
5
6
|
|
|
6
7
|
module Kameleoon
|
|
7
8
|
# @api private
|
|
8
9
|
module Targeting
|
|
9
|
-
|
|
10
10
|
class CookieCondition < Condition
|
|
11
11
|
def initialize(json_condition)
|
|
12
12
|
super(json_condition)
|
|
@@ -26,13 +26,13 @@ module Kameleoon
|
|
|
26
26
|
case @name_match_type
|
|
27
27
|
when Kameleoon::Targeting::Operator::EXACT
|
|
28
28
|
value = cookie.cookies[@condition_name]
|
|
29
|
-
return value
|
|
29
|
+
return !value.nil? ? [value] : []
|
|
30
30
|
when Kameleoon::Targeting::Operator::CONTAINS
|
|
31
|
-
return cookie.cookies.select{|key| key.include? @condition_name}.values
|
|
31
|
+
return cookie.cookies.select { |key| key.include? @condition_name }.values
|
|
32
32
|
when Kameleoon::Targeting::Operator::REGULAR_EXPRESSION
|
|
33
|
-
return cookie.cookies.select{|key| is_regex_match(@condition_name, key)}.values
|
|
33
|
+
return cookie.cookies.select { |key| is_regex_match(@condition_name, key) }.values
|
|
34
34
|
else
|
|
35
|
-
|
|
35
|
+
Logging::KameleoonLogger.error("Unexpected comparing operation for 'Cookie' condition (name): '#{@name_match_type}'")
|
|
36
36
|
end
|
|
37
37
|
[]
|
|
38
38
|
end
|
|
@@ -40,14 +40,14 @@ module Kameleoon
|
|
|
40
40
|
def check_values(values)
|
|
41
41
|
case @value_match_type
|
|
42
42
|
when Kameleoon::Targeting::Operator::EXACT
|
|
43
|
-
|
|
43
|
+
values.any? { |value| value == @condition_value }
|
|
44
44
|
when Kameleoon::Targeting::Operator::CONTAINS
|
|
45
|
-
|
|
45
|
+
values.any? { |value| value.include? @condition_value }
|
|
46
46
|
when Kameleoon::Targeting::Operator::REGULAR_EXPRESSION
|
|
47
|
-
|
|
47
|
+
values.any? { |value| is_regex_match(@condition_value, value) }
|
|
48
48
|
else
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
Logging::KameleoonLogger.error("Unexpected comparing operation for 'Cookie' condition (name): '#{@name_match_type}'")
|
|
50
|
+
false
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'kameleoon/data/device'
|
|
4
|
+
|
|
5
|
+
module Kameleoon
|
|
6
|
+
# @api private
|
|
7
|
+
module Targeting
|
|
8
|
+
# KcsHeatRangeCondition is a condition for checking KCS heat
|
|
9
|
+
class KcsHeatRangeCondition < Condition
|
|
10
|
+
def initialize(json_condition)
|
|
11
|
+
super(json_condition)
|
|
12
|
+
@goal_id = json_condition['goalId'] || -1
|
|
13
|
+
@key_moment_id = json_condition['keyMomentId'] || -1
|
|
14
|
+
@lower_bound = json_condition['lowerBound'] || Float::MAX
|
|
15
|
+
@upper_bound = json_condition['upperBound'] || Float::MIN
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def check(kcs_heat)
|
|
19
|
+
kcs_heat.is_a?(Kameleoon::KcsHeat) && check_targeting(kcs_heat)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def check_targeting(kcs_heat)
|
|
25
|
+
return false unless kcs_heat.values.is_a?(Hash)
|
|
26
|
+
|
|
27
|
+
goal_scores = kcs_heat.values[@key_moment_id]
|
|
28
|
+
return false unless goal_scores.is_a?(Hash)
|
|
29
|
+
|
|
30
|
+
score = goal_scores[@goal_id]
|
|
31
|
+
return false unless score.is_a?(Float) || score.is_a?(Integer)
|
|
32
|
+
|
|
33
|
+
(score >= @lower_bound) && (score <= @upper_bound)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'kameleoon/data/cookie'
|
|
4
|
+
require 'kameleoon/logging/kameleoon_logger'
|
|
4
5
|
require 'kameleoon/version'
|
|
5
6
|
|
|
6
7
|
module Kameleoon
|
|
7
8
|
# @api private
|
|
8
9
|
module Targeting
|
|
9
|
-
|
|
10
10
|
class NumberCondition < Condition
|
|
11
11
|
def initialize(json_condition, value)
|
|
12
12
|
super(json_condition)
|
|
@@ -25,11 +25,11 @@ module Kameleoon
|
|
|
25
25
|
when Kameleoon::Targeting::Operator::EQUAL
|
|
26
26
|
return value == @condition_value
|
|
27
27
|
when Kameleoon::Targeting::Operator::GREATER
|
|
28
|
-
return
|
|
28
|
+
return !@condition_value.nil? && value > @condition_value
|
|
29
29
|
when Kameleoon::Targeting::Operator::LOWER
|
|
30
|
-
return
|
|
30
|
+
return !@condition_value.nil? && value < @condition_value
|
|
31
31
|
else
|
|
32
|
-
|
|
32
|
+
Logging::KameleoonLogger.error("Unexpected comparing operation for '#{@type}' condition: '#{@match_type}'")
|
|
33
33
|
end
|
|
34
34
|
false
|
|
35
35
|
end
|
|
@@ -6,7 +6,6 @@ require 'kameleoon/version'
|
|
|
6
6
|
module Kameleoon
|
|
7
7
|
# @api private
|
|
8
8
|
module Targeting
|
|
9
|
-
|
|
10
9
|
class OperatingSystemCondition < Condition
|
|
11
10
|
def initialize(json_condition)
|
|
12
11
|
super(json_condition)
|
|
@@ -20,7 +19,7 @@ module Kameleoon
|
|
|
20
19
|
private
|
|
21
20
|
|
|
22
21
|
def check_targeting(operating_system)
|
|
23
|
-
|
|
22
|
+
!@os_type.nil? && operating_system.os_type == @os_type
|
|
24
23
|
end
|
|
25
24
|
end
|
|
26
25
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'kameleoon/data/data'
|
|
4
|
+
require 'kameleoon/logging/kameleoon_logger'
|
|
4
5
|
require 'kameleoon/version'
|
|
5
6
|
|
|
6
7
|
module Kameleoon
|
|
@@ -56,7 +57,7 @@ module Kameleoon
|
|
|
56
57
|
(major_sdk == major_condition && minor_sdk < minor_condition) ||
|
|
57
58
|
(major_sdk == major_condition && minor_sdk == minor_condition && patch_sdk < patch_condition)
|
|
58
59
|
else
|
|
59
|
-
|
|
60
|
+
Logging::KameleoonLogger.error("Unexpected comparing operation for 'SdkLanguage' condition: '#{@operator}'")
|
|
60
61
|
false
|
|
61
62
|
end
|
|
62
63
|
end
|
|
@@ -6,11 +6,10 @@ require 'kameleoon/version'
|
|
|
6
6
|
module Kameleoon
|
|
7
7
|
# @api private
|
|
8
8
|
module Targeting
|
|
9
|
-
|
|
10
9
|
class SegmentCondition < Condition
|
|
11
10
|
def initialize(json_condition)
|
|
12
11
|
super(json_condition)
|
|
13
|
-
@segment_id = json_condition[
|
|
12
|
+
@segment_id = json_condition['segmentId']
|
|
14
13
|
end
|
|
15
14
|
|
|
16
15
|
def check(data)
|
|
@@ -22,7 +21,8 @@ module Kameleoon
|
|
|
22
21
|
def check_targeting(segment_info)
|
|
23
22
|
rule = segment_info.data_file.rule_by_segment_id[@segment_id]
|
|
24
23
|
return false unless rule.is_a?(Kameleoon::Configuration::Rule)
|
|
25
|
-
|
|
24
|
+
|
|
25
|
+
rule.targeting_segment.check_tree(->(type) { segment_info.condition_data(type) })
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'kameleoon/logging/kameleoon_logger'
|
|
3
4
|
require 'kameleoon/targeting/condition'
|
|
4
5
|
|
|
5
6
|
module Kameleoon
|
|
@@ -33,7 +34,7 @@ module Kameleoon
|
|
|
33
34
|
pattern = Regexp.new(@condition_value)
|
|
34
35
|
pattern.match?(value)
|
|
35
36
|
else
|
|
36
|
-
|
|
37
|
+
Logging::KameleoonLogger.error("Unexpected comparing operation for '#{@type}' condition: '#{@operator}'")
|
|
37
38
|
false
|
|
38
39
|
end
|
|
39
40
|
end
|
|
@@ -20,20 +20,19 @@ module Kameleoon
|
|
|
20
20
|
|
|
21
21
|
def check(data)
|
|
22
22
|
return false unless data.is_a?(Kameleoon::Targeting::TargetFeatureFlagInfo) && data.variations_storage.size != 0
|
|
23
|
-
|
|
23
|
+
|
|
24
|
+
get_rules(data).any? { |rule| check_rule(data, rule) }
|
|
24
25
|
end
|
|
25
26
|
|
|
26
27
|
private def check_rule(data, rule)
|
|
27
|
-
return false if !rule.is_a?(Kameleoon::Configuration::Rule) || rule.experiment_id
|
|
28
|
+
return false if !rule.is_a?(Kameleoon::Configuration::Rule) || rule.experiment_id.nil?
|
|
28
29
|
|
|
29
|
-
if @condition_rule_id !=
|
|
30
|
-
return false if @condition_rule_id != rule.id
|
|
31
|
-
end
|
|
30
|
+
return false if !@condition_rule_id.nil? && (@condition_rule_id != rule.id)
|
|
32
31
|
|
|
33
32
|
variation = data.variations_storage.get(rule.experiment_id)
|
|
34
|
-
return false if variation
|
|
33
|
+
return false if variation.nil?
|
|
35
34
|
|
|
36
|
-
return true if @condition_variation_key
|
|
35
|
+
return true if @condition_variation_key.nil?
|
|
37
36
|
|
|
38
37
|
variation = data.data_file.variation_by_id[variation.variation_id]
|
|
39
38
|
|
|
@@ -44,10 +43,7 @@ module Kameleoon
|
|
|
44
43
|
|
|
45
44
|
def get_rules(data)
|
|
46
45
|
feature_flag = data.data_file.feature_flag_by_id[@feature_flag_id]
|
|
47
|
-
|
|
48
|
-
return feature_flag.rules
|
|
49
|
-
end
|
|
50
|
-
[]
|
|
46
|
+
feature_flag&.rules || []
|
|
51
47
|
end
|
|
52
48
|
end
|
|
53
49
|
|
|
@@ -6,7 +6,6 @@ require 'kameleoon/data/visitor_visits'
|
|
|
6
6
|
module Kameleoon
|
|
7
7
|
# @api private
|
|
8
8
|
module Targeting
|
|
9
|
-
|
|
10
9
|
class TimeElapsedSinceVisitCondition < NumberCondition
|
|
11
10
|
def initialize(json_condition)
|
|
12
11
|
count_in_millis = json_condition['countInMillis']
|
|
@@ -15,7 +14,7 @@ module Kameleoon
|
|
|
15
14
|
end
|
|
16
15
|
|
|
17
16
|
def check(data)
|
|
18
|
-
return false unless data.is_a?(Kameleoon::VisitorVisits) &&
|
|
17
|
+
return false unless data.is_a?(Kameleoon::VisitorVisits) && !@condition_value.nil?
|
|
19
18
|
|
|
20
19
|
previous_visits_count = data.previous_visit_timestamps.count
|
|
21
20
|
if previous_visits_count >= 1
|
|
@@ -6,7 +6,6 @@ require 'time'
|
|
|
6
6
|
module Kameleoon
|
|
7
7
|
# @api private
|
|
8
8
|
module Targeting
|
|
9
|
-
|
|
10
9
|
class VisitNumberTodayCondition < NumberCondition
|
|
11
10
|
def initialize(json_condition)
|
|
12
11
|
visit_count = json_condition['visitCount']
|
|
@@ -14,10 +13,11 @@ module Kameleoon
|
|
|
14
13
|
end
|
|
15
14
|
|
|
16
15
|
def check(data)
|
|
17
|
-
return false unless
|
|
16
|
+
return false unless VisitorVisits.visitor_visits?(data) && !@condition_value.nil?
|
|
17
|
+
|
|
18
18
|
number_of_visits_today = 0
|
|
19
|
-
start_of_day = (Time.new.to_date.to_time.to_f * 1000).to_i # ... * 1000
|
|
20
|
-
for timestamp in data
|
|
19
|
+
start_of_day = (Time.new.to_date.to_time.to_f * 1000).to_i # ... * 1000 to convert seconds to milliseconds
|
|
20
|
+
for timestamp in VisitorVisits.get_previous_visit_timestamps(data)
|
|
21
21
|
break if timestamp < start_of_day
|
|
22
22
|
number_of_visits_today += 1
|
|
23
23
|
end
|