kameleoon-client-ruby 3.1.1 → 3.2.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.
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