kameleoon-client-ruby 3.18.0 → 3.19.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: 916619df3b6cdccaf7999889e5f646bcbbfcba29eb6960bbf3374607100c1efe
4
- data.tar.gz: d38b503e8cbf9d6df0a0639292b8b5436a59b4544ea53b45ab4d738a5b556dec
3
+ metadata.gz: dde5f6ebb9e6779b9bf8d4edbdded4e11b82aa642ec046bd2493306e0b2a7ad8
4
+ data.tar.gz: 5a15f627b2832ec10f001ef7ee061fa31c6dbd618b02ec55ff9d1d66ee17d5a8
5
5
  SHA512:
6
- metadata.gz: 1d154b47a9375184a1e2375cce4654b7615f51f37f656db8124228e0cce3f83414185f0ae53b40d704432c3d2ae1abb05d6700af504ce015b77df22d46caa02e
7
- data.tar.gz: 730008de9284005e1401352e48bf2707ae8707456f04e8131cfda0e6b666ba59cf5399f541d01b0b5a5baba83ac7acd78a79e82920a191bc7c49fa6dab5e4dd3
6
+ metadata.gz: 91d3f34ab6855ed62d571fa5d40b85a04504926f2d38650c9b62f052bd9f0b79b0ce95ece6636fa37ecc5062020f2361bc5e0e8af80383b0d2b377dffd5919e7
7
+ data.tar.gz: c2e5efd80b73d5f8ce9c48a6f069aadda0fc7d1a6bc390a2d98af4a8a69b36928bac0188c77406bd793d8db6c4701df24838c67efc89b98d4d0e6b7e425b9c68
@@ -13,12 +13,13 @@ module Kameleoon
13
13
  class DataFile
14
14
  attr_reader :last_modified, :settings, :segments, :audience_tracking_segments, :feature_flags, :me_groups,
15
15
  :has_any_targeted_delivery_rule, :feature_flag_by_id, :rule_info_by_exp_id, :variation_by_id,
16
- :custom_data_info, :experiment_ids_with_js_css_variable, :holdout
16
+ :custom_data_info, :experiment_ids_with_js_css_variable, :holdout, :date_modified
17
17
 
18
18
  def to_s
19
19
  'DataFile{' \
20
20
  "environment:#{@environment}," \
21
21
  "last_modified:#{@last_modified}," \
22
+ "date_modified:#{@date_modified}," \
22
23
  "feature_flags:#{@feature_flags.size}," \
23
24
  "settings:#{@settings}" \
24
25
  '}'
@@ -46,13 +47,15 @@ module Kameleoon
46
47
  def get_feature_flag(feature_key)
47
48
  ff = @feature_flags[feature_key]
48
49
  raise Exception::FeatureNotFound, feature_key if ff.nil?
50
+ ff
51
+ end
49
52
 
50
- unless ff.environment_enabled
53
+ def ensure_environment_enabled(feature_flag)
54
+ unless feature_flag.environment_enabled
51
55
  env = @environment.nil? ? 'default' : "'#{@environment}'"
52
- msg = "Feature '#{feature_key}' is disabled for #{env} environment'"
56
+ msg = "Feature '#{feature_flag.feature_key}' is disabled for #{env} environment'"
53
57
  raise Exception::FeatureEnvironmentDisabled, msg
54
58
  end
55
- ff
56
59
  end
57
60
 
58
61
  def experiment_js_css_variable?(experiment_id)
@@ -63,6 +66,7 @@ module Kameleoon
63
66
 
64
67
  def init_default
65
68
  Logging::KameleoonLogger.debug('CALL: DataFile.init_default')
69
+ @date_modified = 0
66
70
  @settings = Settings.new
67
71
  @feature_flags = {}
68
72
  @me_groups = {}
@@ -73,6 +77,7 @@ module Kameleoon
73
77
 
74
78
  def init(configuration)
75
79
  Logging::KameleoonLogger.debug('CALL: DataFile.init(configuration: %s)', configuration)
80
+ @date_modified = configuration['dateModified'] || 0
76
81
  @settings = Settings.new(configuration['configuration'])
77
82
  @segments, @audience_tracking_segments = parse_segments(configuration)
78
83
  @custom_data_info = CustomDataInfo.new(configuration['customData'])
@@ -16,6 +16,18 @@ module Kameleoon
16
16
  @type = hash['type']
17
17
  @value = hash['value']
18
18
  end
19
+
20
+ def get_value
21
+ case @type
22
+ when 'BOOLEAN', 'STRING', 'NUMBER', 'JS', 'CSS'
23
+ @value
24
+ when 'JSON'
25
+ JSON.parse(@value)
26
+ else
27
+ Logging::KameleoonLogger.error("Unknown type '#{@type}' for feature variable")
28
+ @value
29
+ end
30
+ end
19
31
  end
20
32
  end
21
33
  end
@@ -19,7 +19,7 @@ module Kameleoon
19
19
  end
20
20
 
21
21
  def initialize(hash)
22
- @key = hash['key']
22
+ @key = hash['key'] || ''
23
23
  @variables = Variable.create_from_array(hash['variables'])
24
24
  @name = hash['name'] || ''
25
25
  end
@@ -64,6 +64,16 @@ module Kameleoon
64
64
  visitor
65
65
  end
66
66
 
67
+ def peek_visitor(visitor_code)
68
+ Logging::KameleoonLogger.debug("CALL: VisitorManager.peek_visitor(visitor_code: '%s')", visitor_code)
69
+
70
+ visitor = @visitors[visitor_code]
71
+
72
+ Logging::KameleoonLogger.debug("RETURN: VisitorManager.peek_visitor(visitor_code: '%s') -> (visitor: %s)",
73
+ visitor_code, visitor)
74
+ visitor
75
+ end
76
+
67
77
  def enumerate(&blk)
68
78
  @visitors.each_pair { |vc, v| blk.call(vc, v) }
69
79
  end
@@ -369,8 +369,6 @@ module Kameleoon
369
369
  Utils::VisitorCode.validate(visitor_code)
370
370
  variations = {}
371
371
  @data_manager.data_file.feature_flags.each_value do |feature_flag|
372
- next unless feature_flag.environment_enabled
373
-
374
372
  begin
375
373
  variation_key, eval_exp = get_variation_info(visitor_code, feature_flag, track)
376
374
  rescue Exception::FeatureEnvironmentDisabled
@@ -468,7 +466,7 @@ module Kameleoon
468
466
  raise Exception::FeatureVariableNotFound.new(variable_name),
469
467
  "Feature variable #{variable_name} not found"
470
468
  end
471
- value = _parse_feature_variable(variable)
469
+ value = variable.get_value
472
470
  Logging::KameleoonLogger.info(
473
471
  "RETURN: KameleoonClient.get_feature_variable(visitor_code: '%s', feature_key: '%s', variable_name: '%s', " \
474
472
  'is_unique_identifier: %s) -> (variable: %s)',
@@ -503,12 +501,13 @@ module Kameleoon
503
501
  )
504
502
  feature_flag = @data_manager.data_file.get_feature_flag(feature_key)
505
503
  variation = feature_flag.get_variation_by_key(variation_key)
504
+ @data_manager.data_file.ensure_environment_enabled(feature_flag)
506
505
  if variation.nil?
507
506
  raise Exception::FeatureVariationNotFound.new(variation_key),
508
507
  "Variation key #{variation_key} not found"
509
508
  end
510
509
  variables = {}
511
- variation.variables.each { |var| variables[var.key] = _parse_feature_variable(var) }
510
+ variation.variables.each { |var| variables[var.key] = var.get_value }
512
511
  Logging::KameleoonLogger.info(
513
512
  "RETURN: KameleoonClient.get_feature_variation_variables(feature_key: '%s', variation_key: '%s') " \
514
513
  '-> (variables: %s)', feature_key, variation_key, variables
@@ -607,12 +606,16 @@ module Kameleoon
607
606
  )
608
607
  warehouse_audience
609
608
  end
610
-
611
609
  ##
612
- # Returns a list of all feature flag keys
610
+ # Returns a list of all feature flag keys.
613
611
  #
614
612
  # @return [Array] array of all feature flag keys
613
+ #
614
+ # DEPRECATED. Please use `get_data_file` instead.
615
615
  def get_feature_list # rubocop:disable Naming/AccessorMethodName
616
+ Logging::KameleoonLogger.info(
617
+ '[DEPRECATION] `get_feature_list` is deprecated. Please use `get_data_file` instead.'
618
+ )
616
619
  Logging::KameleoonLogger.info('CALL: KameleoonClient.get_feature_list')
617
620
  features = @data_manager.data_file.feature_flags.keys
618
621
  Logging::KameleoonLogger.info('RETURN: KameleoonClient.get_feature_list -> (features: %s)', features)
@@ -638,8 +641,6 @@ module Kameleoon
638
641
  visitor = @visitor_manager.get_visitor(visitor_code)
639
642
  list_keys = []
640
643
  @data_manager.data_file.feature_flags.each do |feature_key, feature_flag|
641
- next unless feature_flag.environment_enabled
642
-
643
644
  begin
644
645
  eval_exp = evaluate(visitor, visitor_code, feature_flag, false, false)
645
646
  rescue Exception::FeatureEnvironmentDisabled
@@ -677,8 +678,6 @@ module Kameleoon
677
678
  map_active_features = {}
678
679
 
679
680
  @data_manager.data_file.feature_flags.each_value do |feature_flag|
680
- next unless feature_flag.environment_enabled
681
-
682
681
  begin
683
682
  eval_exp = evaluate(visitor, visitor_code, feature_flag, false, false)
684
683
  rescue Exception::FeatureEnvironmentDisabled
@@ -803,41 +802,7 @@ module Kameleoon
803
802
  # @return [Kameleoon::Types::DataFile] The current DataFile instance.
804
803
  def get_data_file
805
804
  Logging::KameleoonLogger.info('CALL: KameleoonClient.get_data_file')
806
-
807
- feature_flags_map = @data_manager.data_file.feature_flags.transform_values do |source_feature_flag|
808
- # Collect variations
809
- variations_map = source_feature_flag.variations.each_with_object({}) do |variation, variations_map|
810
- variables_map = variation.variables.each_with_object({}) do |var, variables_map|
811
- variables_map[var.key] = Types::Variable.new(var.key, var.type, _parse_feature_variable(var))
812
- end
813
- variations_map[variation.key] = Types::Variation.new(variation.key, nil, nil, variables_map, variation.name)
814
- end
815
-
816
- # Collect rules
817
- rules = source_feature_flag.rules.map do |rule|
818
- rule_variations = rule.experiment.variations_by_exposition.each_with_object({}) do |var_by_exp, rule_variations|
819
- base_variation = variations_map[var_by_exp.variation_key]
820
- next unless base_variation
821
- rule_variations[base_variation.key] = Types::Variation.new(
822
- base_variation.key,
823
- var_by_exp.variation_id,
824
- rule.experiment.id,
825
- base_variation.variables,
826
- base_variation.name
827
- )
828
- end
829
- Types::Rule.new(rule_variations)
830
- end
831
-
832
- Types::FeatureFlag.new(
833
- variations_map,
834
- source_feature_flag.environment_enabled,
835
- rules,
836
- source_feature_flag.default_variation_key
837
- )
838
- end
839
-
840
- data_file = Types::DataFile.new(feature_flags_map)
805
+ data_file = @data_manager.external_data_file
841
806
  Logging::KameleoonLogger.info('RETURN: KameleoonClient.get_data_file -> (data_file: %s)', data_file)
842
807
  data_file
843
808
  end
@@ -1015,6 +980,11 @@ module Kameleoon
1015
980
  )
1016
981
  eval_exp = nil
1017
982
  forced_variation = visitor&.get_forced_feature_variation(feature_flag.feature_key)
983
+
984
+ unless forced_variation&.simulated
985
+ @data_manager.data_file.ensure_environment_enabled(feature_flag)
986
+ end
987
+
1018
988
  if forced_variation
1019
989
  eval_exp = EvaluatedExperiment.from_forced_variation(forced_variation)
1020
990
  elsif visitor_not_in_holdout?(visitor, visitor_code, track, save, feature_flag.bucketing_custom_data_index) && \
@@ -1276,21 +1246,10 @@ module Kameleoon
1276
1246
  Logging::KameleoonLogger.debug(
1277
1247
  'CALL: KameleoonClient.create_external_variation(variation: %s, eval_exp: %s)', variation, eval_exp
1278
1248
  )
1279
- ext_variables = {}
1280
- variation&.variables&.each do |variable|
1281
- ext_variables[variable.key] = Types::Variable.new(
1282
- variable.key,
1283
- variable.type,
1284
- _parse_feature_variable(variable)
1285
- )
1286
- end
1287
- ext_variables.freeze
1288
- ext_variation = Types::Variation.new(
1289
- variation&.key || '',
1249
+ ext_variation = Types::Variation._build_from_internal(
1250
+ variation,
1290
1251
  eval_exp&.var_by_exp&.variation_id,
1291
- eval_exp&.experiment&.id,
1292
- ext_variables,
1293
- variation&.name || ''
1252
+ eval_exp&.experiment&.id
1294
1253
  )
1295
1254
  Logging::KameleoonLogger.debug(
1296
1255
  'RETURN: KameleoonClient.create_external_variation(variation: %s, eval_exp: %s) -> (ext_variation: %s)',
@@ -1301,18 +1260,6 @@ module Kameleoon
1301
1260
 
1302
1261
  ##
1303
1262
  # helper method for fetching values from a Variable
1304
- def _parse_feature_variable(variable)
1305
- case variable.type
1306
- when 'BOOLEAN', 'STRING', 'NUMBER', 'JS', 'CSS'
1307
- variable.value
1308
- when 'JSON'
1309
- JSON.parse(variable.value)
1310
- else
1311
- Logging::KameleoonLogger.error("Unknown type '#{variable.type}' for feature variable")
1312
- variable.value
1313
- end
1314
- end
1315
-
1316
1263
  def set_unique_identifier(visitor_code, is_unique_identifier)
1317
1264
  Logging::KameleoonLogger.info(
1318
1265
  "The 'isUniqueIdentifier' parameter is deprecated. Please, add 'UniqueIdentifier' to a visitor instead."
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'kameleoon/types/data_file'
4
+
3
5
  module Kameleoon
4
6
  module Managers
5
7
  module Data
@@ -13,7 +15,11 @@ module Kameleoon
13
15
  end
14
16
 
15
17
  def data_file=(value)
16
- @container = Container.new(value)
18
+ @container = Container.new(value, create_external_data_file(value))
19
+ end
20
+
21
+ def external_data_file
22
+ @container.external_data_file
17
23
  end
18
24
 
19
25
  def visitor_code_managed?
@@ -22,14 +28,22 @@ module Kameleoon
22
28
 
23
29
  class Container
24
30
  attr_reader :data_file, :visitor_code_managed
31
+ attr_reader :external_data_file
25
32
 
26
- def initialize(data_file)
33
+ def initialize(data_file, external_data_file)
27
34
  @data_file = data_file
35
+ @external_data_file = external_data_file
28
36
  # Regarding GDPR policy we should set visitorCode if legal consent isn't required or we have at
29
37
  # least one Targeted Delivery rule in datafile
30
38
  @visitor_code_managed = data_file.settings.is_consent_required && !data_file.has_any_targeted_delivery_rule
31
39
  end
32
40
  end
41
+
42
+ private
43
+
44
+ def create_external_data_file(source_data_file)
45
+ Types::DataFile._build_from_internal(source_data_file)
46
+ end
33
47
  end
34
48
  end
35
49
  end
@@ -32,7 +32,7 @@ module Kameleoon
32
32
 
33
33
  @visitor_codes.each do |visitor_code|
34
34
  if @total_size <= @request_size_limit
35
- visitor = @visitor_manager.get_visitor(visitor_code)
35
+ visitor = @visitor_manager.peek_visitor(visitor_code)
36
36
  is_consent_given = consent_given?(visitor)
37
37
  data = collect_tracking_data(visitor_code, visitor, is_consent_given)
38
38
  if !data.empty?
@@ -5,6 +5,7 @@ require 'net/http'
5
5
  require 'time'
6
6
  require 'kameleoon/utils'
7
7
  require 'kameleoon/logging/kameleoon_logger'
8
+ require 'base64'
8
9
 
9
10
  module Kameleoon
10
11
  module Network
@@ -13,6 +14,7 @@ module Kameleoon
13
14
  TOKEN_OBSOLESCENCE_GAP = 1800 # in seconds
14
15
  JWT_ACCESS_TOKEN_FIELD = 'access_token'
15
16
  JWT_EXPIRES_IN_FIELD = 'expires_in'
17
+ BASIC_AUTHORIZATION_PREFIX = 'Basic '
16
18
 
17
19
  def initialize(network_manager, client_id, client_secret)
18
20
  Logging::KameleoonLogger.debug(lambda {
@@ -23,12 +25,18 @@ module Kameleoon
23
25
  @client_id = client_id
24
26
  @client_secret = client_secret
25
27
  @fetching = false
28
+ @basic_auth_token = AccessTokenSource.construct_basic_token(client_id, client_secret)
26
29
  Logging::KameleoonLogger.debug(lambda {
27
30
  format("RETURN: AccessTokenSource.new(network_manager, client_id: '%s', client_secret: '%s')",
28
31
  Utils::Strval.secret(client_id), Utils::Strval.secret(client_secret))
29
32
  })
30
33
  end
31
34
 
35
+ def self.construct_basic_token(client_id, client_secret)
36
+ basic_token_content = "#{client_id}:#{client_secret}"
37
+ "#{BASIC_AUTHORIZATION_PREFIX}#{Base64.strict_encode64(basic_token_content)}"
38
+ end
39
+
32
40
  def get_token(timeout = nil)
33
41
  Logging::KameleoonLogger.debug('CALL: AccessTokenSource.getToken(timeout: %s)', timeout)
34
42
  now = Time.new.to_i
@@ -70,7 +78,7 @@ module Kameleoon
70
78
 
71
79
  def fetch_token(timeout = nil)
72
80
  Logging::KameleoonLogger.debug('CALL: AccessTokenSource.fetch_token(timeout: %s)', timeout)
73
- response_content = @network_manager.fetch_access_jwtoken(@client_id, @client_secret, timeout)
81
+ response_content = @network_manager.fetch_access_jwtoken(@basic_auth_token, timeout)
74
82
  unless response_content
75
83
  Logging::KameleoonLogger.error('Failed to fetch access JWT')
76
84
  return nil
@@ -17,7 +17,10 @@ module Kameleoon
17
17
  def collect_headers(request)
18
18
  headers = { 'Content-Type' => request.content_type }
19
19
  headers.merge!(request.extra_headers) unless request.extra_headers.nil?
20
- headers['Authorization'] = "Bearer #{request.access_token}" unless request.access_token.nil?
20
+ unless request.access_token.nil? || request.access_token.empty?
21
+ token_prefix = request.access_token.start_with?('Basic') ? '' : 'Bearer '
22
+ headers['Authorization'] = "#{token_prefix}#{request.access_token}"
23
+ end
21
24
  headers
22
25
  end
23
26
 
@@ -70,16 +70,15 @@ module Kameleoon
70
70
  unwrap_response(*make_call(request, true, TRACKING_CALL_ATTEMPT_NUMBER - 1, TRACKING_CALL_RETRY_DELAY))
71
71
  end
72
72
 
73
- def fetch_access_jwtoken(client_id, client_secret, timeout = nil)
73
+ def fetch_access_jwtoken(basic_auth_token, timeout = nil)
74
74
  url = @url_provider.make_access_token_url
75
75
  timeout = ensure_timeout(timeout)
76
76
  data_map = {
77
77
  grant_type: ACCESS_TOKEN_GRANT_TYPE,
78
- client_id: client_id,
79
- client_secret: client_secret
80
78
  }
81
79
  data = UriHelper.encode_query(data_map).encode('UTF-8')
82
80
  request = Request.new(Method::POST, url, ContentType::FORM, timeout, data: data)
81
+ request.authorize(basic_auth_token)
83
82
  unwrap_response(*make_call(request, false))
84
83
  end
85
84
 
@@ -11,12 +11,15 @@ module Kameleoon
11
11
  body = 'null'
12
12
  unless @data.nil?
13
13
  if @data.is_a?(String)
14
- body = @data.start_with?('grant_type=client_credentials') ? '****' : @data
15
- else
16
14
  body = @data
17
15
  end
18
16
  end
19
- "Request{method:'#{@method}',url:'#{@url}',headers:#{@extra_headers},body:'#{body}'}"
17
+
18
+ headers = ''
19
+ headers += 'Authorization: ***' unless access_token.nil?
20
+ headers += @extra_headers.to_s unless @extra_headers.nil?
21
+
22
+ "Request{method:'#{@method}',url:'#{@url}',headers:#{headers},body:'#{body}'}"
20
23
  end
21
24
 
22
25
  def initialize(method, url, content_type, timeout, extra_headers: nil, data: nil)
@@ -22,7 +22,7 @@ module Kameleoon
22
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
- ACCESS_TOKEN_URL_FORMAT = 'https://%s/oauth/token'
25
+ ACCESS_TOKEN_URL_FORMAT = 'https://%s/oauth/token?%s'
26
26
 
27
27
  TEST_DATA_API_DOMAIN = 'data.kameleoon.net'
28
28
  TEST_AUTOMATION_API_DOMAIN = 'api.kameleoon.net'
@@ -39,6 +39,15 @@ module Kameleoon
39
39
  @configuration_domain = DEFAULT_CONFIGURATION_DOMAIN
40
40
  @access_token_domain = DEFAULT_ACCESS_TOKEN_DOMAIN
41
41
  @is_custom_domain = false
42
+
43
+ url_params = {
44
+ sdkName: SDK_NAME,
45
+ sdkVersion: SDK_VERSION,
46
+ siteCode: @site_code
47
+ }
48
+ @access_token_url_params = UriHelper.encode_query(url_params)
49
+ url_params[:bodyUa] = true
50
+ @tracking_url_params = UriHelper.encode_query(url_params)
42
51
  update_domains(network_domain)
43
52
  end
44
53
 
@@ -60,7 +69,7 @@ module Kameleoon
60
69
  siteCode: @site_code,
61
70
  bodyUa: true
62
71
  }
63
- format(DATA_API_URL_FORMAT, @data_api_domain, TRACKING_PATH, UriHelper.encode_query(params))
72
+ format(DATA_API_URL_FORMAT, @data_api_domain, TRACKING_PATH, @tracking_url_params)
64
73
  end
65
74
 
66
75
  def make_visitor_data_get_url(visitor_code, filter, is_unique_identifier = false)
@@ -104,11 +113,20 @@ module Kameleoon
104
113
  end
105
114
 
106
115
  def make_access_token_url
107
- format(ACCESS_TOKEN_URL_FORMAT, @access_token_domain)
116
+ format(ACCESS_TOKEN_URL_FORMAT, @access_token_domain, @access_token_url_params)
108
117
  end
109
118
 
110
119
  private
111
120
 
121
+ def make_post_query_base(site_code)
122
+ data_map = {
123
+ "sdkName": Kameleoon::SDK_NAME,
124
+ "sdkVersion": Kameleoon::SDK_VERSION,
125
+ "siteCode": site_code
126
+ }
127
+ data = UriHelper.encode_query(data_map).encode('UTF-8')
128
+ end
129
+
112
130
  def update_domains(network_domain)
113
131
  return if network_domain.nil? || network_domain.empty?
114
132
 
@@ -8,6 +8,7 @@ module Kameleoon
8
8
  # UnknownCondition represents not defined condition, always returns that visitor is targeted (true)
9
9
  class UnknownCondition < Condition
10
10
  def check(_data)
11
+ Logging::KameleoonLogger.warning('Condition of unknown type \'%s\' evaluated as true', type)
11
12
  true
12
13
  end
13
14
  end
@@ -1,18 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'kameleoon/types/feature_flag'
4
+
3
5
  module Kameleoon
4
6
  module Types
5
7
  # DataFile
6
8
  class DataFile
7
- attr_reader :feature_flags
9
+ attr_reader :feature_flags, :date_modified
8
10
 
9
11
  # @api private
10
- def initialize(feature_flags)
12
+ def initialize(feature_flags, date_modified)
11
13
  @feature_flags = feature_flags
14
+ @date_modified = date_modified
15
+ end
16
+
17
+ # @api private
18
+ def self._build_from_internal(source_data_file)
19
+ feature_flags = (source_data_file.feature_flags || {}).transform_values do |feature_flag|
20
+ FeatureFlag._build_from_internal(feature_flag)
21
+ end
22
+ DataFile.new(feature_flags, source_data_file.date_modified)
12
23
  end
13
24
 
14
25
  def to_s
15
- "DataFile{feature_flags:#{@feature_flags}}"
26
+ "DataFile{feature_flags:#{@feature_flags},date_modified:#{@date_modified}}"
16
27
  end
17
28
  end
18
29
  end
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'kameleoon/types/rule'
4
+ require 'kameleoon/types/variation'
5
+
3
6
  module Kameleoon
4
7
  module Types
5
8
  # FeatureFlag
@@ -18,6 +21,25 @@ module Kameleoon
18
21
  @variations[@default_variation_key]
19
22
  end
20
23
 
24
+ # @api private
25
+ def self._build_from_internal(source_feature_flag)
26
+ variations = (source_feature_flag.variations || []).each_with_object({}) do |variation, result|
27
+ ext_variation = Variation._build_from_internal(variation)
28
+ result[ext_variation.key] = ext_variation
29
+ end
30
+
31
+ rules = (source_feature_flag.rules || []).map do |rule|
32
+ Rule._build_from_internal(rule, variations)
33
+ end
34
+
35
+ FeatureFlag.new(
36
+ variations,
37
+ source_feature_flag.environment_enabled,
38
+ rules,
39
+ source_feature_flag.default_variation_key
40
+ )
41
+ end
42
+
21
43
  def to_s
22
44
  "FeatureFlag{variations:#{@variations},environment_enabled:#{@environment_enabled},rules:#{@rules},default_variation_key:#{@default_variation_key}}"
23
45
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'kameleoon/types/variation'
4
+
3
5
  module Kameleoon
4
6
  module Types
5
7
  # Rule
@@ -11,6 +13,25 @@ module Kameleoon
11
13
  @variations = variations
12
14
  end
13
15
 
16
+ # @api private
17
+ def self._build_from_internal(internal_rule, variations)
18
+ rule_variations = {}
19
+ vars_by_exposition = internal_rule.experiment&.variations_by_exposition || []
20
+ vars_by_exposition.each do |var_by_exp|
21
+ base_variation = variations[var_by_exp.variation_key]
22
+ next if base_variation.nil?
23
+
24
+ rule_variations[base_variation.key] = Variation.new(
25
+ base_variation.key,
26
+ var_by_exp.variation_id,
27
+ internal_rule.experiment&.id,
28
+ base_variation.variables,
29
+ base_variation.name
30
+ )
31
+ end
32
+ Rule.new(rule_variations)
33
+ end
34
+
14
35
  def to_s
15
36
  "Rule{variations:#{@variations}}"
16
37
  end
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'json'
4
+ require 'kameleoon/logging/kameleoon_logger'
5
+
3
6
  module Kameleoon
4
7
  module Types
5
8
  # Variable
@@ -16,6 +19,11 @@ module Kameleoon
16
19
  @type = type
17
20
  @value = value
18
21
  end
22
+
23
+ # @api private
24
+ def self._build_from_internal(source_variable)
25
+ Variable.new(source_variable.key, source_variable.type, source_variable.get_value)
26
+ end
19
27
  end
20
28
  end
21
29
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'kameleoon/configuration/variation'
4
+ require 'kameleoon/types/variable'
4
5
 
5
6
  module Kameleoon
6
7
  module Types
@@ -21,6 +22,17 @@ module Kameleoon
21
22
  @name = name
22
23
  end
23
24
 
25
+ # @api private
26
+ def self._build_from_internal(source_variation, variation_id = nil, experiment_id = nil)
27
+ variables = (source_variation&.variables || []).each_with_object({}) do |variable, result|
28
+ ext_variable = Variable._build_from_internal(variable)
29
+ result[ext_variable.key] = ext_variable
30
+ end
31
+ key = source_variation&.key || ''
32
+ name = source_variation&.name || ''
33
+ Variation.new(key, variation_id, experiment_id, variables, name)
34
+ end
35
+
24
36
  def active?
25
37
  @key != Configuration::VariationType::VARIATION_OFF
26
38
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kameleoon
4
- SDK_VERSION = '3.18.0'
4
+ SDK_VERSION = '3.19.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.18.0
4
+ version: 3.19.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kameleoon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-02-13 00:00:00.000000000 Z
11
+ date: 2026-04-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: em-http-request