kameleoon-client-ruby 3.13.0 → 3.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 05500f77891f22fd67b10f0dec830e6673a77d8ff83b391ffbf8547893bfcf1e
4
- data.tar.gz: 4c572f1464af6e5026642e4fac03dcc1e1a99715228c76982c67de5063c2235e
3
+ metadata.gz: b5566ec9ff02ebb63ff3ee337f4c26fc0c229f2de8df0893fe85dc3f83fbc0ce
4
+ data.tar.gz: fcd11bacbaead7a0c357a0fd4828fb6c4b7347ebf1fab728f4e00882b0224dc7
5
5
  SHA512:
6
- metadata.gz: b7ac1acb43bf56661f194dacc810b064e2c4cba67010b1af49d928c190ae53c139ee0447fb7e5f5670ae1fb0c406a0efb7e2c7fe0eedac323635dc0e215de869
7
- data.tar.gz: 58278744be7aaef43a0e0dcbe71bf93ee9ab9c1aec3a0b6e6f296e940f24ce943ee7c7998a550ecf1950cbcb3f36cb78858feacea6289a2b8ce1c3439b4b037e
6
+ metadata.gz: 78418ebd561b9d1fe2639751d0dae68a875a4a76f4782ac079fe2f3f3c6e2b7466c41cb762e978b4895973a97ddf4940b10171d36c2695a565ed2e1b66445f53
7
+ data.tar.gz: b91b577c23c13547d78c09c98a2c39bb788a314b280b50c57bf1cc25891dd74747127aec2bc14b0a5a4908a2cde2d0e2089ec13c6f5200087b7c52bd80b7a68f
@@ -13,16 +13,18 @@ module Kameleoon
13
13
  def initialize(hashes)
14
14
  @local_only = Set[]
15
15
  @visitor_scope = Set[]
16
+ @custom_data_index_by_id = {}
16
17
  unless hashes.nil?
17
18
  for hash in hashes
18
19
  index = hash['index']
19
20
  @local_only.add(index) if hash['localOnly']
20
21
  @visitor_scope.add(index) if hash['scope'] == SCOPE_VISITOR
22
+ id = hash['id']
23
+ @custom_data_index_by_id[id] = index if id && index
21
24
  if hash['isMappingIdentifier']
22
25
  unless @mapping_identifier_index.nil?
23
26
  Logging::KameleoonLogger.warning('More than one mapping identifier is set. Undefined behavior ' \
24
27
  'may occur on cross-device reconciliation.')
25
-
26
28
  end
27
29
  @mapping_identifier_index = index
28
30
  end
@@ -42,6 +44,10 @@ module Kameleoon
42
44
  @visitor_scope.include?(index)
43
45
  end
44
46
 
47
+ def get_custom_data_index_by_id(custom_data_id)
48
+ @custom_data_index_by_id[custom_data_id]
49
+ end
50
+
45
51
  def self.mapping_identifier?(custom_data_info, custom_data)
46
52
  !custom_data_info.nil? && (custom_data.id == custom_data_info.mapping_identifier_index) && \
47
53
  !(custom_data.values.empty? || custom_data.values[0].empty?)
@@ -6,6 +6,7 @@ require 'kameleoon/configuration/feature_flag'
6
6
  require 'kameleoon/configuration/me_group'
7
7
  require 'kameleoon/configuration/settings'
8
8
  require 'kameleoon/logging/kameleoon_logger'
9
+ require 'kameleoon/targeting/models'
9
10
 
10
11
  module Kameleoon
11
12
  module Configuration
@@ -69,18 +70,29 @@ module Kameleoon
69
70
  def init(configuration)
70
71
  Logging::KameleoonLogger.debug('CALL: DataFile.init(configuration: %s)', configuration)
71
72
  @settings = Settings.new(configuration['configuration'])
72
- @feature_flags = {}
73
- configuration['featureFlags'].each do |raw|
74
- ff = FeatureFlag.new(raw)
75
- @feature_flags[ff.feature_key] = ff
76
- end
73
+ segments = parse_segments(configuration)
74
+ @custom_data_info = CustomDataInfo.new(configuration['customData'])
75
+ @feature_flags = parse_feature_flags(configuration, segments, @custom_data_info)
77
76
  @me_groups = make_me_groups(@feature_flags)
78
77
  @has_any_targeted_delivery_rule = any_targeted_delivery_rule?
79
- @custom_data_info = CustomDataInfo.new(configuration['customData'])
80
78
  @holdout = Experiment.from_json(configuration['holdout']) if configuration.include?('holdout')
81
79
  Logging::KameleoonLogger.debug('RETURN: DataFile.init(configuration: %s)', configuration)
82
80
  end
83
81
 
82
+ def parse_segments(configuration)
83
+ configuration['segments'].to_h do |raw_seg|
84
+ seg = Targeting::Segment.new(raw_seg)
85
+ [seg.id, seg]
86
+ end
87
+ end
88
+
89
+ def parse_feature_flags(configuration, segments, cdi)
90
+ configuration['featureFlags'].to_h do |raw_ff|
91
+ ff = FeatureFlag.new(raw_ff, segments, cdi)
92
+ [ff.feature_key, ff]
93
+ end
94
+ end
95
+
84
96
  def any_targeted_delivery_rule?
85
97
  @feature_flags.any? { |_, ff| ff.environment_enabled && ff.rules.any?(&:targeted_delivery_type?) }
86
98
  end
@@ -8,7 +8,8 @@ module Kameleoon
8
8
  module Configuration
9
9
  # Class for manage all feature flags with rules
10
10
  class FeatureFlag
11
- attr_accessor :id, :feature_key, :variations, :default_variation_key, :me_group_name, :environment_enabled, :rules
11
+ attr_reader :id, :feature_key, :variations, :default_variation_key, :me_group_name, :environment_enabled, :rules,
12
+ :bucketing_custom_data_index
12
13
 
13
14
  def self.create_from_array(array)
14
15
  array&.map { |it| FeatureFlag.new(it) }
@@ -21,18 +22,21 @@ module Kameleoon
21
22
  "environment_enabled:#{@environment_enabled}," \
22
23
  "default_variation_key:'#{@default_variation_key}'," \
23
24
  "me_group_name:'#{@me_group_name}'," \
24
- "rules:#{@rules.size}" \
25
+ "rules:#{@rules.size}," \
26
+ "bucketing_custom_data_index:#{@bucketing_custom_data_index}" \
25
27
  '}'
26
28
  end
27
29
 
28
- def initialize(hash)
30
+ def initialize(hash, segments, cdi)
29
31
  @id = hash['id']
30
32
  @feature_key = hash['featureKey']
31
33
  @variations = Variation.create_from_array(hash['variations'])
32
34
  @default_variation_key = hash['defaultVariationKey']
33
35
  @me_group_name = hash['mutuallyExclusiveGroup']
34
36
  @environment_enabled = hash['environmentEnabled']
35
- @rules = Rule.create_from_array(hash['rules'])
37
+ @rules = Rule.create_from_array(hash['rules'], segments)
38
+ bucketing_cd_id = hash['bucketingCustomDataId']
39
+ @bucketing_custom_data_index = bucketing_cd_id ? cdi.get_custom_data_index_by_id(bucketing_cd_id) : nil
36
40
  end
37
41
 
38
42
  def get_variation_by_key(key)
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'experiment'
4
- require 'kameleoon/targeting/models'
5
4
 
6
5
  module Kameleoon
7
6
  # Module which contains all internal data of SDK
@@ -32,22 +31,22 @@ module Kameleoon
32
31
  attr_reader :id, :order, :type, :exposition, :experiment, :respool_time, :segment_id
33
32
  attr_accessor :targeting_segment
34
33
 
35
- def self.create_from_array(array)
36
- array&.map { |it| Rule.new(it) }
34
+ def self.create_from_array(array, segments)
35
+ array&.map { |it| Rule.new(it, segments) }
37
36
  end
38
37
 
39
38
  def to_s
40
39
  "Rule{id:#{@id}}"
41
40
  end
42
41
 
43
- def initialize(hash)
42
+ def initialize(hash, segments)
44
43
  @id = hash['id']
45
44
  @order = hash['order']
46
45
  @type = RuleType.from_literal(hash['type'])
47
46
  @exposition = hash['exposition']
48
47
  @respool_time = hash['respoolTime']
49
- @targeting_segment = Kameleoon::Targeting::Segment.new((hash['segment'])) if hash['segment']
50
- @segment_id = @targeting_segment != nil ? targeting_segment.id : -1
48
+ @segment_id = hash['segmentId'] || -1
49
+ @targeting_segment = segments[@segment_id] if @segment_id != -1
51
50
  @experiment = Experiment.from_json(hash)
52
51
  end
53
52
 
@@ -15,4 +15,3 @@ module Kameleoon
15
15
  end
16
16
  end
17
17
  end
18
-
@@ -933,7 +933,7 @@ module Kameleoon
933
933
  forced_variation = visitor&.get_forced_feature_variation(feature_flag.feature_key)
934
934
  if forced_variation
935
935
  eval_exp = EvaluatedExperiment.from_forced_variation(forced_variation)
936
- elsif visitor_not_in_holdout?(visitor, visitor_code, track, save) && \
936
+ elsif visitor_not_in_holdout?(visitor, visitor_code, track, save, feature_flag.bucketing_custom_data_index) && \
937
937
  ff_unrestricted_by_me_group?(visitor, visitor_code, feature_flag)
938
938
  eval_exp = _calculate_variation_key_for_feature(visitor_code, feature_flag)
939
939
  end
@@ -955,7 +955,7 @@ module Kameleoon
955
955
  unrestricted = true
956
956
  me_group = @data_manager.data_file.me_groups[feature_flag.me_group_name]
957
957
  if me_group
958
- code_for_hash = get_code_for_hash(visitor, visitor_code)
958
+ code_for_hash = get_code_for_hash(visitor, visitor_code, feature_flag.bucketing_custom_data_index)
959
959
  me_group_hash = Utils::Hasher.obtain_hash_for_me_group(code_for_hash, feature_flag.me_group_name)
960
960
  Logging::KameleoonLogger.debug(
961
961
  "Calculated ME group hash %s for code: '%s', meGroup: '%s'",
@@ -970,17 +970,17 @@ module Kameleoon
970
970
  unrestricted
971
971
  end
972
972
 
973
- def visitor_not_in_holdout?(visitor, visitor_code, track, save)
973
+ def visitor_not_in_holdout?(visitor, visitor_code, track, save, bucketing_custom_data_index)
974
974
  holdout = @data_manager.data_file.holdout
975
975
  return true if holdout.nil?
976
976
 
977
977
  in_holdout_variation_key = 'in-holdout'
978
978
  Logging::KameleoonLogger.debug(
979
- "CALL: KameleoonClient.visitor_not_in_holdout?(visitor, visitor_code: '%s', track: %s, save: %s)",
980
- visitor_code, track, save
979
+ "CALL: KameleoonClient.visitor_not_in_holdout?(visitor, visitor_code: '%s', track: %s, save: %s, " \
980
+ 'bucketing_custom_data_index: %s)', visitor_code, track, save, bucketing_custom_data_index
981
981
  )
982
982
  is_not_in_holdout = true
983
- code_for_hash = get_code_for_hash(visitor, visitor_code)
983
+ code_for_hash = get_code_for_hash(visitor, visitor_code, bucketing_custom_data_index)
984
984
  variation_hash = Utils::Hasher.obtain(code_for_hash, holdout.id)
985
985
  Logging::KameleoonLogger.debug("Calculated holdout hash %s for code '%s'", variation_hash, code_for_hash)
986
986
  var_by_exp = holdout.get_variation(variation_hash)
@@ -992,9 +992,9 @@ module Kameleoon
992
992
  end
993
993
  end
994
994
  Logging::KameleoonLogger.debug(
995
- "RETURN: KameleoonClient.visitor_not_in_holdout?(visitor, visitor_code: '%s', track: %s, save: %s)" \
996
- ' -> (is_not_in_holdout: %s)',
997
- visitor_code, track, save, is_not_in_holdout
995
+ "RETURN: KameleoonClient.visitor_not_in_holdout?(visitor, visitor_code: '%s', track: %s, save: %s, " \
996
+ 'bucketing_custom_data_index: %s) -> (is_not_in_holdout: %s)',
997
+ visitor_code, track, save, bucketing_custom_data_index, is_not_in_holdout
998
998
  )
999
999
  is_not_in_holdout
1000
1000
  end
@@ -1038,16 +1038,24 @@ module Kameleoon
1038
1038
  )
1039
1039
  end
1040
1040
 
1041
- def get_code_for_hash(visitor, visitor_code)
1042
- # use mappingIdentifier instead of visitor_code if it was set up
1043
- visitor&.mapping_identifier || visitor_code
1041
+ def get_code_for_hash(visitor, visitor_code, bucketing_custom_data_index)
1042
+ return visitor_code if visitor.nil?
1043
+
1044
+ # 1. Try to use the bucketing custom data's value if bucketingCustomDataId is defined
1045
+ if bucketing_custom_data_index
1046
+ bucketing_custom_data = visitor.custom_data.get(bucketing_custom_data_index)
1047
+ return bucketing_custom_data.values[0] if bucketing_custom_data && !bucketing_custom_data.values.empty?
1048
+ end
1049
+ # 2. Use mappingIdentifier instead of visitorCode if it was set up
1050
+ visitor.mapping_identifier || visitor_code
1044
1051
  end
1045
1052
 
1046
- def evaluate_cbscores(visitor, visitor_code, rule)
1053
+ def evaluate_cbscores(visitor, visitor_code, rule, bucketing_custom_data_index)
1047
1054
  return nil if visitor&.cbscores.nil?
1048
1055
 
1049
1056
  Logging::KameleoonLogger.debug(
1050
- "CALL: KameleoonClient.evaluate_cbscores(visitor, visitor_code: '%s', rule: %s)", visitor_code, rule
1057
+ "CALL: KameleoonClient.evaluate_cbscores(visitor, visitor_code: '%s', rule: %s, " \
1058
+ 'bucketing_custom_data_index: %s)', visitor_code, rule, bucketing_custom_data_index
1051
1059
  )
1052
1060
  eval_exp = nil
1053
1061
  var_id_group_by_scores = visitor.cbscores.values[rule.experiment.id]
@@ -1062,7 +1070,7 @@ module Kameleoon
1062
1070
  if (var_by_exp_in_cbs&.size || 0).positive?
1063
1071
  size = var_by_exp_in_cbs.size
1064
1072
  if size > 1
1065
- code_for_hash = get_code_for_hash(visitor, visitor_code)
1073
+ code_for_hash = get_code_for_hash(visitor, visitor_code, bucketing_custom_data_index)
1066
1074
  variation_hash = Utils::Hasher.obtain(code_for_hash, rule.experiment.id, rule.respool_time)
1067
1075
  Logging::KameleoonLogger.debug("Calculated CBS hash %s for code '%s'", variation_hash, code_for_hash)
1068
1076
  idx = [(variation_hash * size).to_i, size - 1].min
@@ -1073,8 +1081,8 @@ module Kameleoon
1073
1081
  end
1074
1082
  end
1075
1083
  Logging::KameleoonLogger.debug(
1076
- "RETURN: KameleoonClient.evaluate_cbscores(visitor, visitor_code: '%s', rule: %s) -> (eval_exp: %s)",
1077
- visitor_code, rule, eval_exp
1084
+ "RETURN: KameleoonClient.evaluate_cbscores(visitor, visitor_code: '%s', rule: %s," \
1085
+ 'bucketing_custom_data_index: %s) -> (eval_exp: %s)', visitor_code, rule, bucketing_custom_data_index, eval_exp
1078
1086
  )
1079
1087
  eval_exp
1080
1088
  end
@@ -1087,7 +1095,7 @@ module Kameleoon
1087
1095
  visitor_code, feature_flag
1088
1096
  )
1089
1097
  visitor = @visitor_manager.get_visitor(visitor_code)
1090
- code_for_hash = get_code_for_hash(visitor, visitor_code)
1098
+ code_for_hash = get_code_for_hash(visitor, visitor_code, feature_flag.bucketing_custom_data_index)
1091
1099
  # no rules -> return default_variation_key
1092
1100
  eval_exp = nil
1093
1101
  feature_flag.rules.each do |rule|
@@ -1110,7 +1118,7 @@ module Kameleoon
1110
1118
  Logging::KameleoonLogger.debug("Calculated rule hash %s for code '%s'", hash_rule, code_for_hash)
1111
1119
  # check main expostion for rule with hashRule
1112
1120
  if hash_rule <= rule.exposition
1113
- eval_exp = evaluate_cbscores(visitor, visitor_code, rule)
1121
+ eval_exp = evaluate_cbscores(visitor, visitor_code, rule, feature_flag.bucketing_custom_data_index)
1114
1122
  break unless eval_exp.nil?
1115
1123
 
1116
1124
  if rule.targeted_delivery_type?
@@ -3,6 +3,7 @@
3
3
  module Kameleoon
4
4
  module Network
5
5
  module ContentType
6
+ WILDCARD = '*/*'
6
7
  TEXT = 'text/plain'
7
8
  JSON = 'application/json'
8
9
  FORM = 'application/x-www-form-urlencoded'
@@ -66,7 +66,7 @@ module Kameleoon
66
66
 
67
67
  url = @url_provider.make_tracking_url
68
68
  timeout = ensure_timeout(timeout)
69
- request = Request.new(Method::POST, url, ContentType::TEXT, timeout, data: lines)
69
+ request = Request.new(Method::POST, url, ContentType::WILDCARD, timeout, data: lines)
70
70
  unwrap_response(*make_call(request, true, TRACKING_CALL_ATTEMPT_NUMBER - 1, TRACKING_CALL_RETRY_DELAY))
71
71
  end
72
72
 
@@ -19,7 +19,7 @@ module Kameleoon
19
19
  DEFAULT_ACCESS_TOKEN_DOMAIN = 'api.kameleoon.com'
20
20
  DEFAULT_DATA_API_DOMAIN = 'data.kameleoon.io'
21
21
 
22
- CONFIGURATION_API_URL_FORMAT = 'https://%s/%s'
22
+ CONFIGURATION_API_URL_FORMAT = 'https://%s/v3/%s'
23
23
  DATA_API_URL_FORMAT = 'https://%s%s?%s'
24
24
  RT_CONFIGURATION_URL_FORMAT = 'https://%s:8110/sse?%s'
25
25
  ACCESS_TOKEN_URL_FORMAT = 'https://%s/oauth/token'
@@ -9,6 +9,7 @@ module Kameleoon
9
9
  class Segment
10
10
  include TreeBuilder
11
11
  attr_accessor :id, :tree
12
+
12
13
  def to_s
13
14
  @tree.to_s
14
15
  end
@@ -84,13 +85,13 @@ module Kameleoon
84
85
  # Computing results
85
86
  if is_left_child_targeted.nil?
86
87
  if is_right_child_targeted == @or_operator
87
- is_targeted = Marshal.load(Marshal.dump(@or_operator)) #Deep copy
88
+ is_targeted = Marshal.load(Marshal.dump(@or_operator)) # Deep copy
88
89
  else
89
90
  is_targeted = nil
90
91
  end
91
92
  else
92
93
  if is_left_child_targeted == @or_operator
93
- is_targeted = Marshal.load(Marshal.dump(@or_operator)) #Deep copy
94
+ is_targeted = Marshal.load(Marshal.dump(@or_operator)) # Deep copy
94
95
  else
95
96
  if is_right_child_targeted == true
96
97
  is_targeted = true
@@ -102,7 +103,7 @@ module Kameleoon
102
103
  end
103
104
  end
104
105
  end
105
- Marshal.load(Marshal.dump(is_targeted)) #Deep copy
106
+ Marshal.load(Marshal.dump(is_targeted)) # Deep copy
106
107
  end
107
108
 
108
109
  def check_condition(datas, condition = @condition)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kameleoon
4
- SDK_VERSION = '3.13.0'
4
+ SDK_VERSION = '3.14.0'
5
5
  SDK_NAME = 'RUBY'
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kameleoon-client-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.13.0
4
+ version: 3.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kameleoon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-05-26 00:00:00.000000000 Z
11
+ date: 2025-06-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: em-http-request