kameleoon-client-ruby 3.1.1 → 3.3.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 +43 -0
- data/lib/kameleoon/configuration/data_file.rb +31 -2
- data/lib/kameleoon/configuration/rule.rb +4 -1
- data/lib/kameleoon/configuration/variation_exposition.rb +1 -1
- data/lib/kameleoon/data/browser.rb +19 -0
- data/lib/kameleoon/data/cookie.rb +14 -0
- data/lib/kameleoon/data/custom_data.rb +2 -0
- data/lib/kameleoon/data/data.rb +2 -0
- data/lib/kameleoon/data/geolocation.rb +45 -0
- data/lib/kameleoon/data/kcs_heat.rb +12 -0
- data/lib/kameleoon/data/manager/assigned_variation.rb +2 -1
- data/lib/kameleoon/data/manager/page_view_visit.rb +16 -2
- data/lib/kameleoon/data/manager/visitor.rb +71 -14
- data/lib/kameleoon/data/manager/visitor_manager.rb +23 -1
- data/lib/kameleoon/data/operating_system.rb +72 -0
- data/lib/kameleoon/data/page_view.rb +2 -2
- data/lib/kameleoon/data/visitor_visits.rb +20 -0
- data/lib/kameleoon/kameleoon_client.rb +122 -94
- data/lib/kameleoon/managers/remote_data/remote_data_manager.rb +55 -0
- data/lib/kameleoon/managers/remote_data/remote_visitor_data.rb +188 -0
- data/lib/kameleoon/managers/warehouse/warehouse_manager.rb +3 -1
- data/lib/kameleoon/network/access_token_source.rb +22 -5
- data/lib/kameleoon/network/network_manager.rb +10 -10
- data/lib/kameleoon/network/response.rb +1 -1
- data/lib/kameleoon/network/url_provider.rb +16 -12
- data/lib/kameleoon/targeting/condition.rb +24 -2
- data/lib/kameleoon/targeting/condition_factory.rb +39 -6
- data/lib/kameleoon/targeting/conditions/cookie_condition.rb +55 -0
- data/lib/kameleoon/targeting/conditions/exclusive_feature_flag_condition.rb +27 -0
- data/lib/kameleoon/targeting/conditions/geolocation_condition.rb +33 -0
- data/lib/kameleoon/targeting/conditions/kcs_heat_range_condition.rb +37 -0
- data/lib/kameleoon/targeting/conditions/number_condition.rb +38 -0
- data/lib/kameleoon/targeting/conditions/operating_system_condition.rb +27 -0
- data/lib/kameleoon/targeting/conditions/page_view_number_condition.rb +28 -0
- data/lib/kameleoon/targeting/conditions/previous_page_condition.rb +35 -0
- data/lib/kameleoon/targeting/conditions/segment_condition.rb +42 -0
- data/lib/kameleoon/targeting/conditions/target_feature_flag_condition.rb +59 -0
- data/lib/kameleoon/targeting/conditions/time_elapsed_since_visit_condition.rb +29 -0
- data/lib/kameleoon/targeting/conditions/visit_number_today_condition.rb +28 -0
- data/lib/kameleoon/targeting/conditions/visit_number_total_condition.rb +23 -0
- data/lib/kameleoon/targeting/conditions/visitor_new_return_condition.rb +34 -0
- data/lib/kameleoon/targeting/targeting_manager.rb +68 -0
- data/lib/kameleoon/types/remote_visitor_data_filter.rb +29 -0
- data/lib/kameleoon/types/variable.rb +17 -0
- data/lib/kameleoon/types/variation.rb +18 -0
- data/lib/kameleoon/utils.rb +1 -0
- data/lib/kameleoon/version.rb +1 -1
- metadata +28 -4
- data/lib/kameleoon/targeting/conditions/exclusive_experiment.rb +0 -18
- data/lib/kameleoon/targeting/conditions/target_experiment.rb +0 -45
|
@@ -20,12 +20,12 @@ module Kameleoon
|
|
|
20
20
|
@log_func = log_func
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
def get_token
|
|
23
|
+
def get_token(timeout = nil)
|
|
24
24
|
now = Time.new.to_i
|
|
25
25
|
token = @cached_token
|
|
26
|
-
return
|
|
26
|
+
return call_fetch_token(timeout) if token.nil? || token.expired?(now)
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
run_fetch_token if !@fetching && token.obsolete?(now)
|
|
29
29
|
token.value
|
|
30
30
|
end
|
|
31
31
|
|
|
@@ -35,9 +35,26 @@ module Kameleoon
|
|
|
35
35
|
|
|
36
36
|
private
|
|
37
37
|
|
|
38
|
-
def
|
|
38
|
+
def call_fetch_token(timeout)
|
|
39
39
|
@fetching = true
|
|
40
|
-
|
|
40
|
+
fetch_token(timeout)
|
|
41
|
+
rescue StandardError => e
|
|
42
|
+
@fetching = false
|
|
43
|
+
@log_func&.call("Failed to call access token fetching: #{e}")
|
|
44
|
+
nil
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def run_fetch_token
|
|
48
|
+
# setting `@fetching` here to reduce the number of requests until new thread started
|
|
49
|
+
@fetching = true
|
|
50
|
+
Thread.new { fetch_token }
|
|
51
|
+
rescue StandardError => e
|
|
52
|
+
@fetching = false
|
|
53
|
+
@log_func&.call("Failed to run access token fetching: #{e}")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def fetch_token(timeout = nil)
|
|
57
|
+
response_content = @network_manager.fetch_access_jwtoken(@client_id, @client_secret, timeout)
|
|
41
58
|
unless response_content
|
|
42
59
|
@log_func&.call('Failed to fetch access JWT')
|
|
43
60
|
return nil
|
|
@@ -45,17 +45,17 @@ module Kameleoon
|
|
|
45
45
|
make_call(request, true)
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
def get_remote_visitor_data(visitor_code, timeout = nil)
|
|
49
|
-
url = @url_provider.make_visitor_data_get_url(visitor_code)
|
|
48
|
+
def get_remote_visitor_data(visitor_code, filter, is_unique_identifier, timeout = nil)
|
|
49
|
+
url = @url_provider.make_visitor_data_get_url(visitor_code, filter, is_unique_identifier)
|
|
50
50
|
timeout = ensure_timeout(timeout)
|
|
51
51
|
request = Request.new(Method::GET, url, ContentType::JSON, timeout)
|
|
52
52
|
make_call(request, true)
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
-
def send_tracking_data(visitor_code, lines, user_agent, timeout = nil)
|
|
55
|
+
def send_tracking_data(visitor_code, lines, user_agent, is_unique_identifier, timeout = nil)
|
|
56
56
|
return if lines.nil? || lines.empty?
|
|
57
57
|
|
|
58
|
-
url = @url_provider.make_tracking_url(visitor_code)
|
|
58
|
+
url = @url_provider.make_tracking_url(visitor_code, is_unique_identifier)
|
|
59
59
|
timeout = ensure_timeout(timeout)
|
|
60
60
|
data = (lines.map(&:obtain_full_post_text_line).join("\n") || '').encode('UTF-8')
|
|
61
61
|
request = Request.new(Method::POST, url, ContentType::TEXT, timeout, user_agent: user_agent, data: data)
|
|
@@ -87,11 +87,13 @@ module Kameleoon
|
|
|
87
87
|
delay(retry_delay) if attempt.positive? && retry_delay.positive?
|
|
88
88
|
try_authorize(request) if try_access_token_auth
|
|
89
89
|
response = @sync_net_provider.make_request(request)
|
|
90
|
-
if
|
|
90
|
+
if response.success?
|
|
91
|
+
success = true
|
|
92
|
+
elsif !response.error.nil?
|
|
91
93
|
log_failure(request, "Error occurred during request: #{response.error}")
|
|
92
|
-
|
|
94
|
+
else
|
|
93
95
|
log_failure(request, "Received unexpected status code '#{response.code}'")
|
|
94
|
-
if
|
|
96
|
+
if response.code == 401 && request.access_token
|
|
95
97
|
@log_func&.call("Unexpected rejection of access token '#{request.access_token}'")
|
|
96
98
|
@access_token_source.discard_token(request.access_token)
|
|
97
99
|
if attempt == retry_limit
|
|
@@ -101,8 +103,6 @@ module Kameleoon
|
|
|
101
103
|
attempt -= 1
|
|
102
104
|
end
|
|
103
105
|
end
|
|
104
|
-
else
|
|
105
|
-
success = true
|
|
106
106
|
end
|
|
107
107
|
attempt += 1
|
|
108
108
|
end
|
|
@@ -126,7 +126,7 @@ module Kameleoon
|
|
|
126
126
|
end
|
|
127
127
|
|
|
128
128
|
def try_authorize(request)
|
|
129
|
-
token = @access_token_source.get_token
|
|
129
|
+
token = @access_token_source.get_token(request.timeout)
|
|
130
130
|
request.authorize(token)
|
|
131
131
|
end
|
|
132
132
|
end
|
|
@@ -37,25 +37,29 @@ module Kameleoon
|
|
|
37
37
|
@data_api_domain = domain if domain.is_a?(String)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
def make_tracking_url(visitor_code)
|
|
40
|
+
def make_tracking_url(visitor_code, is_unique_identifier = false)
|
|
41
41
|
params = {
|
|
42
42
|
sdkName: SDK_NAME,
|
|
43
43
|
sdkVersion: SDK_VERSION,
|
|
44
|
-
siteCode: @site_code
|
|
45
|
-
visitorCode: visitor_code
|
|
44
|
+
siteCode: @site_code
|
|
46
45
|
}
|
|
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
|
|
|
50
|
-
def make_visitor_data_get_url(visitor_code)
|
|
51
|
-
params = {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
50
|
+
def make_visitor_data_get_url(visitor_code, filter, is_unique_identifier = false)
|
|
51
|
+
params = { siteCode: @site_code }
|
|
52
|
+
params[is_unique_identifier ? :mappingValue : :visitorCode] = visitor_code
|
|
53
|
+
params[:maxNumberPreviousVisits] = filter.previous_visit_amount
|
|
54
|
+
params[:version] = 0
|
|
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
|
|
62
|
+
params[:staticData] = true if filter.device || filter.browser || filter.operating_system
|
|
59
63
|
"https://#{@data_api_domain}#{VISITOR_DATA_PATH}?#{UriHelper.encode_query(params)}"
|
|
60
64
|
end
|
|
61
65
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
require 'kameleoon/exceptions'
|
|
3
4
|
|
|
4
5
|
module Kameleoon
|
|
@@ -6,15 +7,27 @@ module Kameleoon
|
|
|
6
7
|
module Targeting
|
|
7
8
|
module ConditionType
|
|
8
9
|
CUSTOM_DATUM = 'CUSTOM_DATUM'
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
TARGET_FEATURE_FLAG = 'TARGET_FEATURE_FLAG'
|
|
11
|
+
EXCLUSIVE_FEATURE_FLAG = 'EXCLUSIVE_FEATURE_FLAG'
|
|
11
12
|
PAGE_URL = 'PAGE_URL'
|
|
13
|
+
PAGE_VIEWS = 'PAGE_VIEWS'
|
|
14
|
+
PREVIOUS_PAGE = 'PREVIOUS_PAGE'
|
|
12
15
|
PAGE_TITLE = 'PAGE_TITLE'
|
|
13
16
|
VISITOR_CODE = 'VISITOR_CODE'
|
|
14
17
|
DEVICE_TYPE = 'DEVICE_TYPE'
|
|
15
18
|
BROWSER = 'BROWSER'
|
|
16
19
|
SDK_LANGUAGE = 'SDK_LANGUAGE'
|
|
17
20
|
CONVERSIONS = 'CONVERSIONS'
|
|
21
|
+
COOKIE = 'COOKIE'
|
|
22
|
+
OPERATING_SYSTEM = 'OPERATING_SYSTEM'
|
|
23
|
+
GEOLOCATION = 'GEOLOCATION'
|
|
24
|
+
SEGMENT = 'SEGMENT'
|
|
25
|
+
FIRST_VISIT = 'FIRST_VISIT'
|
|
26
|
+
LAST_VISIT = 'LAST_VISIT'
|
|
27
|
+
VISITS = 'VISITS'
|
|
28
|
+
SAME_DAY_VISITS = 'SAME_DAY_VISITS'
|
|
29
|
+
NEW_VISITORS = 'NEW_VISITORS'
|
|
30
|
+
HEAT_SLICE = 'HEAT_SLICE'
|
|
18
31
|
end
|
|
19
32
|
|
|
20
33
|
module Operator
|
|
@@ -52,6 +65,15 @@ module Kameleoon
|
|
|
52
65
|
def get_last_targeting_data(list_data, data_type)
|
|
53
66
|
list_data.select { |data| data.instance == data_type }.last
|
|
54
67
|
end
|
|
68
|
+
|
|
69
|
+
def is_regex_match(value, pattern)
|
|
70
|
+
begin
|
|
71
|
+
return pattern.match? value
|
|
72
|
+
rescue StandardError => e
|
|
73
|
+
log "Failed to match with regex (targeting): #{e}"
|
|
74
|
+
end
|
|
75
|
+
false
|
|
76
|
+
end
|
|
55
77
|
end
|
|
56
78
|
end
|
|
57
79
|
end
|
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
require_relative 'conditions/custom_datum'
|
|
2
|
-
require_relative 'conditions/
|
|
3
|
-
require_relative 'conditions/
|
|
2
|
+
require_relative 'conditions/target_feature_flag_condition'
|
|
3
|
+
require_relative 'conditions/exclusive_feature_flag_condition'
|
|
4
4
|
require_relative 'conditions/page_title_condition'
|
|
5
5
|
require_relative 'conditions/page_url_condition'
|
|
6
|
+
require_relative 'conditions/page_view_number_condition'
|
|
7
|
+
require_relative 'conditions/previous_page_condition'
|
|
6
8
|
require_relative 'conditions/visitor_code_condition'
|
|
7
9
|
require_relative 'conditions/device_condition'
|
|
8
10
|
require_relative 'conditions/conversion_condition'
|
|
9
11
|
require_relative 'conditions/browser_condition'
|
|
10
12
|
require_relative 'conditions/sdk_language_condition'
|
|
13
|
+
require_relative 'conditions/geolocation_condition'
|
|
14
|
+
require_relative 'conditions/kcs_heat_range_condition'
|
|
15
|
+
require_relative 'conditions/operating_system_condition'
|
|
16
|
+
require_relative 'conditions/cookie_condition'
|
|
17
|
+
require_relative 'conditions/segment_condition'
|
|
18
|
+
require_relative 'conditions/visit_number_total_condition'
|
|
19
|
+
require_relative 'conditions/visit_number_today_condition'
|
|
20
|
+
require_relative 'conditions/visitor_new_return_condition'
|
|
21
|
+
require_relative 'conditions/time_elapsed_since_visit_condition'
|
|
11
22
|
require_relative 'conditions/unknown_condition'
|
|
12
23
|
|
|
13
24
|
module Kameleoon
|
|
@@ -19,12 +30,16 @@ module Kameleoon
|
|
|
19
30
|
case condition_json['targetingType']
|
|
20
31
|
when ConditionType::CUSTOM_DATUM
|
|
21
32
|
CustomDatum.new(condition_json)
|
|
22
|
-
when ConditionType::
|
|
23
|
-
|
|
24
|
-
when ConditionType::
|
|
25
|
-
|
|
33
|
+
when ConditionType::TARGET_FEATURE_FLAG
|
|
34
|
+
TargetFeatureFlagCondition.new(condition_json)
|
|
35
|
+
when ConditionType::EXCLUSIVE_FEATURE_FLAG
|
|
36
|
+
ExclusiveFeatureFlagCondition.new(condition_json)
|
|
26
37
|
when ConditionType::PAGE_URL
|
|
27
38
|
PageUrlCondition.new(condition_json)
|
|
39
|
+
when ConditionType::PAGE_VIEWS
|
|
40
|
+
PageViewNumberCondition.new(condition_json)
|
|
41
|
+
when ConditionType::PREVIOUS_PAGE
|
|
42
|
+
PreviousPageCondition.new(condition_json)
|
|
28
43
|
when ConditionType::PAGE_TITLE
|
|
29
44
|
PageTitleCondition.new(condition_json)
|
|
30
45
|
when ConditionType::VISITOR_CODE
|
|
@@ -37,6 +52,24 @@ module Kameleoon
|
|
|
37
52
|
BrowserCondition.new(condition_json)
|
|
38
53
|
when ConditionType::SDK_LANGUAGE
|
|
39
54
|
SdkLanguageCondition.new(condition_json)
|
|
55
|
+
when ConditionType::GEOLOCATION
|
|
56
|
+
GeolocationCondition.new(condition_json)
|
|
57
|
+
when ConditionType::OPERATING_SYSTEM
|
|
58
|
+
OperatingSystemCondition.new(condition_json)
|
|
59
|
+
when ConditionType::COOKIE
|
|
60
|
+
CookieCondition.new(condition_json)
|
|
61
|
+
when ConditionType::SEGMENT
|
|
62
|
+
SegmentCondition.new(condition_json)
|
|
63
|
+
when ConditionType::VISITS
|
|
64
|
+
VisitNumberTotalCondition.new(condition_json)
|
|
65
|
+
when ConditionType::SAME_DAY_VISITS
|
|
66
|
+
VisitNumberTodayCondition.new(condition_json)
|
|
67
|
+
when ConditionType::NEW_VISITORS
|
|
68
|
+
VisitorNewReturnCondition.new(condition_json)
|
|
69
|
+
when ConditionType::FIRST_VISIT, ConditionType::LAST_VISIT
|
|
70
|
+
TimeElapsedSinceVisitCondition.new(condition_json)
|
|
71
|
+
when ConditionType::HEAT_SLICE
|
|
72
|
+
KcsHeatRangeCondition.new(condition_json)
|
|
40
73
|
else
|
|
41
74
|
UnknownCondition.new(condition_json)
|
|
42
75
|
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'kameleoon/data/cookie'
|
|
4
|
+
require 'kameleoon/version'
|
|
5
|
+
|
|
6
|
+
module Kameleoon
|
|
7
|
+
# @api private
|
|
8
|
+
module Targeting
|
|
9
|
+
|
|
10
|
+
class CookieCondition < Condition
|
|
11
|
+
def initialize(json_condition)
|
|
12
|
+
super(json_condition)
|
|
13
|
+
@condition_name = json_condition['name']
|
|
14
|
+
@name_match_type = json_condition['nameMatchType']
|
|
15
|
+
@condition_value = json_condition['value']
|
|
16
|
+
@value_match_type = json_condition['valueMatchType']
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def check(cookie)
|
|
20
|
+
cookie.is_a?(Kameleoon::Cookie) && check_values(select_values(cookie))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def select_values(cookie)
|
|
26
|
+
case @name_match_type
|
|
27
|
+
when Kameleoon::Targeting::Operator::EXACT
|
|
28
|
+
value = cookie.cookies[@condition_name]
|
|
29
|
+
return value != nil ? [value] : []
|
|
30
|
+
when Kameleoon::Targeting::Operator::CONTAINS
|
|
31
|
+
return cookie.cookies.select{|key| key.include? @condition_name}.values()
|
|
32
|
+
when Kameleoon::Targeting::Operator::REGULAR_EXPRESSION
|
|
33
|
+
return cookie.cookies.select{|key| is_regex_match(@condition_name, key)}.values()
|
|
34
|
+
else
|
|
35
|
+
log "Unexpected comparing operation for Cookie condition (name): #{@name_match_type}"
|
|
36
|
+
end
|
|
37
|
+
[]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def check_values(values)
|
|
41
|
+
case @value_match_type
|
|
42
|
+
when Kameleoon::Targeting::Operator::EXACT
|
|
43
|
+
return values.any? {|value| value == @condition_value}
|
|
44
|
+
when Kameleoon::Targeting::Operator::CONTAINS
|
|
45
|
+
return values.any? {|value| value.include? @condition_value}
|
|
46
|
+
when Kameleoon::Targeting::Operator::REGULAR_EXPRESSION
|
|
47
|
+
return values.any? {|value| is_regex_match(@condition_value, value)}
|
|
48
|
+
else
|
|
49
|
+
log "Unexpected comparing operation for Cookie condition (name): #{@name_match_type}"
|
|
50
|
+
return false
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'kameleoon/targeting/condition'
|
|
4
|
+
|
|
5
|
+
module Kameleoon
|
|
6
|
+
# @api private
|
|
7
|
+
module Targeting
|
|
8
|
+
# ExclusiveExperiment represents an instance of Exclusive Experiment condition in user account
|
|
9
|
+
class ExclusiveFeatureFlagCondition < Condition
|
|
10
|
+
def check(data)
|
|
11
|
+
return false unless data.is_a?(ExclusiveFeatureFlagInfo)
|
|
12
|
+
|
|
13
|
+
size = data.variations_storage&.size || 0
|
|
14
|
+
size.zero? || (size == 1 && !data.variations_storage.get(data.current_experiment_id).nil?)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class ExclusiveFeatureFlagInfo
|
|
19
|
+
attr_reader :current_experiment_id, :variations_storage
|
|
20
|
+
|
|
21
|
+
def initialize(current_experiment_id, variations_storage)
|
|
22
|
+
@current_experiment_id = current_experiment_id
|
|
23
|
+
@variations_storage = variations_storage
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'kameleoon/data/geolocation'
|
|
4
|
+
require 'kameleoon/version'
|
|
5
|
+
|
|
6
|
+
module Kameleoon
|
|
7
|
+
# @api private
|
|
8
|
+
module Targeting
|
|
9
|
+
|
|
10
|
+
class GeolocationCondition < Condition
|
|
11
|
+
def initialize(json_condition)
|
|
12
|
+
super(json_condition)
|
|
13
|
+
@country = json_condition['country']
|
|
14
|
+
@region = json_condition['region']
|
|
15
|
+
@city = json_condition['city']
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def check(geolocation)
|
|
19
|
+
geolocation.is_a?(Kameleoon::Geolocation) && check_targeting(geolocation)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def check_targeting(geolocation)
|
|
25
|
+
return false if !@country.is_a?(String) || !@country.casecmp?(geolocation.country)
|
|
26
|
+
return false if @region.is_a?(String) && !@region.casecmp?(geolocation.region)
|
|
27
|
+
return false if @city.is_a?(String) && !@city.casecmp?(geolocation.city)
|
|
28
|
+
|
|
29
|
+
true
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
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
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'kameleoon/data/cookie'
|
|
4
|
+
require 'kameleoon/version'
|
|
5
|
+
|
|
6
|
+
module Kameleoon
|
|
7
|
+
# @api private
|
|
8
|
+
module Targeting
|
|
9
|
+
|
|
10
|
+
class NumberCondition < Condition
|
|
11
|
+
def initialize(json_condition, value)
|
|
12
|
+
super(json_condition)
|
|
13
|
+
@match_type = json_condition['matchType']
|
|
14
|
+
@condition_value = value
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def check(value)
|
|
18
|
+
check_targeting(value)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
protected
|
|
22
|
+
|
|
23
|
+
def check_targeting(value)
|
|
24
|
+
case @match_type
|
|
25
|
+
when Kameleoon::Targeting::Operator::EQUAL
|
|
26
|
+
return value == @condition_value
|
|
27
|
+
when Kameleoon::Targeting::Operator::GREATER
|
|
28
|
+
return @condition_value != nil && value > @condition_value
|
|
29
|
+
when Kameleoon::Targeting::Operator::LOWER
|
|
30
|
+
return @condition_value != nil && value < @condition_value
|
|
31
|
+
else
|
|
32
|
+
log "Unexpected comparing operation for #{type} condition: #{@match_type}"
|
|
33
|
+
end
|
|
34
|
+
false
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'kameleoon/data/operating_system'
|
|
4
|
+
require 'kameleoon/version'
|
|
5
|
+
|
|
6
|
+
module Kameleoon
|
|
7
|
+
# @api private
|
|
8
|
+
module Targeting
|
|
9
|
+
|
|
10
|
+
class OperatingSystemCondition < Condition
|
|
11
|
+
def initialize(json_condition)
|
|
12
|
+
super(json_condition)
|
|
13
|
+
@os_type = Kameleoon::OperatingSystemType.from_name(json_condition['os'])
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def check(operating_system)
|
|
17
|
+
operating_system.is_a?(Kameleoon::OperatingSystem) && check_targeting(operating_system)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def check_targeting(operating_system)
|
|
23
|
+
@os_type != nil && operating_system.os_type == @os_type
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'kameleoon/targeting/conditions/number_condition'
|
|
4
|
+
require 'kameleoon/data/manager/page_view_visit'
|
|
5
|
+
|
|
6
|
+
module Kameleoon
|
|
7
|
+
# @api private
|
|
8
|
+
module Targeting
|
|
9
|
+
|
|
10
|
+
class PageViewNumberCondition < NumberCondition
|
|
11
|
+
def initialize(json_condition)
|
|
12
|
+
page_count = json_condition['pageCount']
|
|
13
|
+
super(json_condition, page_count)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def check(page_view_visit_storage)
|
|
17
|
+
return false unless page_view_visit_storage.is_a?(Kameleoon::DataManager::DataMapStorage)
|
|
18
|
+
|
|
19
|
+
count = 0
|
|
20
|
+
page_view_visit_storage.enumerate do |visit|
|
|
21
|
+
return false unless visit.is_a?(Kameleoon::DataManager::PageViewVisit)
|
|
22
|
+
count += visit.count
|
|
23
|
+
end
|
|
24
|
+
check_targeting(count)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'kameleoon/targeting/conditions/string_value_condition'
|
|
4
|
+
require 'kameleoon/data/manager/page_view_visit'
|
|
5
|
+
|
|
6
|
+
module Kameleoon
|
|
7
|
+
# @api private
|
|
8
|
+
module Targeting
|
|
9
|
+
|
|
10
|
+
class PreviousPageCondition < StringValueCondition
|
|
11
|
+
def initialize(json_condition)
|
|
12
|
+
super(json_condition, json_condition['url'])
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def check(page_view_visit_storage)
|
|
16
|
+
is_targeted = false
|
|
17
|
+
if page_view_visit_storage.is_a?(Kameleoon::DataManager::DataMapStorage)
|
|
18
|
+
most_recent_visit = nil
|
|
19
|
+
second_most_recent_visit = nil
|
|
20
|
+
page_view_visit_storage.enumerate do |visit|
|
|
21
|
+
return false unless visit.is_a?(Kameleoon::DataManager::PageViewVisit)
|
|
22
|
+
if most_recent_visit == nil || visit.last_timestamp > most_recent_visit.last_timestamp
|
|
23
|
+
second_most_recent_visit = most_recent_visit
|
|
24
|
+
most_recent_visit = visit
|
|
25
|
+
elsif second_most_recent_visit == nil || visit.last_timestamp > second_most_recent_visit.last_timestamp
|
|
26
|
+
second_most_recent_visit = visit
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
is_targeted = second_most_recent_visit != nil && check_targeting(second_most_recent_visit.page_view.url)
|
|
30
|
+
end
|
|
31
|
+
is_targeted
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'kameleoon/data/geolocation'
|
|
4
|
+
require 'kameleoon/version'
|
|
5
|
+
|
|
6
|
+
module Kameleoon
|
|
7
|
+
# @api private
|
|
8
|
+
module Targeting
|
|
9
|
+
|
|
10
|
+
class SegmentCondition < Condition
|
|
11
|
+
def initialize(json_condition)
|
|
12
|
+
super(json_condition)
|
|
13
|
+
@segment_id = json_condition["segmentId"]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def check(data)
|
|
17
|
+
data.is_a?(SegmentInfo) && check_targeting(data)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def check_targeting(segment_info)
|
|
23
|
+
rule = segment_info.data_file.rule_by_segment_id[@segment_id]
|
|
24
|
+
return false unless rule.is_a?(Kameleoon::Configuration::Rule)
|
|
25
|
+
rule.targeting_segment.check_tree(->(type) {segment_info.condition_data(type)})
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class SegmentInfo
|
|
30
|
+
attr_reader :data_file
|
|
31
|
+
|
|
32
|
+
def initialize(data_file, condition_data)
|
|
33
|
+
@data_file = data_file
|
|
34
|
+
@condition_data = condition_data
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def condition_data(type)
|
|
38
|
+
@condition_data.call(type)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'kameleoon/targeting/condition'
|
|
4
|
+
require 'kameleoon/exceptions'
|
|
5
|
+
|
|
6
|
+
module Kameleoon
|
|
7
|
+
# @api private
|
|
8
|
+
module Targeting
|
|
9
|
+
# TargetExperiment represents an instance of Experiment condition in user account
|
|
10
|
+
class TargetFeatureFlagCondition < Condition
|
|
11
|
+
include Kameleoon::Exception
|
|
12
|
+
|
|
13
|
+
def initialize(json_condition)
|
|
14
|
+
super(json_condition)
|
|
15
|
+
|
|
16
|
+
@feature_flag_id = json_condition['featureFlagId']
|
|
17
|
+
@condition_variation_key = json_condition['variationKey']
|
|
18
|
+
@condition_rule_id = json_condition['ruleId']
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def check(data)
|
|
22
|
+
return false unless data.is_a?(Kameleoon::Targeting::TargetFeatureFlagInfo) && data.variations_storage.size != 0
|
|
23
|
+
|
|
24
|
+
get_rules(data).any? { |rule| check_rule(data, rule) }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private def check_rule(data, rule)
|
|
28
|
+
return false if !rule.is_a?(Kameleoon::Configuration::Rule) || rule.experiment_id.nil?
|
|
29
|
+
|
|
30
|
+
return false if !@condition_rule_id.nil? && (@condition_rule_id != rule.id)
|
|
31
|
+
|
|
32
|
+
variation = data.variations_storage.get(rule.experiment_id)
|
|
33
|
+
return false if variation.nil?
|
|
34
|
+
|
|
35
|
+
return true if @condition_variation_key.nil?
|
|
36
|
+
|
|
37
|
+
variation = data.data_file.variation_by_id[variation.variation_id]
|
|
38
|
+
|
|
39
|
+
return false unless variation.is_a?(Kameleoon::Configuration::VariationByExposition)
|
|
40
|
+
|
|
41
|
+
variation.variation_key == @condition_variation_key
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def get_rules(data)
|
|
45
|
+
feature_flag = data.data_file.feature_flag_by_id[@feature_flag_id]
|
|
46
|
+
feature_flag&.rules || []
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
class TargetFeatureFlagInfo
|
|
51
|
+
attr_reader :data_file, :variations_storage
|
|
52
|
+
|
|
53
|
+
def initialize(data_file, variations_storage)
|
|
54
|
+
@data_file = data_file
|
|
55
|
+
@variations_storage = variations_storage
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|