kameleoon-client-ruby 3.4.0 → 3.6.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: 2fb2eefaf6fa7008e5e475044e2f3c20baa088332657f0e19e17c73abffdfa3b
4
- data.tar.gz: ef078e7a9d1ac4fd2fef3a74edf4ee5328043b6989c59eafc29559f79ab535d4
3
+ metadata.gz: 92e73ac3d81e29ddfc093e8da1a47f42b9970ea6e8717e0752be511b759db17a
4
+ data.tar.gz: 710361f25b6338e35bb668bb30f116c931efddd21fdb272fa5367add4d82db22
5
5
  SHA512:
6
- metadata.gz: 8df54dcee89dc21a3565948e96b330b655d780ca7cbd4955385b12c5ad64ccdbc8318b17043acb02eddcd2d8dd45191eaeb001d6957e250573e8b9cc606ffbf0
7
- data.tar.gz: 3fe4dfe732310de088f45dcc2002c2504b03e45e2e101bb7199b46783cac46aed80a7e35fc0cc6995ddcb7ba0202629d367922e58d8a8e90b974679f91363086
6
+ metadata.gz: 24db6ec9d63c160690506060d703661f77c5b60160fc585117b91460e74185a095f0b52156b268ba18d445797a584c9bd92e824c2bbecf8533d08e09cf89dab6
7
+ data.tar.gz: ab2f4e530b33235f7105158414ff77ce52f5b824632e4cae53ed06425f32be6f22719dcae86d7d5660cbd09ea4c090672ee5a237828a6db0e0772e80d65032c5
@@ -9,7 +9,7 @@ module Kameleoon
9
9
  module Configuration
10
10
  class DataFile
11
11
  attr_reader :settings, :feature_flags, :has_any_targeted_delivery_rule, :feature_flag_by_id, :rule_by_segment_id,
12
- :variation_by_id, :custom_data_info
12
+ :variation_by_id, :custom_data_info, :experiment_ids_with_js_css_variable
13
13
 
14
14
  def to_s
15
15
  'DataFile{' \
@@ -39,6 +39,10 @@ module Kameleoon
39
39
  ff
40
40
  end
41
41
 
42
+ def experiment_js_css_variable?(experiment_id)
43
+ @experiment_ids_with_js_css_variable.include?(experiment_id)
44
+ end
45
+
42
46
  private
43
47
 
44
48
  def init_default
@@ -71,21 +75,31 @@ module Kameleoon
71
75
  @feature_flag_by_id = {}
72
76
  @rule_by_segment_id = {}
73
77
  @variation_by_id = {}
78
+ @experiment_ids_with_js_css_variable = Set.new
74
79
 
75
80
  @feature_flags.each_value do |feature_flag|
76
81
  @feature_flag_by_id[feature_flag.id] = feature_flag
77
82
  next if feature_flag.rules.nil?
78
83
 
84
+ has_feature_flag_variable_js_css = feature_flag_variable_js_css?(feature_flag)
79
85
  feature_flag.rules.each do |rule|
80
86
  @rule_by_segment_id[rule.segment_id] = rule
81
87
  rule.variation_by_exposition.each do |variation|
82
88
  @variation_by_id[variation.variation_id] = variation
83
89
  end
90
+ @experiment_ids_with_js_css_variable.add(rule.experiment_id) if has_feature_flag_variable_js_css
84
91
  end
85
92
  end
86
93
  @feature_flag_by_id.freeze
87
94
  @rule_by_segment_id.freeze
88
95
  @variation_by_id.freeze
96
+ @experiment_ids_with_js_css_variable.freeze
97
+ end
98
+
99
+ def feature_flag_variable_js_css?(feature_flag)
100
+ feature_flag.variations.first&.variables&.any? do |variable|
101
+ %w[JS CSS].include?(variable.type)
102
+ end
89
103
  end
90
104
  end
91
105
  end
@@ -33,7 +33,7 @@ module Kameleoon
33
33
  @rules = Rule.create_from_array(hash['rules'])
34
34
  end
35
35
 
36
- def get_variation_key(key)
36
+ def get_variation_by_key(key)
37
37
  variations.select { |v| v.key == key }.first
38
38
  end
39
39
  end
@@ -18,7 +18,9 @@ module Kameleoon
18
18
  "assignment_time:#{@assignment_time},rule_type:#{@rule_type}}"
19
19
  end
20
20
 
21
- def initialize(experiment_id, variation_id, rule_type = Kameleoon::Configuration::RuleType::UNKNOWN, assignment_time: nil)
21
+ def initialize(
22
+ experiment_id, variation_id, rule_type = Kameleoon::Configuration::RuleType::UNKNOWN, assignment_time: nil
23
+ )
22
24
  super(DataType::ASSIGNED_VARIATION)
23
25
  @experiment_id = experiment_id
24
26
  @variation_id = variation_id
@@ -12,7 +12,7 @@ module Kameleoon
12
12
  def initialize(page_view, count = 1, timestamp = nil)
13
13
  @page_view = page_view
14
14
  @count = count
15
- @last_timestamp = timestamp != nil ? timestamp : Time.new.to_i
15
+ @last_timestamp = timestamp.nil? ? Time.new.to_i : timestamp
16
16
  end
17
17
 
18
18
  # Not thread-save method, should be called in synchronized code
@@ -20,10 +20,11 @@ module Kameleoon
20
20
  def overwrite(page_view)
21
21
  @page_view = page_view
22
22
  @count += 1
23
+ @last_timestamp = Time.new.to_i
23
24
  end
24
25
 
25
26
  # Not thread-save method, should be called in synchronized code
26
- def merge (page_view_visit)
27
+ def merge(page_view_visit)
27
28
  @count += page_view_visit.count
28
29
  @last_timestamp = [@last_timestamp, page_view_visit.last_timestamp].max
29
30
  end
@@ -24,7 +24,7 @@ module Kameleoon
24
24
  attr_reader :data, :is_unique_identifier
25
25
 
26
26
  def to_s
27
- "Visitor{}"
27
+ 'Visitor{}'
28
28
  end
29
29
 
30
30
  def initialize(source = nil)
@@ -52,7 +52,10 @@ module Kameleoon
52
52
 
53
53
  def count_sendable_data
54
54
  count_sendable_data = @data.count_sendable_data
55
- Logging::KameleoonLogger.debug('CALL/RETURN: Visitor.count_sendable_data -> (count_sendable_data: %s)', count_sendable_data)
55
+ Logging::KameleoonLogger.debug(
56
+ 'CALL/RETURN: Visitor.count_sendable_data -> (count_sendable_data: %s)',
57
+ count_sendable_data
58
+ )
56
59
  count_sendable_data
57
60
  end
58
61
 
@@ -82,7 +85,10 @@ module Kameleoon
82
85
 
83
86
  def operating_system
84
87
  operating_system = @data.operating_system
85
- Logging::KameleoonLogger.debug('CALL/RETURN: Visitor.operating_system -> (operating_system: %s)', operating_system)
88
+ Logging::KameleoonLogger.debug(
89
+ 'CALL/RETURN: Visitor.operating_system -> (operating_system: %s)',
90
+ operating_system
91
+ )
86
92
  operating_system
87
93
  end
88
94
 
@@ -122,7 +128,14 @@ module Kameleoon
122
128
  end
123
129
 
124
130
  def mapping_identifier=(value)
125
- @data.mapping_identifier = value
131
+ return unless @data.mapping_identifier.nil?
132
+
133
+ @data.mutex.with_write_lock do
134
+ if @data.mapping_identifier.nil?
135
+ @data.mapping_identifier = value
136
+ Logging::KameleoonLogger.debug('CALL/RETURN: Visitor.mapping_identifier = %s', value)
137
+ end
138
+ end
126
139
  end
127
140
 
128
141
  def custom_data
@@ -5,8 +5,8 @@ require 'kameleoon/logging/kameleoon_logger'
5
5
  module Kameleoon
6
6
  module Hybrid
7
7
  TC_INIT = 'window.kameleoonQueue=window.kameleoonQueue||[];'
8
- TC_ASSIGN_VARIATION_FORMAT = "window.kameleoonQueue.push(['Experiments.assignVariation',%d,%d]);"
9
- TC_TRIGGER_FORMAT = "window.kameleoonQueue.push(['Experiments.trigger',%d,true]);"
8
+ TC_ASSIGN_VARIATION_FORMAT = "window.kameleoonQueue.push(['Experiments.assignVariation',%d,%d,true]);"
9
+ TC_TRIGGER_FORMAT = "window.kameleoonQueue.push(['Experiments.trigger',%d,%s]);"
10
10
  TC_ASSIGN_VARIATION_TRIGGER_FORMAT = TC_ASSIGN_VARIATION_FORMAT + TC_TRIGGER_FORMAT
11
11
 
12
12
  # Will be useful for Ruby 3.0
@@ -19,10 +19,11 @@ module Kameleoon
19
19
 
20
20
  # Implementation of Cache with auto cleaning feature
21
21
  class ManagerImpl < Manager
22
- def initialize(expiration_time)
22
+ def initialize(expiration_time, data_manager)
23
23
  super()
24
24
  Logging::KameleoonLogger.debug('CALL: HybridManager.new(expiration_time: %s)', expiration_time)
25
25
  @expiration_time = expiration_time
26
+ @data_manager = data_manager
26
27
  Logging::KameleoonLogger.debug('RETURN: HybridManager.new(expiration_time: %s)', expiration_time)
27
28
  end
28
29
 
@@ -34,7 +35,9 @@ module Kameleoon
34
35
  expired_time = (Time.now - @expiration_time).to_i
35
36
  visitor_variations.enumerate do |v|
36
37
  if v.assignment_time > expired_time
37
- v_code = format(TC_ASSIGN_VARIATION_TRIGGER_FORMAT, v.experiment_id, v.variation_id, v.experiment_id)
38
+ tracking_only = !@data_manager.data_file.experiment_js_css_variable?(v.experiment_id)
39
+ v_code = format(TC_ASSIGN_VARIATION_TRIGGER_FORMAT, v.experiment_id, v.variation_id, v.experiment_id,
40
+ tracking_only)
38
41
  tracking_code.push(v_code)
39
42
  end
40
43
  end
@@ -24,6 +24,8 @@ require 'kameleoon/real_time/real_time_configuration_service'
24
24
  require 'kameleoon/storage/cache_factory'
25
25
  require 'kameleoon/targeting/models'
26
26
  require 'kameleoon/targeting/targeting_manager'
27
+ require 'kameleoon/types/variable'
28
+ require 'kameleoon/types/variation'
27
29
  require 'rufus/scheduler'
28
30
  require 'yaml'
29
31
  require 'json'
@@ -59,7 +61,7 @@ module Kameleoon
59
61
  @visitor_manager = Kameleoon::DataManager::VisitorManager.new(
60
62
  @data_manager, config.session_duration_second, @scheduler
61
63
  )
62
- @hybrid_manager = Hybrid::ManagerImpl.new(HYBRID_EXPIRATION_TIME)
64
+ @hybrid_manager = Hybrid::ManagerImpl.new(HYBRID_EXPIRATION_TIME, @data_manager)
63
65
  @network_manager = Network::NetworkManager.new(
64
66
  config.environment,
65
67
  config.default_timeout_millisecond,
@@ -81,6 +83,7 @@ module Kameleoon
81
83
  Logging::KameleoonLogger.log_level = Logging::LogLevel::INFO
82
84
  end
83
85
 
86
+ ObjectSpace.define_finalizer(self, method(:dispose))
84
87
  Logging::KameleoonLogger.info("RETURN: KameleoonClient.new(site_code: '%s', config: %s)",
85
88
  site_code, config)
86
89
  end
@@ -263,22 +266,31 @@ module Kameleoon
263
266
  # @param [String] feature_key Key of the feature flag you want to expose to a user. This field is mandatory.
264
267
  # @param [Bool] is_unique_identifier(DEPRECATED) Parameter that specifies whether the visitorCode is a unique
265
268
  # identifier. This field is optional.
269
+ # @param [Bool] track Optional flag indicating whether tracking of the feature evaluation is enabled
270
+ # (`true`) or disabled (`false`); the default value is `true`.
271
+ #
272
+ # @return [Bool] If the user has not been associated with your feature flag before, the SDK returns
273
+ # a random boolean value (`true` if the user should have this feature or `false` if not).
266
274
  #
267
275
  # @raise [Kameleoon::Exception::FeatureNotFound] Feature Flag isn't found in this configuration
268
276
  # @raise [Kameleoon::Exception::VisitorCodeInvalid] If the visitor code is empty or longer than 255 chars
269
277
  #
270
- def feature_active?(visitor_code, feature_key, is_unique_identifier: nil)
278
+ def feature_active?(visitor_code, feature_key, is_unique_identifier: nil, track: true)
271
279
  Logging::KameleoonLogger.info(
272
- "CALL: KameleoonClient.feature_active?(visitor_code: '%s', feature_key: '%s', is_unique_identifier: %s)",
273
- visitor_code, feature_key, is_unique_identifier
280
+ "CALL: KameleoonClient.feature_active?(visitor_code: '%s', feature_key: '%s', " \
281
+ 'is_unique_identifier: %s, track: %s)',
282
+ visitor_code, feature_key, is_unique_identifier, track
274
283
  )
275
284
  Utils::VisitorCode.validate(visitor_code)
276
285
  set_unique_identifier(visitor_code, is_unique_identifier) unless is_unique_identifier.nil?
277
- _, variation_key = _get_feature_variation_key(visitor_code, feature_key)
286
+ feature_flag = @data_manager.data_file.get_feature_flag(feature_key)
287
+ variation_key, = get_variation_info(visitor_code, feature_flag, track)
278
288
  feature_active = variation_key != Kameleoon::Configuration::VariationType::VARIATION_OFF
289
+ @tracking_manager.add_visitor_code(visitor_code) if track
279
290
  Logging::KameleoonLogger.info(
280
- "RETURN: KameleoonClient.feature_active?(visitor_code: '%s', feature_key: '%s', is_unique_identifier: %s) " \
281
- '-> (feature_active: false)', visitor_code, feature_key, is_unique_identifier
291
+ "RETURN: KameleoonClient.feature_active?(visitor_code: '%s', feature_key: '%s', " \
292
+ 'is_unique_identifier: %s, track: %s) -> (feature_active: %s)',
293
+ visitor_code, feature_key, is_unique_identifier, track, feature_active
282
294
  )
283
295
  feature_active
284
296
  rescue Exception::FeatureEnvironmentDisabled
@@ -286,6 +298,82 @@ module Kameleoon
286
298
  false
287
299
  end
288
300
 
301
+ ##
302
+ # Retrieves the variation assigned to the given visitor for a specific feature flag.
303
+ #
304
+ # @param [String] visitor_code The unique identifier of the visitor.
305
+ # @param [String] feature_key The unique identifier of the feature flag.
306
+ # @param [Bool] track Optional flag indicating whether tracking of the feature evaluation is enabled
307
+ # (`true`) or disabled (`false`); the default value is `true`.
308
+ #
309
+ # @return [Kameleoon::Types::Variation] The variation assigned to the visitor if the visitor is associated with some
310
+ # rule of the feature flag, otherwise the method returns the default variation of the feature flag.
311
+ #
312
+ # @raise [Kameleoon::Exception::VisitorCodeInvalid] If the visitor code is empty or longer than 255 chars.
313
+ # @raise [Kameleoon::Exception::FeatureNotFound] Feature Flag isn't found in this configuration.
314
+ # @raise [Kameleoon::Exception::FeatureEnvironmentDisabled] If the requested feature flag is disabled for
315
+ # the current environment.
316
+ def get_variation(visitor_code, feature_key, track: true)
317
+ Logging::KameleoonLogger.info(
318
+ "CALL: KameleoonClient.get_variation(visitor_code: '%s', feature_key: '%s', track: %s)",
319
+ visitor_code, feature_key, track
320
+ )
321
+ Utils::VisitorCode.validate(visitor_code)
322
+ feature_flag = @data_manager.data_file.get_feature_flag(feature_key)
323
+ variation_key, var_by_exp, rule = get_variation_info(visitor_code, feature_flag, track)
324
+ variation = feature_flag.get_variation_by_key(variation_key)
325
+ external_variation = make_external_variation(variation, var_by_exp&.variation_id, rule&.experiment_id)
326
+ @tracking_manager.add_visitor_code(visitor_code) if track
327
+ Logging::KameleoonLogger.info(
328
+ "RETURN: KameleoonClient.get_variation(visitor_code: '%s', feature_key: '%s', track: %s)" \
329
+ ' -> (variation: %s)',
330
+ visitor_code, feature_key, track, external_variation
331
+ )
332
+ external_variation
333
+ end
334
+
335
+ ##
336
+ # Forms a dictionary of variations assigned to a given visitor across all feature flags.
337
+ # This method iterates over all available feature flags and returns the assigned variation for each flag
338
+ # associated with the specified visitor.
339
+ #
340
+ # @param [String] visitor_code The unique identifier of the visitor.
341
+ # @param [Bool] only_active Optional flag indicating whether to return only variations for active feature
342
+ # flags (`true`) or for any feature flags (`false`); the default value is `false`.
343
+ # @param [Bool] track Optional flag indicating whether tracking of the feature evaluation is enabled
344
+ # (`true`) or disabled (`false`); the default value is `true`.
345
+ #
346
+ # @return [Hash] A hash consisting of feature flag keys as keys (`String`) and their corresponding
347
+ # variations (or the default variation of that feature flag) as values (`Kameleoon::Types::Variation`).
348
+ #
349
+ # @raise [Kameleoon::Exception::VisitorCodeInvalid] If the visitor code is empty or longer than 255 chars.
350
+ def get_variations(visitor_code, only_active: false, track: true)
351
+ Logging::KameleoonLogger.info(
352
+ "CALL: KameleoonClient.get_variations(visitor_code: '%s', only_active: %s, track: %s)",
353
+ visitor_code, only_active, track
354
+ )
355
+ Utils::VisitorCode.validate(visitor_code)
356
+ variations = {}
357
+ @data_manager.data_file.feature_flags.each_value do |feature_flag|
358
+ next unless feature_flag.environment_enabled
359
+
360
+ variation_key, var_by_exp, rule = get_variation_info(visitor_code, feature_flag, track)
361
+ next if only_active && (variation_key == Configuration::VariationType::VARIATION_OFF)
362
+
363
+ variation = feature_flag.get_variation_by_key(variation_key)
364
+ variations[feature_flag.feature_key] =
365
+ make_external_variation(variation, var_by_exp&.variation_id, rule&.experiment_id)
366
+ end
367
+ variations.freeze
368
+ @tracking_manager.add_visitor_code(visitor_code) if track
369
+ Logging::KameleoonLogger.info(
370
+ "RETURN: KameleoonClient.get_variations(visitor_code: '%s', only_active: %s, track: %s)" \
371
+ ' -> (variations: %s)',
372
+ visitor_code, only_active, track, variations
373
+ )
374
+ variations
375
+ end
376
+
289
377
  ##
290
378
  # get_feature_variation_key returns a variation key for visitor code
291
379
  #
@@ -305,7 +393,12 @@ module Kameleoon
305
393
  # @raise [Kameleoon::Exception::FeatureEnvironmentDisabled] If the requested feature flag is disabled for
306
394
  # the current environment
307
395
  #
396
+ # DEPRECATED. Please use `get_variation(visitor_code, feature_key, track: true)` instead.
308
397
  def get_feature_variation_key(visitor_code, feature_key, is_unique_identifier: nil)
398
+ Logging::KameleoonLogger.info(
399
+ '[DEPRECATION] `get_feature_variation_key` is deprecated. ' \
400
+ 'Please use `get_variation(visitor_code, feature_key, track: true)` instead.'
401
+ )
309
402
  Logging::KameleoonLogger.info(
310
403
  "CALL: KameleoonClient.get_feature_variation_key(visitor_code: '%s', feature_key: '%s', " \
311
404
  'is_unique_identifier: %s)',
@@ -339,7 +432,12 @@ module Kameleoon
339
432
  # @raise [Kameleoon::Exception::FeatureEnvironmentDisabled] If the requested feature flag is disabled for
340
433
  # the current environment
341
434
  #
435
+ # DEPRECATED. Please use `get_variation(visitor_code, feature_key, track: true)` instead.
342
436
  def get_feature_variable(visitor_code, feature_key, variable_name, is_unique_identifier: nil)
437
+ Logging::KameleoonLogger.info(
438
+ '[DEPRECATION] `get_feature_variable` is deprecated. ' \
439
+ 'Please use `get_variation(visitor_code, feature_key, track: true)` instead.'
440
+ )
343
441
  Logging::KameleoonLogger.info(
344
442
  "CALL: KameleoonClient.get_feature_variable(visitor_code: '%s', feature_key: '%s', variable_name: '%s', " \
345
443
  'is_unique_identifier: %s)', visitor_code, feature_key, variable_name, is_unique_identifier
@@ -347,7 +445,7 @@ module Kameleoon
347
445
  Utils::VisitorCode.validate(visitor_code)
348
446
  set_unique_identifier(visitor_code, is_unique_identifier) unless is_unique_identifier.nil?
349
447
  feature_flag, variation_key = _get_feature_variation_key(visitor_code, feature_key)
350
- variation = feature_flag.get_variation_key(variation_key)
448
+ variation = feature_flag.get_variation_by_key(variation_key)
351
449
  variable = variation&.get_variable_by_key(variable_name)
352
450
  if variable.nil?
353
451
  raise Exception::FeatureVariableNotFound.new(variable_name),
@@ -376,13 +474,18 @@ module Kameleoon
376
474
  # @raise [Kameleoon::Exception::FeatureVariationNotFound]
377
475
  # @raise [Kameleoon::Exception::FeatureEnvironmentDisabled]
378
476
  #
477
+ # DEPRECATED. Please use `get_variation(visitor_code, feature_key, track: false)` instead.
379
478
  def get_feature_variation_variables(feature_key, variation_key)
479
+ Logging::KameleoonLogger.info(
480
+ '[DEPRECATION] `get_feature_variation_variables` is deprecated. ' \
481
+ 'Please use `get_variation(visitor_code, feature_key, track: false)` instead.'
482
+ )
380
483
  Logging::KameleoonLogger.info(
381
484
  "CALL: KameleoonClient.get_feature_variation_variables(feature_key: '%s', variation_key: '%s')",
382
485
  feature_key, variation_key
383
486
  )
384
487
  feature_flag = @data_manager.data_file.get_feature_flag(feature_key)
385
- variation = feature_flag.get_variation_key(variation_key)
488
+ variation = feature_flag.get_variation_by_key(variation_key)
386
489
  if variation.nil?
387
490
  raise Exception::FeatureVariationNotFound.new(variation_key),
388
491
  "Variation key #{variation_key} not found"
@@ -508,7 +611,7 @@ module Kameleoon
508
611
  #
509
612
  # DEPRECATED. Please use `get_active_features` instead.
510
613
  def get_active_feature_list_for_visitor(visitor_code)
511
- Logging::KameleoonLogger.warning('[DEPRECATION] `get_active_feature_list_for_visitor` is deprecated.' \
614
+ Logging::KameleoonLogger.info('[DEPRECATION] `get_active_feature_list_for_visitor` is deprecated.' \
512
615
  ' Please use `get_active_features` instead.')
513
616
  Logging::KameleoonLogger.info("CALL: KameleoonClient.get_active_feature_list_for_visitor(visitor_code: '%s')",
514
617
  visitor_code)
@@ -537,7 +640,13 @@ module Kameleoon
537
640
  # @raise [Kameleoon::Exception::VisitorCodeInvalid] If the visitor code is empty or longer than 255 chars or is nil
538
641
  #
539
642
  # @return [Hash] Hash of active features for a visitor
643
+ #
644
+ # DEPRECATED. Please use `get_variations(visitor_code, only_active: true, track: false)` instead.
540
645
  def get_active_features(visitor_code)
646
+ Logging::KameleoonLogger.info(
647
+ '[DEPRECATION] `get_active_features` is deprecated. ' \
648
+ 'Please use `get_variations(visitor_code, only_active: true, track: false)` instead.'
649
+ )
541
650
  Logging::KameleoonLogger.info("CALL: KameleoonClient.get_active_features(visitor_code: '%s')", visitor_code)
542
651
  Utils::VisitorCode.validate(visitor_code)
543
652
  map_active_features = {}
@@ -550,24 +659,9 @@ module Kameleoon
550
659
 
551
660
  next if variation_key == Configuration::VariationType::VARIATION_OFF
552
661
 
553
- variation = feature_flag.get_variation_key(variation_key)
554
- variables = {}
555
-
556
- variation&.variables&.each do |variable|
557
- variables[variable.key] = Kameleoon::Types::Variable.new(
558
- variable.key,
559
- variable.type,
560
- _parse_feature_variable(variable)
561
- )
562
- end
563
-
564
- variables.freeze
565
- map_active_features[feature_flag.feature_key] = Kameleoon::Types::Variation.new(
566
- variation_key,
567
- var_by_exp ? var_by_exp.variation_id : nil,
568
- rule ? rule.experiment_id : nil,
569
- variables
570
- )
662
+ variation = feature_flag.get_variation_by_key(variation_key)
663
+ map_active_features[feature_flag.feature_key] =
664
+ make_external_variation(variation, var_by_exp&.variation_id, rule&.experiment_id)
571
665
  end
572
666
 
573
667
  map_active_features.freeze
@@ -754,6 +848,22 @@ module Kameleoon
754
848
  @targeting_manager.check_targeting(visitor_code, campaign_id, exp_ff_rule)
755
849
  end
756
850
 
851
+ def get_variation_info(visitor_code, feature_flag, track)
852
+ Logging::KameleoonLogger.debug(
853
+ "CALL: KameleoonClient.get_variation_info(visitor_code: '%s', feature_flag: %s, track: %s)",
854
+ visitor_code, feature_flag, track
855
+ )
856
+ var_by_exp, rule = _calculate_variation_key_for_feature(visitor_code, feature_flag)
857
+ variation_key = _get_variation_key(var_by_exp, rule, feature_flag)
858
+ save_variation(visitor_code, rule, var_by_exp, track: track)
859
+ Logging::KameleoonLogger.debug(
860
+ "RETURN: KameleoonClient.get_variation_info(visitor_code: '%s', feature_flag: %s, track: %s)" \
861
+ ' -> (variation_key: %s, variation_by_exposition: %s, rule: %s)',
862
+ visitor_code, feature_flag, track, variation_key, var_by_exp, rule
863
+ )
864
+ [variation_key, var_by_exp, rule]
865
+ end
866
+
757
867
  ##
758
868
  # helper method for getting variation key for feature flag
759
869
  def _get_feature_variation_key(visitor_code, feature_key)
@@ -764,16 +874,7 @@ module Kameleoon
764
874
  feature_flag = @data_manager.data_file.get_feature_flag(feature_key)
765
875
  variation, rule = _calculate_variation_key_for_feature(visitor_code, feature_flag)
766
876
  variation_key = _get_variation_key(variation, rule, feature_flag)
767
- visitor = nil
768
- unless rule.nil?
769
- experiment_id = rule.experiment_id
770
- variation_id = variation.variation_id unless variation.nil?
771
- visitor = @visitor_manager.get_or_create_visitor(visitor_code)
772
- unless experiment_id.nil? || variation_id.nil?
773
- as_variation = Kameleoon::DataManager::AssignedVariation.new(experiment_id, variation_id, rule.type)
774
- visitor.assign_variation(as_variation)
775
- end
776
- end
877
+ save_variation(visitor_code, rule, variation)
777
878
  @tracking_manager.add_visitor_code(visitor_code)
778
879
  Logging::KameleoonLogger.debug(
779
880
  "RETURN: KameleoonClient._get_feature_variation_key(visitor_code: '%s', feature_key: '%s')" \
@@ -783,6 +884,25 @@ module Kameleoon
783
884
  [feature_flag, variation_key]
784
885
  end
785
886
 
887
+ def save_variation(visitor_code, rule, var_by_exp, track: true)
888
+ experiment_id = rule&.experiment_id
889
+ variation_id = var_by_exp&.variation_id
890
+ return if experiment_id.nil? || variation_id.nil?
891
+
892
+ Logging::KameleoonLogger.debug(
893
+ "CALL: KameleoonClient.save_variation(visitor_code: '%s', rule: %s, var_by_exp: %s, track: %s)",
894
+ visitor_code, rule, var_by_exp, track
895
+ )
896
+ visitor = @visitor_manager.get_or_create_visitor(visitor_code)
897
+ as_variation = Kameleoon::DataManager::AssignedVariation.new(experiment_id, variation_id, rule.type)
898
+ as_variation.mark_as_sent unless track
899
+ visitor.assign_variation(as_variation)
900
+ Logging::KameleoonLogger.debug(
901
+ "RETURN: KameleoonClient.save_variation(visitor_code: '%s', rule: %s, var_by_exp: %s, track: %s)",
902
+ visitor_code, rule, var_by_exp, track
903
+ )
904
+ end
905
+
786
906
  ##
787
907
  # helper method for calculate variation key for feature flag
788
908
  def _calculate_variation_key_for_feature(visitor_code, feature_flag)
@@ -845,22 +965,47 @@ module Kameleoon
845
965
  feature_flag.default_variation_key
846
966
  end
847
967
 
968
+ def make_external_variation(internal_variation, variation_id, experiment_id)
969
+ Logging::KameleoonLogger.debug(
970
+ 'CALL: KameleoonClient.make_external_variation(internal_variation: %s, variation_id: %s, experiment_id: %s)',
971
+ internal_variation, variation_id, experiment_id
972
+ )
973
+ variables = {}
974
+ internal_variation&.variables&.each do |variable|
975
+ variables[variable.key] = Types::Variable.new(
976
+ variable.key,
977
+ variable.type,
978
+ _parse_feature_variable(variable)
979
+ )
980
+ end
981
+ variables.freeze
982
+ variation = Types::Variation.new(internal_variation&.key, variation_id, experiment_id, variables)
983
+ Logging::KameleoonLogger.debug(
984
+ 'RETURN: KameleoonClient.make_external_variation(internal_variation: %s, variation_id: %s, experiment_id: %s)' \
985
+ ' -> (variation: %s)',
986
+ internal_variation, variation_id, experiment_id, variation
987
+ )
988
+ variation
989
+ end
990
+
848
991
  ##
849
992
  # helper method for fetching values from a Variable
850
993
  def _parse_feature_variable(variable)
851
994
  case variable.type
852
- when 'BOOLEAN', 'STRING', 'NUMBER'
995
+ when 'BOOLEAN', 'STRING', 'NUMBER', 'JS', 'CSS'
853
996
  variable.value
854
997
  when 'JSON'
855
998
  JSON.parse(variable.value)
856
999
  else
857
- raise TypeError.new('Unknown type for feature variable'),
858
- 'Unknown type for feature variable'
1000
+ Logging::KameleoonLogger.error("Unknown type '#{variable.type}' for feature variable")
1001
+ variable.value
859
1002
  end
860
1003
  end
861
1004
 
862
1005
  def set_unique_identifier(visitor_code, is_unique_identifier)
863
- Logging::KameleoonLogger.warning("The 'isUniqueIdentifier' parameter is deprecated. Please, add 'UniqueIdentifier' to a visitor instead.")
1006
+ Logging::KameleoonLogger.info(
1007
+ "The 'isUniqueIdentifier' parameter is deprecated. Please, add 'UniqueIdentifier' to a visitor instead."
1008
+ )
864
1009
  @visitor_manager.add_data(visitor_code, UniqueIdentifier.new(is_unique_identifier))
865
1010
  end
866
1011
  end
@@ -113,7 +113,7 @@ module Kameleoon
113
113
  'Setting top level domain is strictly recommended, otherwise you may have problems when using subdomains.'
114
114
  )
115
115
  end
116
- @top_level_domain = top_level_domain
116
+ @top_level_domain = Utils::Domain.validate_top_level_domain(top_level_domain)
117
117
  end
118
118
 
119
119
  def self.read_from_yaml(path)
@@ -16,18 +16,18 @@ module Kameleoon
16
16
  @container = Container.new(value)
17
17
  end
18
18
 
19
- def consent_required?
20
- @container.is_consent_required
19
+ def visitor_code_managed?
20
+ @container.visitor_code_managed
21
21
  end
22
22
 
23
23
  class Container
24
- attr_reader :data_file, :is_consent_required
24
+ attr_reader :data_file, :visitor_code_managed
25
25
 
26
26
  def initialize(data_file)
27
27
  @data_file = data_file
28
28
  # Regarding GDPR policy we should set visitorCode if legal consent isn't required or we have at
29
29
  # least one Targeted Delivery rule in datafile
30
- @is_consent_required = data_file.settings.is_consent_required && !data_file.has_any_targeted_delivery_rule
30
+ @visitor_code_managed = data_file.settings.is_consent_required && !data_file.has_any_targeted_delivery_rule
31
31
  end
32
32
  end
33
33
  end
@@ -42,12 +42,20 @@ module Kameleoon
42
42
  filter = Types::RemoteVisitorDataFilter.new unless filter.is_a?(Types::RemoteVisitorDataFilter)
43
43
  is_unique_identifier = @visitor_manger.get_visitor(visitor_code)&.is_unique_identifier || false
44
44
  response = @network_manager.get_remote_visitor_data(visitor_code, filter, is_unique_identifier, timeout)
45
- (data_to_add, data_to_return) = parse_custom_data_array(visitor_code, response)
45
+ remote_visitor_data = parse_custom_data_array(visitor_code, response)
46
+ remote_visitor_data.mark_data_as_sent(@data_manager.data_file.custom_data_info)
47
+ data_to_add = remote_visitor_data.collect_data_to_add
46
48
  if add_data && !data_to_add.empty?
47
49
  # Cannot use `VisitorManager.add_data` because it could use remote visitor data for mapping.
48
50
  visitor = @visitor_manger.get_or_create_visitor(visitor_code)
49
51
  visitor.add_data(*data_to_add, overwrite: false)
50
52
  end
53
+ if filter.visitor_code && !remote_visitor_data.visitor_code.nil?
54
+ # We apply visitor code from the latest visit fetched from Data API
55
+ visitor = @visitor_manger.get_or_create_visitor(visitor_code)
56
+ visitor.mapping_identifier = remote_visitor_data.visitor_code
57
+ end
58
+ data_to_return = remote_visitor_data.collect_data_to_return
51
59
  Logging::KameleoonLogger.debug(
52
60
  "RETURN: RemoteDataManager.get_visitor_data(visitor_code: '%s', add_data: %s, filter: %s," \
53
61
  ' timeout: %s) -> (visitor_data: %s)',
@@ -59,9 +67,7 @@ module Kameleoon
59
67
  ##
60
68
  # helper method used by `get_remote_visitor_data`
61
69
  def parse_custom_data_array(visitor_code, response)
62
- remote_visitor_data = RemoteVisitorData.new(JSON.parse(response))
63
- remote_visitor_data.mark_data_as_sent(@data_manager.data_file.custom_data_info)
64
- [remote_visitor_data.collect_data_to_add, remote_visitor_data.collect_data_to_return]
70
+ RemoteVisitorData.new(JSON.parse(response))
65
71
  rescue StandardError => e
66
72
  Logging::KameleoonLogger.error("Parsing of remote visitor data of '#{visitor_code}' failed: #{e}")
67
73
  raise
@@ -16,7 +16,7 @@ module Kameleoon
16
16
  module RemoteData
17
17
  class RemoteVisitorData
18
18
  attr_reader :custom_data_dict, :page_view_visits, :conversions, :experiments, :device, :browser,
19
- :operating_system, :geolocation, :previous_visitor_visits, :kcs_heat
19
+ :operating_system, :geolocation, :previous_visitor_visits, :kcs_heat, :visitor_code
20
20
 
21
21
  def initialize(hash)
22
22
  current_visit = hash['currentVisit']
@@ -68,18 +68,20 @@ module Kameleoon
68
68
  private
69
69
 
70
70
  def parse_visit(hash)
71
+ @visitor_code = hash['visitorCode'] if @visitor_code.nil?
71
72
  custom_data_events = hash['customDataEvents']
72
- parse_custom_data(custom_data_events) if custom_data_events != nil && custom_data_events.size.positive?
73
+ parse_custom_data(custom_data_events) if !custom_data_events.nil? && custom_data_events.size.positive?
73
74
  page_events = hash['pageEvents']
74
- parse_pages(page_events) if page_events != nil && page_events.size.positive?
75
+ parse_pages(page_events) if !page_events.nil? && page_events.size.positive?
75
76
  experiment_events = hash['experimentEvents']
76
- parse_experiments(experiment_events) if experiment_events != nil && experiment_events.size.positive?
77
+ parse_experiments(experiment_events) if !experiment_events.nil? && experiment_events.size.positive?
77
78
  conversion_events = hash['conversionEvents']
78
- parse_conversions(conversion_events) if conversion_events != nil && conversion_events.size.positive?
79
+ parse_conversions(conversion_events) if !conversion_events.nil? && conversion_events.size.positive?
79
80
  geolocation_events = hash['geolocationEvents']
80
- @geolocation = parse_geolocation(geolocation_events) if @geolocation.nil? && geolocation_events != nil && geolocation_events.size.positive?
81
+ @geolocation = parse_geolocation(geolocation_events) if @geolocation.nil? && !geolocation_events.nil? && \
82
+ geolocation_events.size.positive?
81
83
  static_data_events = hash['staticDataEvent']
82
- parse_static_data(static_data_events) if static_data_events != nil
84
+ parse_static_data(static_data_events) unless static_data_events.nil?
83
85
  end
84
86
 
85
87
  def parse_custom_data(custom_data_events)
@@ -80,12 +80,13 @@ module Kameleoon
80
80
  Thread.new do
81
81
  result = @network_manager.send_tracking_data(lines)
82
82
  if result != false
83
- Logging::KameleoonLogger.debug('Successful request for tracking visitors: %s, data: %s',
84
- visitor_codes, visitor_data)
83
+ Logging::KameleoonLogger.info('Successful request for tracking visitors: %s, data: %s',
84
+ visitor_codes, visitor_data)
85
85
  visitor_data.each { |d| d.mark_as_sent if d.is_a?(Kameleoon::Data) }
86
86
  else
87
- Logging::KameleoonLogger.debug('Failed request for tracking visitors: %s, data: %s',
88
- visitor_codes, visitor_data)
87
+ Logging::KameleoonLogger.error('Tracking request failed')
88
+ Logging::KameleoonLogger.info('Failed request for tracking visitors: %s, data: %s',
89
+ visitor_codes, visitor_data)
89
90
  visitor_data.each { |d| d.mark_as_unsent if d.is_a?(Kameleoon::Data) }
90
91
  @tracking_visitors.add_all(visitor_codes)
91
92
  end
@@ -31,7 +31,7 @@ module Kameleoon
31
31
  Utils::VisitorCode.validate(visitor_code)
32
32
  Logging::KameleoonLogger.debug("Read visitor code '%s' from cookies %s", visitor_code, cookies)
33
33
  # Remove adding cookies when we will be sure that it doesn't break anything
34
- add(visitor_code, cookies) unless @data_manager.consent_required?
34
+ add(visitor_code, cookies) unless @data_manager.visitor_code_managed?
35
35
  Logging::KameleoonLogger.debug(
36
36
  "RETURN: CookieManager.get_or_add(cookies: %s, default_visitor_code: '%s') -> (visitor_code: '%s')",
37
37
  cookies, default_visitor_code, visitor_code
@@ -42,7 +42,7 @@ module Kameleoon
42
42
  if default_visitor_code.nil?
43
43
  visitor_code = Utils::VisitorCode.generate
44
44
  Logging::KameleoonLogger.debug("Generated new visitor code '%s'", visitor_code)
45
- add(visitor_code, cookies) unless @data_manager.consent_required?
45
+ add(visitor_code, cookies) unless @data_manager.visitor_code_managed?
46
46
  Logging::KameleoonLogger.debug(
47
47
  "RETURN: CookieManager.get_or_add(cookies: %s, default_visitor_code: '%s') -> (visitor_code: '%s')",
48
48
  cookies, default_visitor_code, visitor_code
@@ -56,7 +56,8 @@ module Kameleoon
56
56
  add(visitor_code, cookies)
57
57
  Logging::KameleoonLogger.debug(
58
58
  "RETURN: CookieManager.get_or_add(cookies: %s, default_visitor_code: '%s') -> (visitor_code: '%s')",
59
- cookies, default_visitor_code, visitor_code)
59
+ cookies, default_visitor_code, visitor_code
60
+ )
60
61
  visitor_code
61
62
  end
62
63
 
@@ -72,7 +73,7 @@ module Kameleoon
72
73
  remove(cookies)
73
74
  end
74
75
  Logging::KameleoonLogger.debug("RETURN: CookieManager.update(visitor_code: '%s', consent: %s, cookies: %s)",
75
- visitor_code, consent, cookies)
76
+ visitor_code, consent, cookies)
76
77
  end
77
78
 
78
79
  private
@@ -88,13 +89,13 @@ module Kameleoon
88
89
  }
89
90
  cookies[VISITOR_CODE_COOKIE] = cookie
90
91
  Logging::KameleoonLogger.debug("RETURN: CookieManager.add(visitor_code: '%s', cookies: %s)",
91
- visitor_code, cookies)
92
+ visitor_code, cookies)
92
93
  cookies
93
94
  end
94
95
 
95
96
  def remove(cookies)
96
97
  Logging::KameleoonLogger.debug('CALL: CookieManager.remove(cookies: %s)', cookies)
97
- cookies[VISITOR_CODE_COOKIE] = nil if @data_manager.consent_required?
98
+ cookies[VISITOR_CODE_COOKIE] = nil if @data_manager.visitor_code_managed?
98
99
  Logging::KameleoonLogger.debug('RETURN: CookieManager.remove(cookies: %s)', cookies)
99
100
  cookies
100
101
  end
@@ -88,11 +88,13 @@ module Kameleoon
88
88
  if response.success?
89
89
  success = true
90
90
  elsif !response.error.nil?
91
- Logging::KameleoonLogger.warning("%s call '%s' failed: Error occurred during request: %s",
92
- request.method, request.url, response.error)
91
+ log_level = attempt < retry_limit ? Logging::LogLevel::WARNING : Logging::LogLevel::ERROR
92
+ Logging::KameleoonLogger.log(log_level, "%s call '%s' failed: Error occurred during request: %s",
93
+ request.method, request.url, response.error)
93
94
  else
94
- Logging::KameleoonLogger.warning("%s call '%s' failed: Received unexpected status code '%s'",
95
- request.method, request.url, response.code)
95
+ log_level = attempt < retry_limit ? Logging::LogLevel::WARNING : Logging::LogLevel::ERROR
96
+ Logging::KameleoonLogger.log(log_level, "%s call '%s' failed: Received unexpected status code '%s'",
97
+ request.method, request.url, response.code)
96
98
  if response.code == 401 && request.access_token
97
99
  Logging::KameleoonLogger.warning("Unexpected rejection of access token '#{request.access_token}'")
98
100
  @access_token_source.discard_token(request.access_token)
@@ -108,7 +110,6 @@ module Kameleoon
108
110
  attempt += 1
109
111
  end
110
112
  Logging::KameleoonLogger.debug('Fetched response %s for request %s', response, request)
111
- Logging::KameleoonLogger.error("Failed %s request '%s'", request.method, request.url) unless success
112
113
  success ? response.body : false
113
114
  end
114
115
 
@@ -19,7 +19,7 @@ module Kameleoon
19
19
  DEFAULT_DATA_API_DOMAIN = 'data.kameleoon.io'
20
20
  TEST_DATA_API_DOMAIN = 'data.kameleoon.net'
21
21
  DEFAULT_AUTOMATION_API_DOMAIN = 'api.kameleoon.com'
22
- TEST_AUTOMATION_API_DOMAIN = 'api.master.preview.kameleoon.net'
22
+ TEST_AUTOMATION_API_DOMAIN = 'api.kameleoon.net'
23
23
 
24
24
  attr_reader :site_code, :data_api_domain, :automation_api_domain
25
25
 
@@ -13,15 +13,17 @@ module Kameleoon
13
13
  super(json_condition, json_condition['title'])
14
14
  end
15
15
 
16
- def check(page_view_visit_storage)
17
- return false unless page_view_visit_storage.is_a?(Kameleoon::DataManager::DataMapStorage)
16
+ def check(page_view_visits)
17
+ return false unless page_view_visits.is_a?(Kameleoon::DataManager::DataMapStorage)
18
18
 
19
- is_targeted = false
20
- page_view_visit_storage.enumerate do |visit|
21
- is_targeted = visit.is_a?(Kameleoon::DataManager::PageViewVisit) && check_targeting(visit.page_view.title)
22
- break if is_targeted
19
+ latest = nil
20
+ page_view_visits.enumerate do |visit|
21
+ if visit.is_a?(Kameleoon::DataManager::PageViewVisit) \
22
+ && (latest.nil? || visit.last_timestamp > latest.last_timestamp)
23
+ latest = visit
24
+ end
23
25
  end
24
- is_targeted
26
+ !latest.nil? && latest.is_a?(Kameleoon::DataManager::PageViewVisit) && check_targeting(latest.page_view.title)
25
27
  end
26
28
  end
27
29
  end
@@ -13,22 +13,17 @@ module Kameleoon
13
13
  super(json_condition, json_condition['url'])
14
14
  end
15
15
 
16
- def check(page_view_visit_storage)
17
- return false unless page_view_visit_storage.is_a?(Kameleoon::DataManager::DataMapStorage)
18
- return !page_view_visit_storage.get(condition_value).nil? if operator == Operator::EXACT
16
+ def check(page_view_visits)
17
+ return false unless page_view_visits.is_a?(Kameleoon::DataManager::DataMapStorage)
19
18
 
20
- is_targeted = false
21
- page_view_visit_storage.enumerate do |visit|
22
- is_targeted = check_page_view_visit(visit)
23
- break if is_targeted
19
+ latest = nil
20
+ page_view_visits.enumerate do |visit|
21
+ if visit.is_a?(Kameleoon::DataManager::PageViewVisit) \
22
+ && (latest.nil? || visit.last_timestamp > latest.last_timestamp)
23
+ latest = visit
24
+ end
24
25
  end
25
- is_targeted
26
- end
27
-
28
- private
29
-
30
- def check_page_view_visit(visit)
31
- visit.is_a?(Kameleoon::DataManager::PageViewVisit) && check_targeting(visit.page_view.url)
26
+ !latest.nil? && latest.is_a?(Kameleoon::DataManager::PageViewVisit) && check_targeting(latest.page_view.url)
32
27
  end
33
28
  end
34
29
  end
@@ -5,7 +5,7 @@ module Kameleoon
5
5
  module Types
6
6
  class RemoteVisitorDataFilter
7
7
  attr_reader :previous_visit_amount, :current_visit, :custom_data, :page_views, :geolocation, :device, :browser,
8
- :operating_system, :conversions, :experiments, :kcs
8
+ :operating_system, :conversions, :experiments, :kcs, :visitor_code
9
9
 
10
10
  def to_s
11
11
  "RemoteVisitorDataFilter{previous_visit_amount:#{@previous_visit_amount}," \
@@ -17,13 +17,15 @@ module Kameleoon
17
17
  "browser:#{@browser}," \
18
18
  "operating_system:#{@operating_system}," \
19
19
  "conversions:#{@conversions}," \
20
- "experiments:#{@experiments},kcs:#{@kcs}}"
20
+ "experiments:#{@experiments}," \
21
+ "kcs:#{@kcs}," \
22
+ "visitor_code:#{@visitor_code}}"
21
23
  end
22
24
 
23
25
  def initialize(
24
26
  previous_visit_amount: 1, current_visit: true, custom_data: true, page_views: false,
25
27
  geolocation: false, device: false, browser: false, operating_system: false, conversions: false,
26
- experiments: false, kcs: false
28
+ experiments: false, kcs: false, visitor_code: true
27
29
  )
28
30
  @previous_visit_amount = previous_visit_amount
29
31
  @current_visit = current_visit
@@ -36,6 +38,7 @@ module Kameleoon
36
38
  @conversions = conversions
37
39
  @experiments = experiments
38
40
  @kcs = kcs
41
+ @visitor_code = visitor_code
39
42
  end
40
43
  end
41
44
  end
@@ -17,6 +17,10 @@ module Kameleoon
17
17
  @experiment_id = experiment_id
18
18
  @variables = variables
19
19
  end
20
+
21
+ def active?
22
+ variation_key != Configuration::VariationType::VARIATION_OFF
23
+ end
20
24
  end
21
25
  end
22
26
  end
@@ -4,6 +4,7 @@ require 'fiber'
4
4
  require 'bigdecimal'
5
5
  require 'kameleoon/utils'
6
6
  require 'kameleoon/exceptions'
7
+ require 'kameleoon/logging/kameleoon_logger'
7
8
 
8
9
  module Kameleoon
9
10
  # Utils is a helper module for project
@@ -68,5 +69,35 @@ module Kameleoon
68
69
  secret[0, length - hidden_length] + hid_ch * hidden_length
69
70
  end
70
71
  end
72
+
73
+ module Domain
74
+ HTTP = 'http://'
75
+ HTTPS = 'https://'
76
+ REGEX_DOMAIN = /^(\.?(([a-zA-Z\d][a-zA-Z\d-]*[a-zA-Z\d])|[a-zA-Z\d]))
77
+ (\.(([a-zA-Z\d][a-zA-Z\d-]*[a-zA-Z\d])|[a-zA-Z\d])){1,126}$/x.freeze
78
+
79
+ def self.validate_top_level_domain(top_level_domain)
80
+ return nil if top_level_domain.nil? || top_level_domain.empty?
81
+
82
+ top_level_domain = top_level_domain.downcase
83
+
84
+ [HTTP, HTTPS].each do |protocol|
85
+ next unless top_level_domain.start_with?(protocol)
86
+
87
+ top_level_domain = top_level_domain[protocol.length..]
88
+ Logging::KameleoonLogger.warning(
89
+ "The top-level domain contains '%s'. Domain after protocol trimming: '%s'", protocol, top_level_domain
90
+ )
91
+ break
92
+ end
93
+
94
+ unless REGEX_DOMAIN.match?(top_level_domain)
95
+ Logging::KameleoonLogger.error("The top-level domain '%s' is invalid.", top_level_domain)
96
+ return nil
97
+ end
98
+
99
+ top_level_domain
100
+ end
101
+ end
71
102
  end
72
103
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kameleoon
4
- SDK_VERSION = '3.4.0'
4
+ SDK_VERSION = '3.6.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.4.0
4
+ version: 3.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kameleoon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-15 00:00:00.000000000 Z
11
+ date: 2024-11-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: em-http-request