kameleoon-client-ruby 3.1.1 → 3.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|