kameleoon-client-ruby 3.1.1 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) 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 +30 -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/manager/assigned_variation.rb +2 -1
  12. data/lib/kameleoon/data/manager/page_view_visit.rb +16 -2
  13. data/lib/kameleoon/data/manager/visitor.rb +64 -14
  14. data/lib/kameleoon/data/manager/visitor_manager.rb +23 -1
  15. data/lib/kameleoon/data/operating_system.rb +72 -0
  16. data/lib/kameleoon/data/page_view.rb +2 -2
  17. data/lib/kameleoon/data/visitor_visits.rb +13 -0
  18. data/lib/kameleoon/kameleoon_client.rb +75 -94
  19. data/lib/kameleoon/managers/remote_data/remote_data_manager.rb +57 -0
  20. data/lib/kameleoon/managers/remote_data/remote_visitor_data.rb +162 -0
  21. data/lib/kameleoon/managers/warehouse/warehouse_manager.rb +3 -1
  22. data/lib/kameleoon/network/access_token_source.rb +22 -5
  23. data/lib/kameleoon/network/network_manager.rb +10 -10
  24. data/lib/kameleoon/network/response.rb +1 -1
  25. data/lib/kameleoon/network/url_provider.rb +15 -12
  26. data/lib/kameleoon/targeting/condition.rb +22 -2
  27. data/lib/kameleoon/targeting/condition_factory.rb +36 -6
  28. data/lib/kameleoon/targeting/conditions/cookie_condition.rb +55 -0
  29. data/lib/kameleoon/targeting/conditions/exclusive_feature_flag_condition.rb +27 -0
  30. data/lib/kameleoon/targeting/conditions/geolocation_condition.rb +33 -0
  31. data/lib/kameleoon/targeting/conditions/number_condition.rb +38 -0
  32. data/lib/kameleoon/targeting/conditions/operating_system_condition.rb +27 -0
  33. data/lib/kameleoon/targeting/conditions/page_view_number_condition.rb +28 -0
  34. data/lib/kameleoon/targeting/conditions/previous_page_condition.rb +35 -0
  35. data/lib/kameleoon/targeting/conditions/segment_condition.rb +42 -0
  36. data/lib/kameleoon/targeting/conditions/target_feature_flag_condition.rb +63 -0
  37. data/lib/kameleoon/targeting/conditions/time_elapsed_since_visit_condition.rb +30 -0
  38. data/lib/kameleoon/targeting/conditions/visit_number_today_condition.rb +28 -0
  39. data/lib/kameleoon/targeting/conditions/visit_number_total_condition.rb +21 -0
  40. data/lib/kameleoon/targeting/conditions/visitor_new_return_condition.rb +33 -0
  41. data/lib/kameleoon/targeting/targeting_manager.rb +66 -0
  42. data/lib/kameleoon/types/remote_visitor_data_filter.rb +24 -0
  43. data/lib/kameleoon/utils.rb +1 -0
  44. data/lib/kameleoon/version.rb +1 -1
  45. metadata +24 -4
  46. data/lib/kameleoon/targeting/conditions/exclusive_experiment.rb +0 -18
  47. data/lib/kameleoon/targeting/conditions/target_experiment.rb +0 -45
@@ -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,28 @@ 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[:currentVisit] = filter.current_visit if filter.current_visit
56
+ params[:customData] = filter.custom_data if filter.custom_data
57
+ params[:conversion] = filter.conversions if filter.conversions
58
+ params[:geolocation] = filter.geolocation if filter.geolocation
59
+ params[:experiment] = filter.experiments if filter.experiments
60
+ params[:page] = filter.page_views if filter.page_views
61
+ params[:staticData] = true if filter.device || filter.browser || filter.operating_system
59
62
  "https://#{@data_api_domain}#{VISITOR_DATA_PATH}?#{UriHelper.encode_query(params)}"
60
63
  end
61
64
 
@@ -6,15 +6,26 @@ module Kameleoon
6
6
  module Targeting
7
7
  module ConditionType
8
8
  CUSTOM_DATUM = 'CUSTOM_DATUM'
9
- TARGET_EXPERIMENT = 'TARGET_EXPERIMENT'
10
- EXCLUSIVE_EXPERIMENT = 'EXCLUSIVE_EXPERIMENT'
9
+ TARGET_FEATURE_FLAG = 'TARGET_FEATURE_FLAG'
10
+ EXCLUSIVE_FEATURE_FLAG = 'EXCLUSIVE_FEATURE_FLAG'
11
11
  PAGE_URL = 'PAGE_URL'
12
+ PAGE_VIEWS = 'PAGE_VIEWS'
13
+ PREVIOUS_PAGE = 'PREVIOUS_PAGE'
12
14
  PAGE_TITLE = 'PAGE_TITLE'
13
15
  VISITOR_CODE = 'VISITOR_CODE'
14
16
  DEVICE_TYPE = 'DEVICE_TYPE'
15
17
  BROWSER = 'BROWSER'
16
18
  SDK_LANGUAGE = 'SDK_LANGUAGE'
17
19
  CONVERSIONS = 'CONVERSIONS'
20
+ COOKIE = 'COOKIE'
21
+ OPERATING_SYSTEM = 'OPERATING_SYSTEM'
22
+ GEOLOCATION = 'GEOLOCATION'
23
+ SEGMENT = 'SEGMENT'
24
+ FIRST_VISIT = 'FIRST_VISIT'
25
+ LAST_VISIT = 'LAST_VISIT'
26
+ VISITS = 'VISITS'
27
+ SAME_DAY_VISITS = 'SAME_DAY_VISITS'
28
+ NEW_VISITORS = 'NEW_VISITORS'
18
29
  end
19
30
 
20
31
  module Operator
@@ -52,6 +63,15 @@ module Kameleoon
52
63
  def get_last_targeting_data(list_data, data_type)
53
64
  list_data.select { |data| data.instance == data_type }.last
54
65
  end
66
+
67
+ def is_regex_match(value, pattern)
68
+ begin
69
+ return pattern.match? value
70
+ rescue StandardError => e
71
+ log "Failed to match with regex (targeting): #{e}"
72
+ end
73
+ false
74
+ end
55
75
  end
56
76
  end
57
77
  end
@@ -1,13 +1,23 @@
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/operating_system_condition'
15
+ require_relative 'conditions/cookie_condition'
16
+ require_relative 'conditions/segment_condition'
17
+ require_relative 'conditions/visit_number_total_condition'
18
+ require_relative 'conditions/visit_number_today_condition'
19
+ require_relative 'conditions/visitor_new_return_condition'
20
+ require_relative 'conditions/time_elapsed_since_visit_condition'
11
21
  require_relative 'conditions/unknown_condition'
12
22
 
13
23
  module Kameleoon
@@ -19,12 +29,16 @@ module Kameleoon
19
29
  case condition_json['targetingType']
20
30
  when ConditionType::CUSTOM_DATUM
21
31
  CustomDatum.new(condition_json)
22
- when ConditionType::TARGET_EXPERIMENT
23
- TargetExperiment.new(condition_json)
24
- when ConditionType::EXCLUSIVE_EXPERIMENT
25
- ExclusiveExperiment.new(condition_json)
32
+ when ConditionType::TARGET_FEATURE_FLAG
33
+ TargetFeatureFlagCondition.new(condition_json)
34
+ when ConditionType::EXCLUSIVE_FEATURE_FLAG
35
+ ExclusiveFeatureFlagCondition.new(condition_json)
26
36
  when ConditionType::PAGE_URL
27
37
  PageUrlCondition.new(condition_json)
38
+ when ConditionType::PAGE_VIEWS
39
+ PageViewNumberCondition.new(condition_json)
40
+ when ConditionType::PREVIOUS_PAGE
41
+ PreviousPageCondition.new(condition_json)
28
42
  when ConditionType::PAGE_TITLE
29
43
  PageTitleCondition.new(condition_json)
30
44
  when ConditionType::VISITOR_CODE
@@ -37,6 +51,22 @@ module Kameleoon
37
51
  BrowserCondition.new(condition_json)
38
52
  when ConditionType::SDK_LANGUAGE
39
53
  SdkLanguageCondition.new(condition_json)
54
+ when ConditionType::GEOLOCATION
55
+ GeolocationCondition.new(condition_json)
56
+ when ConditionType::OPERATING_SYSTEM
57
+ OperatingSystemCondition.new(condition_json)
58
+ when ConditionType::COOKIE
59
+ CookieCondition.new(condition_json)
60
+ when ConditionType::SEGMENT
61
+ SegmentCondition.new(condition_json)
62
+ when ConditionType::VISITS
63
+ VisitNumberTotalCondition.new(condition_json)
64
+ when ConditionType::SAME_DAY_VISITS
65
+ VisitNumberTodayCondition.new(condition_json)
66
+ when ConditionType::NEW_VISITORS
67
+ VisitorNewReturnCondition.new(condition_json)
68
+ when ConditionType::FIRST_VISIT, ConditionType::LAST_VISIT
69
+ TimeElapsedSinceVisitCondition.new(condition_json)
40
70
  else
41
71
  UnknownCondition.new(condition_json)
42
72
  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,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,63 @@
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
+ get_rules(data).any? {|rule| check_rule(data, rule)}
24
+ end
25
+
26
+ private def check_rule(data, rule)
27
+ return false if !rule.is_a?(Kameleoon::Configuration::Rule) || rule.experiment_id == nil
28
+
29
+ if @condition_rule_id != nil
30
+ return false if @condition_rule_id != rule.id
31
+ end
32
+
33
+ variation = data.variations_storage.get(rule.experiment_id)
34
+ return false if variation == nil
35
+
36
+ return true if @condition_variation_key == nil
37
+
38
+ variation = data.data_file.variation_by_id[variation.variation_id]
39
+
40
+ return false unless variation.is_a?(Kameleoon::Configuration::VariationByExposition)
41
+
42
+ variation.variation_key == @condition_variation_key
43
+ end
44
+
45
+ def get_rules(data)
46
+ feature_flag = data.data_file.feature_flag_by_id[@feature_flag_id]
47
+ if feature_flag != nil
48
+ return feature_flag.rules
49
+ end
50
+ []
51
+ end
52
+ end
53
+
54
+ class TargetFeatureFlagInfo
55
+ attr_reader :data_file, :variations_storage
56
+
57
+ def initialize(data_file, variations_storage)
58
+ @data_file = data_file
59
+ @variations_storage = variations_storage
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kameleoon/targeting/conditions/number_condition'
4
+ require 'kameleoon/data/visitor_visits'
5
+
6
+ module Kameleoon
7
+ # @api private
8
+ module Targeting
9
+
10
+ class TimeElapsedSinceVisitCondition < NumberCondition
11
+ def initialize(json_condition)
12
+ count_in_millis = json_condition['countInMillis']
13
+ super(json_condition, count_in_millis)
14
+ @is_first_visit = @type == Kameleoon::Targeting::ConditionType::FIRST_VISIT
15
+ end
16
+
17
+ def check(data)
18
+ return false unless data.is_a?(Kameleoon::VisitorVisits) && @condition_value != nil
19
+
20
+ previous_visits_count = data.previous_visit_timestamps.count
21
+ if previous_visits_count >= 1
22
+ now = (Time.now.to_f * 1000).to_i # ... * 1000 for convert seconds to milliseconds
23
+ visit_time = data.previous_visit_timestamps[@is_first_visit ? previous_visits_count - 1 : 0]
24
+ return check_targeting(now - visit_time)
25
+ end
26
+ false
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kameleoon/data/visitor_visits'
4
+ require 'time'
5
+
6
+ module Kameleoon
7
+ # @api private
8
+ module Targeting
9
+
10
+ class VisitNumberTodayCondition < NumberCondition
11
+ def initialize(json_condition)
12
+ visit_count = json_condition['visitCount']
13
+ super(json_condition, visit_count)
14
+ end
15
+
16
+ def check(data)
17
+ return false unless data.is_a?(Kameleoon::VisitorVisits) && @condition_value != nil
18
+ number_of_visits_today = 0
19
+ start_of_day = (Time.new.to_date.to_time.to_f * 1000).to_i # ... * 1000 for convert seconds to milliseconds
20
+ for timestamp in data.previous_visit_timestamps
21
+ break if timestamp < start_of_day
22
+ number_of_visits_today += 1
23
+ end
24
+ check_targeting(number_of_visits_today + 1) # +1 for current visit
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kameleoon/targeting/conditions/number_condition'
4
+
5
+ module Kameleoon
6
+ # @api private
7
+ module Targeting
8
+
9
+ class VisitNumberTotalCondition < NumberCondition
10
+ def initialize(json_condition)
11
+ visit_count = json_condition['visitCount']
12
+ super(json_condition, visit_count)
13
+ end
14
+
15
+ def check(data)
16
+ return false unless data.is_a?(Kameleoon::VisitorVisits) && @condition_value != nil
17
+ check_targeting(data.previous_visit_timestamps.size + 1) # +1 for current visit
18
+ end
19
+ end
20
+ end
21
+ end