kameleoon-client-ruby 3.17.3 → 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 +4 -4
- data/lib/kameleoon/configuration/data_file.rb +9 -4
- data/lib/kameleoon/configuration/variable.rb +12 -0
- data/lib/kameleoon/configuration/variation.rb +1 -1
- data/lib/kameleoon/data/application_version.rb +21 -0
- data/lib/kameleoon/data/data.rb +1 -0
- data/lib/kameleoon/data/manager/visitor.rb +16 -1
- data/lib/kameleoon/data/manager/visitor_manager.rb +16 -5
- data/lib/kameleoon/kameleoon_client.rb +25 -77
- data/lib/kameleoon/kameleoon_client_config.rb +2 -2
- data/lib/kameleoon/managers/data/data_manager.rb +16 -2
- data/lib/kameleoon/managers/tracking/tracking_builder.rb +1 -1
- data/lib/kameleoon/network/access_token_source.rb +9 -1
- data/lib/kameleoon/network/net_provider.rb +4 -1
- data/lib/kameleoon/network/network_manager.rb +2 -3
- data/lib/kameleoon/network/request.rb +6 -3
- data/lib/kameleoon/network/url_provider.rb +21 -3
- data/lib/kameleoon/sem_version.rb +55 -0
- data/lib/kameleoon/targeting/condition.rb +1 -0
- data/lib/kameleoon/targeting/condition_factory.rb +3 -0
- data/lib/kameleoon/targeting/conditions/browser_condition.rb +2 -2
- data/lib/kameleoon/targeting/conditions/sdk_language_condition.rb +5 -33
- data/lib/kameleoon/targeting/conditions/unknown_condition.rb +1 -0
- data/lib/kameleoon/targeting/conditions/version_condition.rb +55 -0
- data/lib/kameleoon/targeting/targeting_manager.rb +2 -0
- data/lib/kameleoon/types/data_file.rb +14 -3
- data/lib/kameleoon/types/feature_flag.rb +22 -0
- data/lib/kameleoon/types/rule.rb +21 -0
- data/lib/kameleoon/types/variable.rb +8 -0
- data/lib/kameleoon/types/variation.rb +12 -0
- data/lib/kameleoon/version.rb +1 -1
- metadata +5 -3
- data/lib/kameleoon/sdk_version.rb +0 -31
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dde5f6ebb9e6779b9bf8d4edbdded4e11b82aa642ec046bd2493306e0b2a7ad8
|
|
4
|
+
data.tar.gz: 5a15f627b2832ec10f001ef7ee061fa31c6dbd618b02ec55ff9d1d66ee17d5a8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'data'
|
|
4
|
+
|
|
5
|
+
module Kameleoon
|
|
6
|
+
# ApplicationVersion contains information about the application version on the visitor's device.
|
|
7
|
+
# It is used for targeting by application version.
|
|
8
|
+
class ApplicationVersion < Data
|
|
9
|
+
attr_reader :version
|
|
10
|
+
|
|
11
|
+
# @param [String] version Application version (semantic versioning: major, major.minor, or major.minor.patch)
|
|
12
|
+
def initialize(version)
|
|
13
|
+
super(DataType::APPLICATION_VERSION)
|
|
14
|
+
@version = version
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_s
|
|
18
|
+
"ApplicationVersion{version:#{@version}}"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/kameleoon/data/data.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'concurrent'
|
|
4
4
|
require 'time'
|
|
5
|
+
require 'kameleoon/data/application_version'
|
|
5
6
|
require 'kameleoon/data/browser'
|
|
6
7
|
require 'kameleoon/data/cbscores'
|
|
7
8
|
require 'kameleoon/data/conversion'
|
|
@@ -103,6 +104,13 @@ module Kameleoon
|
|
|
103
104
|
operating_system
|
|
104
105
|
end
|
|
105
106
|
|
|
107
|
+
def application_version
|
|
108
|
+
application_version = @data.application_version
|
|
109
|
+
Logging::KameleoonLogger.debug('CALL/RETURN: Visitor.application_version -> (application_version: %s)',
|
|
110
|
+
application_version)
|
|
111
|
+
application_version
|
|
112
|
+
end
|
|
113
|
+
|
|
106
114
|
def cookie
|
|
107
115
|
cookie = @data.cookie
|
|
108
116
|
Logging::KameleoonLogger.debug('CALL/RETURN: Visitor.cookie -> (cookie: %s)', cookie)
|
|
@@ -275,6 +283,8 @@ module Kameleoon
|
|
|
275
283
|
@data.cookie = data
|
|
276
284
|
when OperatingSystem
|
|
277
285
|
@data.set_operating_system(data, overwrite)
|
|
286
|
+
when ApplicationVersion
|
|
287
|
+
@data.set_application_version(data, overwrite)
|
|
278
288
|
when Geolocation
|
|
279
289
|
@data.set_geolocation(data, overwrite)
|
|
280
290
|
when KcsHeat
|
|
@@ -305,7 +315,8 @@ module Kameleoon
|
|
|
305
315
|
end
|
|
306
316
|
|
|
307
317
|
class VisitorData
|
|
308
|
-
attr_reader :time_started, :mutex, :device, :browser, :geolocation, :operating_system, :visitor_visits
|
|
318
|
+
attr_reader :time_started, :mutex, :device, :browser, :geolocation, :operating_system, :visitor_visits,
|
|
319
|
+
:application_version
|
|
309
320
|
attr_accessor :last_activity_time, :legal_consent, :user_agent, :cookie, :kcs_heat, :cbscores,
|
|
310
321
|
:mapping_identifier, :forced_variations, :simulated_variations
|
|
311
322
|
|
|
@@ -462,6 +473,10 @@ module Kameleoon
|
|
|
462
473
|
@operating_system = operating_system if overwrite || @operating_system.nil?
|
|
463
474
|
end
|
|
464
475
|
|
|
476
|
+
def set_application_version(application_version, overwrite)
|
|
477
|
+
@application_version = application_version if overwrite || @application_version.nil?
|
|
478
|
+
end
|
|
479
|
+
|
|
465
480
|
def set_cbscores(cbs, overwrite)
|
|
466
481
|
@cbscores = cbs if overwrite || @cbscores.nil?
|
|
467
482
|
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
|
|
@@ -72,10 +82,10 @@ module Kameleoon
|
|
|
72
82
|
@visitors.size
|
|
73
83
|
end
|
|
74
84
|
|
|
75
|
-
def add_data(visitor_code, *args, overwrite: true)
|
|
85
|
+
def add_data(visitor_code, *args, overwrite: true, track: true)
|
|
76
86
|
Logging::KameleoonLogger.debug(
|
|
77
|
-
"CALL: VisitorManager.add_data(visitor_code: '%s', args: %s, overwrite: %s)",
|
|
78
|
-
visitor_code, args, overwrite
|
|
87
|
+
"CALL: VisitorManager.add_data(visitor_code: '%s', args: %s, overwrite: %s, track: %s)",
|
|
88
|
+
visitor_code, args, overwrite, track
|
|
79
89
|
)
|
|
80
90
|
visitor = get_or_create_visitor(visitor_code)
|
|
81
91
|
cdi = @data_manager.data_file.custom_data_info
|
|
@@ -87,12 +97,13 @@ module Kameleoon
|
|
|
87
97
|
when Conversion
|
|
88
98
|
data = process_conversion(data, cdi)
|
|
89
99
|
end
|
|
100
|
+
data.mark_as_sent if !track && data.respond_to?(:mark_as_sent)
|
|
90
101
|
args[i] = data
|
|
91
102
|
end
|
|
92
103
|
visitor.add_data(*args, overwrite: overwrite)
|
|
93
104
|
Logging::KameleoonLogger.debug(
|
|
94
|
-
"RETURN: VisitorManager.add_data(visitor_code: '%s', args: %s, overwrite: %s) -> (visitor)",
|
|
95
|
-
visitor_code, args, overwrite
|
|
105
|
+
"RETURN: VisitorManager.add_data(visitor_code: '%s', args: %s, overwrite: %s, track: %s) -> (visitor)",
|
|
106
|
+
visitor_code, args, overwrite, track
|
|
96
107
|
)
|
|
97
108
|
visitor
|
|
98
109
|
end
|
|
@@ -162,17 +162,18 @@ module Kameleoon
|
|
|
162
162
|
#
|
|
163
163
|
# @param [String] visitor_code Visitor code
|
|
164
164
|
# @param [...Data] data Data to associate with the visitor code
|
|
165
|
+
# @param [Bool] track A boolean indicating whether the data should be tracked (sent to server). Default is true.
|
|
165
166
|
#
|
|
166
167
|
# @raise [Kameleoon::Exception::VisitorCodeInvalid] If the visitor code is empty or longer than 255 chars
|
|
167
168
|
#
|
|
168
|
-
def add_data(visitor_code, *args)
|
|
169
|
-
Logging::KameleoonLogger.info("CALL: KameleoonClient.add_data(visitor_code: '%s', args: %s)",
|
|
170
|
-
visitor_code, args)
|
|
169
|
+
def add_data(visitor_code, *args, track: true)
|
|
170
|
+
Logging::KameleoonLogger.info("CALL: KameleoonClient.add_data(visitor_code: '%s', args: %s, track: %s)",
|
|
171
|
+
visitor_code, args, track)
|
|
171
172
|
Utils::VisitorCode.validate(visitor_code)
|
|
172
|
-
@visitor_manager.add_data(visitor_code, *args)
|
|
173
|
+
@visitor_manager.add_data(visitor_code, *args, track: track)
|
|
173
174
|
Logging::KameleoonLogger.info(
|
|
174
|
-
"RETURN: KameleoonClient.add_data(visitor_code: '%s', args: %s)",
|
|
175
|
-
visitor_code, args
|
|
175
|
+
"RETURN: KameleoonClient.add_data(visitor_code: '%s', args: %s, track: %s)",
|
|
176
|
+
visitor_code, args, track
|
|
176
177
|
)
|
|
177
178
|
end
|
|
178
179
|
|
|
@@ -368,8 +369,6 @@ module Kameleoon
|
|
|
368
369
|
Utils::VisitorCode.validate(visitor_code)
|
|
369
370
|
variations = {}
|
|
370
371
|
@data_manager.data_file.feature_flags.each_value do |feature_flag|
|
|
371
|
-
next unless feature_flag.environment_enabled
|
|
372
|
-
|
|
373
372
|
begin
|
|
374
373
|
variation_key, eval_exp = get_variation_info(visitor_code, feature_flag, track)
|
|
375
374
|
rescue Exception::FeatureEnvironmentDisabled
|
|
@@ -467,7 +466,7 @@ module Kameleoon
|
|
|
467
466
|
raise Exception::FeatureVariableNotFound.new(variable_name),
|
|
468
467
|
"Feature variable #{variable_name} not found"
|
|
469
468
|
end
|
|
470
|
-
value =
|
|
469
|
+
value = variable.get_value
|
|
471
470
|
Logging::KameleoonLogger.info(
|
|
472
471
|
"RETURN: KameleoonClient.get_feature_variable(visitor_code: '%s', feature_key: '%s', variable_name: '%s', " \
|
|
473
472
|
'is_unique_identifier: %s) -> (variable: %s)',
|
|
@@ -502,12 +501,13 @@ module Kameleoon
|
|
|
502
501
|
)
|
|
503
502
|
feature_flag = @data_manager.data_file.get_feature_flag(feature_key)
|
|
504
503
|
variation = feature_flag.get_variation_by_key(variation_key)
|
|
504
|
+
@data_manager.data_file.ensure_environment_enabled(feature_flag)
|
|
505
505
|
if variation.nil?
|
|
506
506
|
raise Exception::FeatureVariationNotFound.new(variation_key),
|
|
507
507
|
"Variation key #{variation_key} not found"
|
|
508
508
|
end
|
|
509
509
|
variables = {}
|
|
510
|
-
variation.variables.each { |var| variables[var.key] =
|
|
510
|
+
variation.variables.each { |var| variables[var.key] = var.get_value }
|
|
511
511
|
Logging::KameleoonLogger.info(
|
|
512
512
|
"RETURN: KameleoonClient.get_feature_variation_variables(feature_key: '%s', variation_key: '%s') " \
|
|
513
513
|
'-> (variables: %s)', feature_key, variation_key, variables
|
|
@@ -606,12 +606,16 @@ module Kameleoon
|
|
|
606
606
|
)
|
|
607
607
|
warehouse_audience
|
|
608
608
|
end
|
|
609
|
-
|
|
610
609
|
##
|
|
611
|
-
# Returns a list of all feature flag keys
|
|
610
|
+
# Returns a list of all feature flag keys.
|
|
612
611
|
#
|
|
613
612
|
# @return [Array] array of all feature flag keys
|
|
613
|
+
#
|
|
614
|
+
# DEPRECATED. Please use `get_data_file` instead.
|
|
614
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
|
+
)
|
|
615
619
|
Logging::KameleoonLogger.info('CALL: KameleoonClient.get_feature_list')
|
|
616
620
|
features = @data_manager.data_file.feature_flags.keys
|
|
617
621
|
Logging::KameleoonLogger.info('RETURN: KameleoonClient.get_feature_list -> (features: %s)', features)
|
|
@@ -637,8 +641,6 @@ module Kameleoon
|
|
|
637
641
|
visitor = @visitor_manager.get_visitor(visitor_code)
|
|
638
642
|
list_keys = []
|
|
639
643
|
@data_manager.data_file.feature_flags.each do |feature_key, feature_flag|
|
|
640
|
-
next unless feature_flag.environment_enabled
|
|
641
|
-
|
|
642
644
|
begin
|
|
643
645
|
eval_exp = evaluate(visitor, visitor_code, feature_flag, false, false)
|
|
644
646
|
rescue Exception::FeatureEnvironmentDisabled
|
|
@@ -676,8 +678,6 @@ module Kameleoon
|
|
|
676
678
|
map_active_features = {}
|
|
677
679
|
|
|
678
680
|
@data_manager.data_file.feature_flags.each_value do |feature_flag|
|
|
679
|
-
next unless feature_flag.environment_enabled
|
|
680
|
-
|
|
681
681
|
begin
|
|
682
682
|
eval_exp = evaluate(visitor, visitor_code, feature_flag, false, false)
|
|
683
683
|
rescue Exception::FeatureEnvironmentDisabled
|
|
@@ -802,41 +802,7 @@ module Kameleoon
|
|
|
802
802
|
# @return [Kameleoon::Types::DataFile] The current DataFile instance.
|
|
803
803
|
def get_data_file
|
|
804
804
|
Logging::KameleoonLogger.info('CALL: KameleoonClient.get_data_file')
|
|
805
|
-
|
|
806
|
-
feature_flags_map = @data_manager.data_file.feature_flags.transform_values do |source_feature_flag|
|
|
807
|
-
# Collect variations
|
|
808
|
-
variations_map = source_feature_flag.variations.each_with_object({}) do |variation, variations_map|
|
|
809
|
-
variables_map = variation.variables.each_with_object({}) do |var, variables_map|
|
|
810
|
-
variables_map[var.key] = Types::Variable.new(var.key, var.type, _parse_feature_variable(var))
|
|
811
|
-
end
|
|
812
|
-
variations_map[variation.key] = Types::Variation.new(variation.key, nil, nil, variables_map, variation.name)
|
|
813
|
-
end
|
|
814
|
-
|
|
815
|
-
# Collect rules
|
|
816
|
-
rules = source_feature_flag.rules.map do |rule|
|
|
817
|
-
rule_variations = rule.experiment.variations_by_exposition.each_with_object({}) do |var_by_exp, rule_variations|
|
|
818
|
-
base_variation = variations_map[var_by_exp.variation_key]
|
|
819
|
-
next unless base_variation
|
|
820
|
-
rule_variations[base_variation.key] = Types::Variation.new(
|
|
821
|
-
base_variation.key,
|
|
822
|
-
var_by_exp.variation_id,
|
|
823
|
-
rule.experiment.id,
|
|
824
|
-
base_variation.variables,
|
|
825
|
-
base_variation.name
|
|
826
|
-
)
|
|
827
|
-
end
|
|
828
|
-
Types::Rule.new(rule_variations)
|
|
829
|
-
end
|
|
830
|
-
|
|
831
|
-
Types::FeatureFlag.new(
|
|
832
|
-
variations_map,
|
|
833
|
-
source_feature_flag.environment_enabled,
|
|
834
|
-
rules,
|
|
835
|
-
source_feature_flag.default_variation_key
|
|
836
|
-
)
|
|
837
|
-
end
|
|
838
|
-
|
|
839
|
-
data_file = Types::DataFile.new(feature_flags_map)
|
|
805
|
+
data_file = @data_manager.external_data_file
|
|
840
806
|
Logging::KameleoonLogger.info('RETURN: KameleoonClient.get_data_file -> (data_file: %s)', data_file)
|
|
841
807
|
data_file
|
|
842
808
|
end
|
|
@@ -1014,6 +980,11 @@ module Kameleoon
|
|
|
1014
980
|
)
|
|
1015
981
|
eval_exp = nil
|
|
1016
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
|
+
|
|
1017
988
|
if forced_variation
|
|
1018
989
|
eval_exp = EvaluatedExperiment.from_forced_variation(forced_variation)
|
|
1019
990
|
elsif visitor_not_in_holdout?(visitor, visitor_code, track, save, feature_flag.bucketing_custom_data_index) && \
|
|
@@ -1275,21 +1246,10 @@ module Kameleoon
|
|
|
1275
1246
|
Logging::KameleoonLogger.debug(
|
|
1276
1247
|
'CALL: KameleoonClient.create_external_variation(variation: %s, eval_exp: %s)', variation, eval_exp
|
|
1277
1248
|
)
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
ext_variables[variable.key] = Types::Variable.new(
|
|
1281
|
-
variable.key,
|
|
1282
|
-
variable.type,
|
|
1283
|
-
_parse_feature_variable(variable)
|
|
1284
|
-
)
|
|
1285
|
-
end
|
|
1286
|
-
ext_variables.freeze
|
|
1287
|
-
ext_variation = Types::Variation.new(
|
|
1288
|
-
variation&.key || '',
|
|
1249
|
+
ext_variation = Types::Variation._build_from_internal(
|
|
1250
|
+
variation,
|
|
1289
1251
|
eval_exp&.var_by_exp&.variation_id,
|
|
1290
|
-
eval_exp&.experiment&.id
|
|
1291
|
-
ext_variables,
|
|
1292
|
-
variation&.name || ''
|
|
1252
|
+
eval_exp&.experiment&.id
|
|
1293
1253
|
)
|
|
1294
1254
|
Logging::KameleoonLogger.debug(
|
|
1295
1255
|
'RETURN: KameleoonClient.create_external_variation(variation: %s, eval_exp: %s) -> (ext_variation: %s)',
|
|
@@ -1300,18 +1260,6 @@ module Kameleoon
|
|
|
1300
1260
|
|
|
1301
1261
|
##
|
|
1302
1262
|
# helper method for fetching values from a Variable
|
|
1303
|
-
def _parse_feature_variable(variable)
|
|
1304
|
-
case variable.type
|
|
1305
|
-
when 'BOOLEAN', 'STRING', 'NUMBER', 'JS', 'CSS'
|
|
1306
|
-
variable.value
|
|
1307
|
-
when 'JSON'
|
|
1308
|
-
JSON.parse(variable.value)
|
|
1309
|
-
else
|
|
1310
|
-
Logging::KameleoonLogger.error("Unknown type '#{variable.type}' for feature variable")
|
|
1311
|
-
variable.value
|
|
1312
|
-
end
|
|
1313
|
-
end
|
|
1314
|
-
|
|
1315
1263
|
def set_unique_identifier(visitor_code, is_unique_identifier)
|
|
1316
1264
|
Logging::KameleoonLogger.info(
|
|
1317
1265
|
"The 'isUniqueIdentifier' parameter is deprecated. Please, add 'UniqueIdentifier' to a visitor instead."
|
|
@@ -9,8 +9,8 @@ module Kameleoon
|
|
|
9
9
|
DEFAULT_SESSION_DURATION_MINUTES = 30
|
|
10
10
|
DEFAULT_TIMEOUT_MILLISECONDS = 10_000
|
|
11
11
|
DEFAULT_TRACKING_INTERVAL_MILLISECONDS = 1000
|
|
12
|
-
MIN_TRACKING_INTERVAL_MILLISECONDS =
|
|
13
|
-
MAX_TRACKING_INTERVAL_MILLISECONDS =
|
|
12
|
+
MIN_TRACKING_INTERVAL_MILLISECONDS = 1000
|
|
13
|
+
MAX_TRACKING_INTERVAL_MILLISECONDS = 5000
|
|
14
14
|
|
|
15
15
|
attr_reader :client_id, :client_secret, :refresh_interval_second, :session_duration_second,
|
|
16
16
|
:default_timeout_millisecond, :tracking_interval_second, :environment, :top_level_domain,
|
|
@@ -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.
|
|
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(@
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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,
|
|
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
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'kameleoon/logging/kameleoon_logger'
|
|
4
|
+
|
|
5
|
+
module Kameleoon
|
|
6
|
+
# SemVersion is a utility class for semantic version comparison
|
|
7
|
+
class SemVersion
|
|
8
|
+
include Comparable
|
|
9
|
+
|
|
10
|
+
attr_reader :major, :minor, :patch
|
|
11
|
+
|
|
12
|
+
def initialize(major = 0, minor = 0, patch = 0)
|
|
13
|
+
@major = major
|
|
14
|
+
@minor = minor
|
|
15
|
+
@patch = patch
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def <=>(other)
|
|
19
|
+
return nil unless other.is_a?(SemVersion)
|
|
20
|
+
|
|
21
|
+
c = @major <=> other.major
|
|
22
|
+
return c unless c.zero?
|
|
23
|
+
|
|
24
|
+
c = @minor <=> other.minor
|
|
25
|
+
return c unless c.zero?
|
|
26
|
+
|
|
27
|
+
@patch <=> other.patch
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def to_f
|
|
31
|
+
"#{@major}.#{@minor}".to_f
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.from_string(version_string)
|
|
35
|
+
return nil unless version_string.is_a?(String)
|
|
36
|
+
|
|
37
|
+
versions = [0, 0, 0]
|
|
38
|
+
version_parts = version_string.split('.')
|
|
39
|
+
return nil unless version_parts.length >= 1
|
|
40
|
+
|
|
41
|
+
version_parts.each_with_index do |part, i|
|
|
42
|
+
break if i >= 3
|
|
43
|
+
|
|
44
|
+
begin
|
|
45
|
+
versions[i] = Integer(part)
|
|
46
|
+
rescue ArgumentError
|
|
47
|
+
Logging::KameleoonLogger.error("Invalid version component, index: %s, value: '%s'", i, part)
|
|
48
|
+
return nil
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
SemVersion.new(versions[0], versions[1], versions[2])
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require_relative 'conditions/custom_datum'
|
|
2
|
+
require_relative 'conditions/version_condition'
|
|
2
3
|
require_relative 'conditions/target_experiment_condition'
|
|
3
4
|
require_relative 'conditions/target_feature_flag_condition'
|
|
4
5
|
require_relative 'conditions/target_personalization_condition'
|
|
@@ -77,6 +78,8 @@ module Kameleoon
|
|
|
77
78
|
TimeElapsedSinceVisitCondition.new(condition_json)
|
|
78
79
|
when ConditionType::HEAT_SLICE
|
|
79
80
|
KcsHeatRangeCondition.new(condition_json)
|
|
81
|
+
when ConditionType::APPLICATION_VERSION
|
|
82
|
+
VersionCondition.new(condition_json)
|
|
80
83
|
else
|
|
81
84
|
Logging::KameleoonLogger.info("Unexpected TargetingConditionType: '%s'", cond_type)
|
|
82
85
|
UnknownCondition.new(condition_json)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'kameleoon/data/browser'
|
|
4
4
|
require 'kameleoon/logging/kameleoon_logger'
|
|
5
|
-
require 'kameleoon/
|
|
5
|
+
require 'kameleoon/sem_version'
|
|
6
6
|
|
|
7
7
|
module Kameleoon
|
|
8
8
|
# @api private
|
|
@@ -33,7 +33,7 @@ module Kameleoon
|
|
|
33
33
|
|
|
34
34
|
return true if @version.nil?
|
|
35
35
|
|
|
36
|
-
version_number =
|
|
36
|
+
version_number = SemVersion.from_string(@version)&.to_f || Float::NAN
|
|
37
37
|
return false if version_number.nan?
|
|
38
38
|
|
|
39
39
|
case @version_match_type
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'kameleoon/data/data'
|
|
4
|
-
require 'kameleoon/logging/kameleoon_logger'
|
|
5
3
|
require 'kameleoon/version'
|
|
4
|
+
require_relative 'version_condition'
|
|
6
5
|
|
|
7
6
|
module Kameleoon
|
|
8
7
|
# @api private
|
|
@@ -18,12 +17,10 @@ module Kameleoon
|
|
|
18
17
|
end
|
|
19
18
|
|
|
20
19
|
# SdkLanguageCondition is a condition for checking targeting sdk type and version
|
|
21
|
-
class SdkLanguageCondition <
|
|
20
|
+
class SdkLanguageCondition < VersionCondition
|
|
22
21
|
def initialize(json_condition)
|
|
23
22
|
super(json_condition)
|
|
24
23
|
@sdk_language = json_condition['sdkLanguage']
|
|
25
|
-
@version = json_condition['version']
|
|
26
|
-
@operator = json_condition['versionMatchType']
|
|
27
24
|
end
|
|
28
25
|
|
|
29
26
|
def check(data)
|
|
@@ -33,34 +30,9 @@ module Kameleoon
|
|
|
33
30
|
private
|
|
34
31
|
|
|
35
32
|
def check_targeting(sdk_info)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return true if @version.nil?
|
|
39
|
-
|
|
40
|
-
version_components_condition = SdkVersion.get_version_components(@version)
|
|
41
|
-
version_components_sdk_info = SdkVersion.get_version_components(sdk_info.version)
|
|
42
|
-
|
|
43
|
-
return false if version_components_condition.nil? || version_components_sdk_info.nil?
|
|
44
|
-
|
|
45
|
-
major_condition, minor_condition, patch_condition = version_components_condition
|
|
46
|
-
major_sdk, minor_sdk, patch_sdk = version_components_sdk_info
|
|
47
|
-
|
|
48
|
-
case @operator
|
|
49
|
-
when Operator::EQUAL
|
|
50
|
-
major_sdk == major_condition && minor_sdk == minor_condition && patch_sdk == patch_condition
|
|
51
|
-
when Operator::GREATER
|
|
52
|
-
major_sdk > major_condition ||
|
|
53
|
-
(major_sdk == major_condition && minor_sdk > minor_condition) ||
|
|
54
|
-
(major_sdk == major_condition && minor_sdk == minor_condition && patch_sdk > patch_condition)
|
|
55
|
-
when Operator::LOWER
|
|
56
|
-
major_sdk < major_condition ||
|
|
57
|
-
(major_sdk == major_condition && minor_sdk < minor_condition) ||
|
|
58
|
-
(major_sdk == major_condition && minor_sdk == minor_condition && patch_sdk < patch_condition)
|
|
59
|
-
else
|
|
60
|
-
Logging::KameleoonLogger.error("Unexpected comparing operation for 'SdkLanguage' condition: '#{@operator}'")
|
|
61
|
-
false
|
|
62
|
-
end
|
|
33
|
+
@sdk_language == sdk_info.language &&
|
|
34
|
+
(@version.nil? || compare_with_version(sdk_info.version))
|
|
63
35
|
end
|
|
64
36
|
end
|
|
65
37
|
end
|
|
66
|
-
end
|
|
38
|
+
end
|
|
@@ -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
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'kameleoon/logging/kameleoon_logger'
|
|
4
|
+
require 'kameleoon/sem_version'
|
|
5
|
+
require 'kameleoon/targeting/condition'
|
|
6
|
+
|
|
7
|
+
module Kameleoon
|
|
8
|
+
# @api private
|
|
9
|
+
module Targeting
|
|
10
|
+
# VersionCondition is a base class for conditions that compare versions.
|
|
11
|
+
# Subclasses should implement `check` method and call `compare_with_version` for version comparison.
|
|
12
|
+
class VersionCondition < Condition
|
|
13
|
+
def initialize(json_condition)
|
|
14
|
+
super(json_condition)
|
|
15
|
+
@version = json_condition['version']
|
|
16
|
+
@version_match_type = json_condition['versionMatchType']
|
|
17
|
+
@cached_version_condition = nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def check(data)
|
|
21
|
+
data.is_a?(String) && compare_with_version(data)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
protected
|
|
25
|
+
|
|
26
|
+
def compare_with_version(version)
|
|
27
|
+
@cached_version_condition ||= SemVersion.from_string(@version)
|
|
28
|
+
version_compare = SemVersion.from_string(version)
|
|
29
|
+
|
|
30
|
+
if @cached_version_condition.nil? || version_compare.nil?
|
|
31
|
+
Logging::KameleoonLogger.error(
|
|
32
|
+
"VersionCondition has unexpected values to compare, condition version: '%s', provided version: '%s'",
|
|
33
|
+
@version, version
|
|
34
|
+
)
|
|
35
|
+
return false
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
case @version_match_type
|
|
39
|
+
when Operator::EQUAL
|
|
40
|
+
version_compare == @cached_version_condition
|
|
41
|
+
when Operator::GREATER
|
|
42
|
+
version_compare > @cached_version_condition
|
|
43
|
+
when Operator::LOWER
|
|
44
|
+
version_compare < @cached_version_condition
|
|
45
|
+
else
|
|
46
|
+
Logging::KameleoonLogger.error(
|
|
47
|
+
"Unexpected comparing operation for '%s' condition: '%s'",
|
|
48
|
+
type, @version_match_type
|
|
49
|
+
)
|
|
50
|
+
false
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -92,6 +92,8 @@ module Kameleoon
|
|
|
92
92
|
condition_data = VisitNumberTodayCondition::TargetingData.new(visitor&.time_started, visitor&.visitor_visits)
|
|
93
93
|
when ConditionType::HEAT_SLICE
|
|
94
94
|
condition_data = visitor&.kcs_heat
|
|
95
|
+
when ConditionType::APPLICATION_VERSION
|
|
96
|
+
condition_data = visitor&.application_version&.version
|
|
95
97
|
end
|
|
96
98
|
Logging::KameleoonLogger.debug(
|
|
97
99
|
"RETURN: TargetingManager.get_condition_data(type: %s, visitor, visitor_code: '%s', campaign_id: %s) -> " \
|
|
@@ -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
|
data/lib/kameleoon/types/rule.rb
CHANGED
|
@@ -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
|
data/lib/kameleoon/version.rb
CHANGED
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
|
+
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-
|
|
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
|
|
@@ -86,6 +86,7 @@ files:
|
|
|
86
86
|
- lib/kameleoon/configuration/variable.rb
|
|
87
87
|
- lib/kameleoon/configuration/variation.rb
|
|
88
88
|
- lib/kameleoon/configuration/variation_exposition.rb
|
|
89
|
+
- lib/kameleoon/data/application_version.rb
|
|
89
90
|
- lib/kameleoon/data/browser.rb
|
|
90
91
|
- lib/kameleoon/data/cbscores.rb
|
|
91
92
|
- lib/kameleoon/data/conversion.rb
|
|
@@ -145,7 +146,7 @@ files:
|
|
|
145
146
|
- lib/kameleoon/real_time/sse_client.rb
|
|
146
147
|
- lib/kameleoon/real_time/sse_message.rb
|
|
147
148
|
- lib/kameleoon/real_time/sse_request.rb
|
|
148
|
-
- lib/kameleoon/
|
|
149
|
+
- lib/kameleoon/sem_version.rb
|
|
149
150
|
- lib/kameleoon/storage/cache.rb
|
|
150
151
|
- lib/kameleoon/storage/cache_factory.rb
|
|
151
152
|
- lib/kameleoon/targeting/condition.rb
|
|
@@ -172,6 +173,7 @@ files:
|
|
|
172
173
|
- lib/kameleoon/targeting/conditions/target_personalization_condition.rb
|
|
173
174
|
- lib/kameleoon/targeting/conditions/time_elapsed_since_visit_condition.rb
|
|
174
175
|
- lib/kameleoon/targeting/conditions/unknown_condition.rb
|
|
176
|
+
- lib/kameleoon/targeting/conditions/version_condition.rb
|
|
175
177
|
- lib/kameleoon/targeting/conditions/visit_number_today_condition.rb
|
|
176
178
|
- lib/kameleoon/targeting/conditions/visit_number_total_condition.rb
|
|
177
179
|
- lib/kameleoon/targeting/conditions/visitor_code_condition.rb
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'kameleoon/logging/kameleoon_logger'
|
|
4
|
-
|
|
5
|
-
module Kameleoon
|
|
6
|
-
# SdkManager is a helper method for fetching / obtaining version of SDK from string
|
|
7
|
-
class SdkVersion
|
|
8
|
-
def self.get_version_components(version_string)
|
|
9
|
-
versions = [0, 0, 0]
|
|
10
|
-
|
|
11
|
-
version_parts = version_string.split('.')
|
|
12
|
-
version_parts.each_with_index do |part, i|
|
|
13
|
-
versions[i] = Integer(part)
|
|
14
|
-
rescue ArgumentError
|
|
15
|
-
Logging::KameleoonLogger.error('Invalid version component, index: %s, value: %s', i, part)
|
|
16
|
-
return nil
|
|
17
|
-
end
|
|
18
|
-
versions
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def self.get_float_version(version_string)
|
|
22
|
-
version_components = get_version_components(version_string)
|
|
23
|
-
|
|
24
|
-
return Float::NAN if version_components.nil?
|
|
25
|
-
|
|
26
|
-
major = version_components[0]
|
|
27
|
-
minor = version_components[1]
|
|
28
|
-
"#{major}.#{minor}".to_f
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|