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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/lib/kameleoon/configuration/custom_data_info.rb +43 -0
  3. data/lib/kameleoon/configuration/data_file.rb +31 -2
  4. data/lib/kameleoon/configuration/rule.rb +4 -1
  5. data/lib/kameleoon/configuration/variation_exposition.rb +1 -1
  6. data/lib/kameleoon/data/browser.rb +19 -0
  7. data/lib/kameleoon/data/cookie.rb +14 -0
  8. data/lib/kameleoon/data/custom_data.rb +2 -0
  9. data/lib/kameleoon/data/data.rb +2 -0
  10. data/lib/kameleoon/data/geolocation.rb +45 -0
  11. data/lib/kameleoon/data/kcs_heat.rb +12 -0
  12. data/lib/kameleoon/data/manager/assigned_variation.rb +2 -1
  13. data/lib/kameleoon/data/manager/page_view_visit.rb +16 -2
  14. data/lib/kameleoon/data/manager/visitor.rb +71 -14
  15. data/lib/kameleoon/data/manager/visitor_manager.rb +23 -1
  16. data/lib/kameleoon/data/operating_system.rb +72 -0
  17. data/lib/kameleoon/data/page_view.rb +2 -2
  18. data/lib/kameleoon/data/visitor_visits.rb +20 -0
  19. data/lib/kameleoon/kameleoon_client.rb +122 -94
  20. data/lib/kameleoon/managers/remote_data/remote_data_manager.rb +55 -0
  21. data/lib/kameleoon/managers/remote_data/remote_visitor_data.rb +188 -0
  22. data/lib/kameleoon/managers/warehouse/warehouse_manager.rb +3 -1
  23. data/lib/kameleoon/network/access_token_source.rb +22 -5
  24. data/lib/kameleoon/network/network_manager.rb +10 -10
  25. data/lib/kameleoon/network/response.rb +1 -1
  26. data/lib/kameleoon/network/url_provider.rb +16 -12
  27. data/lib/kameleoon/targeting/condition.rb +24 -2
  28. data/lib/kameleoon/targeting/condition_factory.rb +39 -6
  29. data/lib/kameleoon/targeting/conditions/cookie_condition.rb +55 -0
  30. data/lib/kameleoon/targeting/conditions/exclusive_feature_flag_condition.rb +27 -0
  31. data/lib/kameleoon/targeting/conditions/geolocation_condition.rb +33 -0
  32. data/lib/kameleoon/targeting/conditions/kcs_heat_range_condition.rb +37 -0
  33. data/lib/kameleoon/targeting/conditions/number_condition.rb +38 -0
  34. data/lib/kameleoon/targeting/conditions/operating_system_condition.rb +27 -0
  35. data/lib/kameleoon/targeting/conditions/page_view_number_condition.rb +28 -0
  36. data/lib/kameleoon/targeting/conditions/previous_page_condition.rb +35 -0
  37. data/lib/kameleoon/targeting/conditions/segment_condition.rb +42 -0
  38. data/lib/kameleoon/targeting/conditions/target_feature_flag_condition.rb +59 -0
  39. data/lib/kameleoon/targeting/conditions/time_elapsed_since_visit_condition.rb +29 -0
  40. data/lib/kameleoon/targeting/conditions/visit_number_today_condition.rb +28 -0
  41. data/lib/kameleoon/targeting/conditions/visit_number_total_condition.rb +23 -0
  42. data/lib/kameleoon/targeting/conditions/visitor_new_return_condition.rb +34 -0
  43. data/lib/kameleoon/targeting/targeting_manager.rb +68 -0
  44. data/lib/kameleoon/types/remote_visitor_data_filter.rb +29 -0
  45. data/lib/kameleoon/types/variable.rb +17 -0
  46. data/lib/kameleoon/types/variation.rb +18 -0
  47. data/lib/kameleoon/utils.rb +1 -0
  48. data/lib/kameleoon/version.rb +1 -1
  49. metadata +28 -4
  50. data/lib/kameleoon/targeting/conditions/exclusive_experiment.rb +0 -18
  51. 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 fetch_token if token.nil? || token.expired?(now)
26
+ return call_fetch_token(timeout) if token.nil? || token.expired?(now)
27
27
 
28
- Thread.new { fetch_token } if !@fetching && token.obsolete?(now)
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 fetch_token
38
+ def call_fetch_token(timeout)
39
39
  @fetching = true
40
- response_content = @network_manager.fetch_access_jwtoken(@client_id, @client_secret)
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 !response.error.nil?
90
+ if response.success?
91
+ success = true
92
+ elsif !response.error.nil?
91
93
  log_failure(request, "Error occurred during request: #{response.error}")
92
- elsif response.code / 100 != 2
94
+ else
93
95
  log_failure(request, "Received unexpected status code '#{response.code}'")
94
- if ((response.code == 401) || (response.code == 403)) && request.access_token
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
@@ -15,7 +15,7 @@ module Kameleoon
15
15
  end
16
16
 
17
17
  def success?
18
- @error.nil? && (@code / 100 == 2)
18
+ @error.nil? && ((@code / 100 == 2) || (@code == 403))
19
19
  end
20
20
  end
21
21
  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
- siteCode: @site_code,
53
- visitorCode: visitor_code,
54
- currentVisit: true,
55
- maxNumberPreviousVisits: 1,
56
- customData: true,
57
- version: 0
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
- TARGET_EXPERIMENT = 'TARGET_EXPERIMENT'
10
- EXCLUSIVE_EXPERIMENT = 'EXCLUSIVE_EXPERIMENT'
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/target_experiment'
3
- require_relative 'conditions/exclusive_experiment'
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::TARGET_EXPERIMENT
23
- TargetExperiment.new(condition_json)
24
- when ConditionType::EXCLUSIVE_EXPERIMENT
25
- ExclusiveExperiment.new(condition_json)
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