kameleoon-client-ruby 2.3.0 → 3.1.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/client_readiness.rb +40 -0
- data/lib/kameleoon/configuration/data_file.rb +41 -0
- data/lib/kameleoon/configuration/feature_flag.rb +3 -2
- data/lib/kameleoon/configuration/rule.rb +18 -3
- data/lib/kameleoon/configuration/settings.rb +5 -0
- data/lib/kameleoon/configuration/variable.rb +0 -2
- data/lib/kameleoon/configuration/variation_exposition.rb +0 -2
- data/lib/kameleoon/data/browser.rb +1 -1
- data/lib/kameleoon/data/conversion.rb +1 -1
- data/lib/kameleoon/data/custom_data.rb +10 -14
- data/lib/kameleoon/data/data.rb +22 -3
- data/lib/kameleoon/data/device.rb +2 -1
- data/lib/kameleoon/data/manager/assigned_variation.rb +38 -0
- data/lib/kameleoon/data/manager/data_array_storage.rb +43 -0
- data/lib/kameleoon/data/manager/data_map_storage.rb +43 -0
- data/lib/kameleoon/data/manager/page_view_visit.rb +19 -0
- data/lib/kameleoon/data/manager/visitor.rb +142 -0
- data/lib/kameleoon/data/manager/visitor_manager.rb +71 -0
- data/lib/kameleoon/data/page_view.rb +2 -1
- data/lib/kameleoon/exceptions.rb +30 -35
- data/lib/kameleoon/hybrid/manager.rb +13 -31
- data/lib/kameleoon/{client.rb → kameleoon_client.rb} +194 -334
- data/lib/kameleoon/kameleoon_client_config.rb +91 -0
- data/lib/kameleoon/kameleoon_client_factory.rb +42 -0
- data/lib/kameleoon/managers/warehouse/warehouse_manager.rb +33 -0
- data/lib/kameleoon/network/access_token_source.rb +109 -0
- data/lib/kameleoon/network/activity_event.rb +6 -3
- data/lib/kameleoon/network/cookie/cookie_manager.rb +84 -0
- data/lib/kameleoon/network/net_provider.rb +25 -58
- data/lib/kameleoon/network/network_manager.rb +65 -43
- data/lib/kameleoon/network/request.rb +7 -2
- data/lib/kameleoon/network/response.rb +0 -8
- data/lib/kameleoon/network/url_provider.rb +30 -12
- data/lib/kameleoon/real_time/sse_client.rb +2 -0
- data/lib/kameleoon/targeting/conditions/browser_condition.rb +2 -3
- data/lib/kameleoon/targeting/conditions/conversion_condition.rb +12 -4
- data/lib/kameleoon/targeting/conditions/custom_datum.rb +19 -13
- data/lib/kameleoon/targeting/conditions/device_condition.rb +3 -4
- data/lib/kameleoon/targeting/conditions/exclusive_experiment.rb +2 -1
- data/lib/kameleoon/targeting/conditions/page_title_condition.rb +11 -4
- data/lib/kameleoon/targeting/conditions/page_url_condition.rb +18 -4
- data/lib/kameleoon/targeting/conditions/string_value_condition.rb +2 -0
- data/lib/kameleoon/targeting/conditions/target_experiment.rb +11 -6
- data/lib/kameleoon/utils.rb +41 -4
- data/lib/kameleoon/version.rb +1 -1
- data/lib/kameleoon.rb +4 -2
- metadata +16 -10
- data/lib/kameleoon/client_config.rb +0 -44
- data/lib/kameleoon/configuration/experiment.rb +0 -42
- data/lib/kameleoon/cookie.rb +0 -88
- data/lib/kameleoon/factory.rb +0 -43
- data/lib/kameleoon/network/experiment_event.rb +0 -35
- data/lib/kameleoon/storage/variation_storage.rb +0 -42
- data/lib/kameleoon/storage/visitor_variation.rb +0 -20
@@ -1,27 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'kameleoon/client_readiness'
|
3
4
|
require 'kameleoon/targeting/models'
|
4
5
|
require 'kameleoon/exceptions'
|
5
|
-
require 'kameleoon/cookie'
|
6
6
|
require 'kameleoon/data/custom_data'
|
7
7
|
require 'kameleoon/data/user_agent'
|
8
|
+
require 'kameleoon/data/manager/assigned_variation'
|
9
|
+
require 'kameleoon/data/manager/visitor_manager'
|
8
10
|
require 'kameleoon/configuration/feature_flag'
|
9
11
|
require 'kameleoon/configuration/variation'
|
10
|
-
require 'kameleoon/configuration/
|
12
|
+
require 'kameleoon/configuration/data_file'
|
13
|
+
require 'kameleoon/network/access_token_source'
|
11
14
|
require 'kameleoon/network/activity_event'
|
12
|
-
require 'kameleoon/network/experiment_event'
|
13
|
-
require 'kameleoon/network/url_provider'
|
14
15
|
require 'kameleoon/network/network_manager'
|
16
|
+
require 'kameleoon/network/url_provider'
|
17
|
+
require 'kameleoon/network/cookie/cookie_manager'
|
18
|
+
require 'kameleoon/managers/warehouse/warehouse_manager'
|
15
19
|
require 'kameleoon/real_time/real_time_configuration_service'
|
16
|
-
require 'kameleoon/storage/variation_storage'
|
17
20
|
require 'kameleoon/hybrid/manager'
|
18
21
|
require 'kameleoon/storage/cache_factory'
|
19
22
|
require 'rufus/scheduler'
|
20
23
|
require 'yaml'
|
21
24
|
require 'json'
|
22
|
-
require 'em-synchrony'
|
23
|
-
require 'em-synchrony/em-http'
|
24
|
-
require 'em-synchrony/fiber_iterator'
|
25
25
|
require 'objspace'
|
26
26
|
require 'time'
|
27
27
|
require 'ostruct'
|
@@ -30,37 +30,39 @@ module Kameleoon
|
|
30
30
|
##
|
31
31
|
# Client for Kameleoon
|
32
32
|
#
|
33
|
-
class
|
34
|
-
include Cookie
|
33
|
+
class KameleoonClient
|
35
34
|
include Exception
|
36
35
|
|
36
|
+
attr_reader :site_code
|
37
|
+
|
37
38
|
##
|
38
|
-
# You should create
|
39
|
+
# You should create KameleoonClient with the Client Factory only.
|
39
40
|
#
|
40
41
|
def initialize(site_code, config)
|
42
|
+
raise Exception::SiteCodeIsEmpty, 'Provided site_sode is empty' if site_code&.empty? != false
|
43
|
+
|
41
44
|
@site_code = site_code
|
42
|
-
|
45
|
+
@config = config
|
43
46
|
@real_time_configuration_service = nil
|
44
47
|
@update_configuration_handler = nil
|
45
48
|
@fetch_configuration_update_job = nil
|
46
|
-
@
|
47
|
-
@
|
48
|
-
@
|
49
|
-
@data = {}
|
50
|
-
@user_agents = {}
|
51
|
-
@variation_storage = Kameleoon::Storage::VariationStorage.new
|
52
|
-
@hybrid_manager = Kameleoon::Hybrid::ManagerImpl.new(
|
53
|
-
CACHE_EXPIRATION_TIMEOUT,
|
54
|
-
CACHE_EXPIRATION_TIMEOUT * 3,
|
55
|
-
Kameleoon::Storage::CacheFactoryImpl.new,
|
56
|
-
method(:log)
|
57
|
-
)
|
49
|
+
@data_file = Configuration::DataFile.new(config.environment)
|
50
|
+
@visitor_manager = Kameleoon::DataManager::VisitorManager.new(config.session_duration_second)
|
51
|
+
@hybrid_manager = Kameleoon::Hybrid::ManagerImpl.new(HYBRID_EXPIRATION_TIME, method(:log))
|
58
52
|
@network_manager = Network::NetworkManager.new(
|
59
|
-
|
60
|
-
|
61
|
-
Network::
|
53
|
+
config.environment,
|
54
|
+
config.default_timeout_millisecond,
|
55
|
+
Network::AccessTokenSourceFactory.new(config.client_id, config.client_secret, method(:log)),
|
56
|
+
Network::UrlProvider.new(site_code),
|
62
57
|
method(:log)
|
63
58
|
)
|
59
|
+
@warehouse_manager = Managers::Warehouse::WarehouseManager.new(@network_manager, @visitor_manager, method(:log))
|
60
|
+
@cookie_manager = Network::Cookie::CookieManager.new(config.top_level_domain)
|
61
|
+
@readiness = ClientReadiness.new
|
62
|
+
end
|
63
|
+
|
64
|
+
def wait_init
|
65
|
+
@readiness.wait
|
64
66
|
end
|
65
67
|
|
66
68
|
##
|
@@ -72,8 +74,8 @@ module Kameleoon
|
|
72
74
|
# @note The implementation logic is described here:
|
73
75
|
# First we check if a kameleoonVisitorCode cookie or query parameter associated with the current HTTP request can be
|
74
76
|
# found. If so, we will use this as the visitor identifier. If no cookie / parameter is found in the current
|
75
|
-
# request, we either randomly generate a new identifier, or use the
|
76
|
-
# is passed. This allows our customers to use their own identifiers as visitor codes, should they wish to.
|
77
|
+
# request, we either randomly generate a new identifier, or use the default_visitor_code argument as identifier if
|
78
|
+
# it is passed. This allows our customers to use their own identifiers as visitor codes, should they wish to.
|
77
79
|
# This can have the added benefit of matching Kameleoon visitors with their own users without any additional
|
78
80
|
# look-ups in a matching table.
|
79
81
|
# In any case, the server-side (via HTTP header) kameleoonVisitorCode cookie is set with the value. Then this
|
@@ -89,61 +91,15 @@ module Kameleoon
|
|
89
91
|
# cookies = {'kameleoonVisitorCode' => '1234asdf4321fdsa'}
|
90
92
|
# visitor_code = get_visitor_code(cookies, 'my-domaine.com')
|
91
93
|
#
|
92
|
-
def get_visitor_code(cookies,
|
93
|
-
|
94
|
-
end
|
95
|
-
|
96
|
-
# DEPRECATED. Please use `get_visitor_code` instead.
|
97
|
-
def obtain_visitor_code(cookies, top_level_domain, default_visitor_code = nil)
|
98
|
-
warn '[DEPRECATION] `obtain_visitor_code` is deprecated. Please use `get_visitor_code` instead.'
|
99
|
-
get_visitor_code(cookies, top_level_domain, default_visitor_code)
|
94
|
+
def get_visitor_code(cookies, default_visitor_code = nil)
|
95
|
+
@cookie_manager.get_or_add(cookies, default_visitor_code)
|
100
96
|
end
|
101
97
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
# registered variation and return the variation_id.
|
108
|
-
# You have to make sure that proper error handling is set up in your code as shown in the example to the right to
|
109
|
-
# catch potential exceptions.
|
110
|
-
#
|
111
|
-
# @param [String] visitor_code Visitor code
|
112
|
-
# @param [Integer] experiment_id Id of the experiment you want to trigger.
|
113
|
-
#
|
114
|
-
# @return [Integer] Id of the variation
|
115
|
-
#
|
116
|
-
# @raise [Kameleoon::Exception::ExperimentConfigurationNotFound] Raise when experiment configuration is not found
|
117
|
-
# @raise [Kameleoon::Exception::NotAllocated] The visitor triggered the experiment, but did not activate it.
|
118
|
-
# Usually, this happens because the user has been associated with excluded traffic
|
119
|
-
# @raise [Kameleoon::Exception::NotTargeted] The visitor is not targeted by the experiment, as the
|
120
|
-
# associated targeting segment conditions were not fulfilled. He should see the reference variation
|
121
|
-
# @raise [Kameleoon::Exception::VisitorCodeNotValid] If the visitor code is empty or longer than 255 chars
|
122
|
-
#
|
123
|
-
def trigger_experiment(visitor_code, experiment_id)
|
124
|
-
check_visitor_code(visitor_code)
|
125
|
-
experiment = @experiments.find { |exp| exp.id.to_s == experiment_id.to_s }
|
126
|
-
if experiment.nil?
|
127
|
-
raise Exception::ExperimentConfigurationNotFound.new(experiment_id),
|
128
|
-
"Experiment #{experiment_id} is not found"
|
129
|
-
end
|
130
|
-
check_site_code_enable(experiment)
|
131
|
-
targeted = check_targeting(visitor_code, experiment_id, experiment)
|
132
|
-
if targeted
|
133
|
-
# saved_variation = get_valid_saved_variation(visitor_code, experiment)
|
134
|
-
variation_id = calculate_variation_for_experiment(visitor_code, experiment)
|
135
|
-
save_variation(visitor_code, experiment_id, variation_id)
|
136
|
-
end
|
137
|
-
_send_tracking_request(visitor_code, experiment_id, variation_id)
|
138
|
-
unless targeted
|
139
|
-
raise Exception::NotTargeted.new(visitor_code),
|
140
|
-
"Experiment #{experiment_id} is not targeted for visitor #{visitor_code}"
|
141
|
-
end
|
142
|
-
if variation_id.nil?
|
143
|
-
raise Exception::NotAllocated.new(visitor_code),
|
144
|
-
"Experiment #{experiment_id} is not active for visitor #{visitor_code}"
|
145
|
-
end
|
146
|
-
variation_id
|
98
|
+
def set_legal_consent(visitor_code, consent, cookies = nil)
|
99
|
+
Utils::VisitorCode.validate(visitor_code)
|
100
|
+
visitor = @visitor_manager.get_or_create_visitor(visitor_code)
|
101
|
+
visitor.legal_consent = consent
|
102
|
+
@cookie_manager.update(visitor_code, consent, cookies)
|
147
103
|
end
|
148
104
|
|
149
105
|
##
|
@@ -157,19 +113,12 @@ module Kameleoon
|
|
157
113
|
# @param [String] visitor_code Visitor code
|
158
114
|
# @param [...Data] data Data to associate with the visitor code
|
159
115
|
#
|
160
|
-
# @raise [Kameleoon::Exception::
|
116
|
+
# @raise [Kameleoon::Exception::VisitorCodeInvalid] If the visitor code is empty or longer than 255 chars
|
161
117
|
#
|
162
118
|
def add_data(visitor_code, *args)
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
if data_element.is_a?(UserAgent)
|
167
|
-
add_user_agent_data(visitor_code, data_element)
|
168
|
-
next
|
169
|
-
end
|
170
|
-
@data[visitor_code] = [] unless @data.key?(visitor_code)
|
171
|
-
@data[visitor_code].push(data_element)
|
172
|
-
end
|
119
|
+
Utils::VisitorCode.validate(visitor_code)
|
120
|
+
visitor = @visitor_manager.get_or_create_visitor(visitor_code)
|
121
|
+
visitor.add_data(method(:log), *args)
|
173
122
|
end
|
174
123
|
|
175
124
|
##
|
@@ -185,10 +134,10 @@ module Kameleoon
|
|
185
134
|
# @param [Integer] goal_id Id of the goal
|
186
135
|
# @param [Float] revenue Optional - Revenue of the conversion.
|
187
136
|
#
|
188
|
-
# @raise [Kameleoon::Exception::
|
137
|
+
# @raise [Kameleoon::Exception::VisitorCodeInvalid] If the visitor code is empty or longer than 255 chars
|
189
138
|
#
|
190
139
|
def track_conversion(visitor_code, goal_id, revenue = 0.0)
|
191
|
-
|
140
|
+
Utils::VisitorCode.validate(visitor_code)
|
192
141
|
add_data(visitor_code, Conversion.new(goal_id, revenue))
|
193
142
|
flush(visitor_code)
|
194
143
|
end
|
@@ -202,74 +151,17 @@ module Kameleoon
|
|
202
151
|
#
|
203
152
|
# @param [String] visitor_code Optional field - Visitor code, without visitor code it flush all of the data
|
204
153
|
#
|
205
|
-
# @raise [Kameleoon::Exception::
|
154
|
+
# @raise [Kameleoon::Exception::VisitorCodeInvalid] If the visitor code is not nil and is empty or longer than 255 chars
|
206
155
|
#
|
207
156
|
def flush(visitor_code = nil)
|
208
|
-
|
209
|
-
if
|
210
|
-
_send_tracking_request(visitor_code)
|
157
|
+
Utils::VisitorCode.validate(visitor_code) unless visitor_code.nil?
|
158
|
+
if visitor_code.nil?
|
159
|
+
@visitor_manager.enumerate { |visitor_code, visitor| _send_tracking_request(visitor_code, visitor, false) }
|
211
160
|
else
|
212
|
-
|
161
|
+
_send_tracking_request(visitor_code, nil, true)
|
213
162
|
end
|
214
163
|
end
|
215
164
|
|
216
|
-
##
|
217
|
-
# Obtain variation associated data.
|
218
|
-
#
|
219
|
-
# To retrieve JSON data associated with a variation, call the get_variation_associated_data method of our SDK.
|
220
|
-
# The JSON data usually represents some metadata of the variation, and can be configured on our web application
|
221
|
-
# interface or via our Automation API.
|
222
|
-
# This method takes the variationID as a parameter and will return the data as a json string.
|
223
|
-
# It will throw an exception () if the variation ID is wrong or corresponds to an experiment that is not yet online.
|
224
|
-
#
|
225
|
-
# @param [Integer] variation_id
|
226
|
-
#
|
227
|
-
# @return [Hash] Hash object of the json object.
|
228
|
-
#
|
229
|
-
# @raise [Kameleoon::Exception::VariationNotFound] Raise exception if the variation is not found.
|
230
|
-
#
|
231
|
-
def get_variation_associated_data(variation_id)
|
232
|
-
variation = @experiments.map(&:variations).flatten.select { |var| var['id'].to_i == variation_id.to_i }.first
|
233
|
-
if variation.nil?
|
234
|
-
raise Exception::VariationConfigurationNotFound.new(variation_id),
|
235
|
-
"Variation key #{variation_id} not found"
|
236
|
-
else
|
237
|
-
JSON.parse(variation['customJson'])
|
238
|
-
end
|
239
|
-
end
|
240
|
-
|
241
|
-
# DEPRECATED. Please use `get_variation_associated_data` instead.
|
242
|
-
def obtain_variation_associated_data(variation_id)
|
243
|
-
warn '[DEPRECATION] `obtain_variation_associated_data` is deprecated.
|
244
|
-
Please use `get_variation_associated_data` instead.'
|
245
|
-
get_variation_associated_data(variation_id)
|
246
|
-
end
|
247
|
-
|
248
|
-
# #
|
249
|
-
# Activate a feature toggle.
|
250
|
-
|
251
|
-
# This method takes a visitor_code and feature_key (or feature_id) as mandatory arguments to check if the specified
|
252
|
-
# feature will be active for a given user.
|
253
|
-
# If such a user has never been associated with this feature flag, the SDK returns a boolean value randomly
|
254
|
-
# (true if the user should have this feature or false if not). If a user with a given visitorCode is already
|
255
|
-
# registered with this feature flag, it will detect the previous featureFlag value.
|
256
|
-
# You have to make sure that proper error handling is set up in your code as shown in the example
|
257
|
-
# to the right to catch potential exceptions.
|
258
|
-
|
259
|
-
# @param [String] visitor_code
|
260
|
-
# @param [String | Integer] feature_key
|
261
|
-
|
262
|
-
# @raise [Kameleoon::Exception::FeatureConfigurationNotFound] Feature Flag isn't found in this configuration
|
263
|
-
# @raise [Kameleoon::Exception::NotTargeted] The visitor is not targeted by the experiment, as the
|
264
|
-
# associated targeting segment conditions were not fulfilled. He should see the reference variation
|
265
|
-
# @raise [Kameleoon::Exception::VisitorCodeNotValid] If the visitor code is empty or longer than 255 chars
|
266
|
-
#
|
267
|
-
# DEPRECATED. Please use `feature_active?` instead.
|
268
|
-
def activate_feature(visitor_code, feature_key)
|
269
|
-
warn '[DEPRECATION] `activate_feature` is deprecated. Please use `feature_active?` instead.'
|
270
|
-
feature_active?(visitor_code, feature_key)
|
271
|
-
end
|
272
|
-
|
273
165
|
##
|
274
166
|
# Check if feature is active for a given visitor code
|
275
167
|
#
|
@@ -284,12 +176,14 @@ module Kameleoon
|
|
284
176
|
# @param [String] visitor_code Unique identifier of the user. This field is mandatory.
|
285
177
|
# @param [String] feature_key Key of the feature flag you want to expose to a user. This field is mandatory.
|
286
178
|
#
|
287
|
-
# @raise [Kameleoon::Exception::
|
288
|
-
# @raise [Kameleoon::Exception::
|
179
|
+
# @raise [Kameleoon::Exception::FeatureNotFound] Feature Flag isn't found in this configuration
|
180
|
+
# @raise [Kameleoon::Exception::VisitorCodeInvalid] If the visitor code is empty or longer than 255 chars
|
289
181
|
#
|
290
182
|
def feature_active?(visitor_code, feature_key)
|
291
183
|
_, variation_key = _get_feature_variation_key(visitor_code, feature_key)
|
292
184
|
variation_key != Kameleoon::Configuration::VariationType::VARIATION_OFF
|
185
|
+
rescue Exception::FeatureEnvironmentDisabled
|
186
|
+
false
|
293
187
|
end
|
294
188
|
|
295
189
|
#
|
@@ -304,8 +198,10 @@ module Kameleoon
|
|
304
198
|
# @param [String] visitor_code
|
305
199
|
# @param [String] feature_key
|
306
200
|
#
|
307
|
-
# @raise [Kameleoon::Exception::
|
308
|
-
# @raise [Kameleoon::Exception::
|
201
|
+
# @raise [Kameleoon::Exception::FeatureNotFound] Feature Flag isn't found in this configuration
|
202
|
+
# @raise [Kameleoon::Exception::VisitorCodeInvalid] If the visitor code is empty or longer than 255 chars
|
203
|
+
# @raise [Kameleoon::Exception::FeatureEnvironmentDisabled] If the requested feature flag is disabled for
|
204
|
+
# the current environment
|
309
205
|
#
|
310
206
|
def get_feature_variation_key(visitor_code, feature_key)
|
311
207
|
_, variation_key = _get_feature_variation_key(visitor_code, feature_key)
|
@@ -321,9 +217,11 @@ module Kameleoon
|
|
321
217
|
# @param [String] feature_key
|
322
218
|
# @param [String] variable_name
|
323
219
|
#
|
324
|
-
# @raise [Kameleoon::Exception::
|
220
|
+
# @raise [Kameleoon::Exception::FeatureNotFound] Feature Flag isn't found in this configuration
|
325
221
|
# @raise [Kameleoon::Exception::FeatureVariableNotFound]
|
326
|
-
# @raise [Kameleoon::Exception::
|
222
|
+
# @raise [Kameleoon::Exception::VisitorCodeInvalid] If the visitor code is empty or longer than 255 chars
|
223
|
+
# @raise [Kameleoon::Exception::FeatureEnvironmentDisabled] If the requested feature flag is disabled for
|
224
|
+
# the current environment
|
327
225
|
#
|
328
226
|
def get_feature_variable(visitor_code, feature_key, variable_name)
|
329
227
|
feature_flag, variation_key = _get_feature_variation_key(visitor_code, feature_key)
|
@@ -346,14 +244,15 @@ module Kameleoon
|
|
346
244
|
# @param [String] feature_key
|
347
245
|
# @param [String] variation_key
|
348
246
|
#
|
349
|
-
# @raise [Kameleoon::Exception::
|
350
|
-
# @raise [Kameleoon::Exception::
|
247
|
+
# @raise [Kameleoon::Exception::FeatureNotFound] Feature Flag isn't found in this configuration
|
248
|
+
# @raise [Kameleoon::Exception::FeatureVariationNotFound]
|
249
|
+
# @raise [Kameleoon::Exception::FeatureEnvironmentDisabled]
|
351
250
|
#
|
352
|
-
def
|
353
|
-
feature_flag =
|
251
|
+
def get_feature_variation_variables(feature_key, variation_key)
|
252
|
+
feature_flag = @data_file.get_feature_flag(feature_key)
|
354
253
|
variation = feature_flag.get_variation_key(variation_key)
|
355
254
|
if variation.nil?
|
356
|
-
raise Exception::
|
255
|
+
raise Exception::FeatureVariationNotFound.new(variation_key),
|
357
256
|
"Variation key #{variation_key} not found"
|
358
257
|
end
|
359
258
|
variables = {}
|
@@ -361,27 +260,6 @@ module Kameleoon
|
|
361
260
|
variables
|
362
261
|
end
|
363
262
|
|
364
|
-
##
|
365
|
-
# Retrieve a feature variable.
|
366
|
-
#
|
367
|
-
# A feature variable can be changed easily via our web application.
|
368
|
-
#
|
369
|
-
# @param [String | Integer] feature_key
|
370
|
-
# @param [String] variable_key
|
371
|
-
#
|
372
|
-
# @raise [Kameleoon::Exception::FeatureConfigurationNotFound] Feature Flag isn't found in this configuration
|
373
|
-
# @raise [Kameleoon::Exception::FeatureVariableNotFound]
|
374
|
-
#
|
375
|
-
# DEPRECATED. Please use `get_feature_variable` instead.
|
376
|
-
def obtain_feature_variable(feature_key, variable_key)
|
377
|
-
warn '[DEPRECATION] `obtain_feature_variable` is deprecated. Please use `get_feature_variable` instead.'
|
378
|
-
all_variables = get_feature_all_variables(
|
379
|
-
feature_key,
|
380
|
-
Configuration::VariationType::VARIATION_OFF
|
381
|
-
)
|
382
|
-
all_variables[variable_key]
|
383
|
-
end
|
384
|
-
|
385
263
|
##
|
386
264
|
# The get_remote_data method allows you to retrieve data (according to a key passed as argument)
|
387
265
|
# stored on a remote Kameleoon server. Usually data will be stored on our remote
|
@@ -390,7 +268,7 @@ module Kameleoon
|
|
390
268
|
# way to quickly store massive amounts of data that can be later retrieved for each of your visitors / users.
|
391
269
|
#
|
392
270
|
# @param [String] key Key you want to retrieve data. This field is mandatory.
|
393
|
-
# @param [
|
271
|
+
# @param [Integer] timeout Timeout for request (in milliseconds). Equals default_timeout in a config file.
|
394
272
|
# This field is optional.
|
395
273
|
#
|
396
274
|
# @return [Hash] Hash object of the json object.
|
@@ -399,13 +277,6 @@ module Kameleoon
|
|
399
277
|
JSON.parse(response) if response
|
400
278
|
end
|
401
279
|
|
402
|
-
##
|
403
|
-
# DEPRECATED. Please use `get_feature_variable` instead.
|
404
|
-
def retrieve_data_from_remote_source(key, timeout = @default_timeout)
|
405
|
-
warn '[DEPRECATION] `retrieve_data_from_remote_source` is deprecated. Please use `get_remote_date` instead.'
|
406
|
-
get_remote_data(key, timeout)
|
407
|
-
end
|
408
|
-
|
409
280
|
##
|
410
281
|
# The get_remote_visitor_data is a method for retrieving custom data for
|
411
282
|
# the latest visit of `visitor_code` from Kameleoon Data API and optionally adding it
|
@@ -415,7 +286,7 @@ module Kameleoon
|
|
415
286
|
# This field is mandatory.
|
416
287
|
# @param [Bool] add_data A boolean indicating whether the method should automatically add retrieved data
|
417
288
|
# for a visitor. If not specified, the default value is `True`. This field is optional.
|
418
|
-
# @param [
|
289
|
+
# @param [Integer] timeout Timeout for request (in milliseconds). Equals default_timeout in a config file.
|
419
290
|
# This field is optional.
|
420
291
|
#
|
421
292
|
# @return [Array] An array of data assigned to the given visitor.
|
@@ -427,29 +298,30 @@ module Kameleoon
|
|
427
298
|
end
|
428
299
|
|
429
300
|
##
|
430
|
-
#
|
431
|
-
#
|
432
|
-
#
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
#
|
439
|
-
#
|
301
|
+
# Retrieves data associated with a visitor's warehouse audiences and adds it to the visitor.
|
302
|
+
# Retrieves all audience data associated with the visitor in your data warehouse using the specified
|
303
|
+
# `visitor_code` and `warehouse_key`. The `warehouse_key` is typically your internal user ID.
|
304
|
+
# The `custom_data_index` parameter corresponds to the Kameleoon custom data that Kameleoon uses to target your
|
305
|
+
# visitors. You can refer to the
|
306
|
+
# <a href="https://help.kameleoon.com/warehouse-audience-targeting/">warehouse targeting documentation</a>
|
307
|
+
# for additional details. The method returns a `CustomData` object, confirming
|
308
|
+
# that the data has been added to the visitor and is available for targeting purposes.
|
309
|
+
#
|
310
|
+
# @param [String] visitor_code A unique visitor identification string, can't exceed 255 characters length.
|
311
|
+
# This field is mandatory.
|
312
|
+
# @param [Integer] custom_data_index An integer representing the index of the custom data you want to use to target
|
313
|
+
# your BigQuery Audiences. This field is mandatory.
|
314
|
+
# @param [String] warehouse_key A key to identify the warehouse data, typically your internal user ID.
|
315
|
+
# This field is optional.
|
316
|
+
# @param [Integer] timeout Timeout for request (in milliseconds). Equals default_timeout in a config file.
|
317
|
+
# This field is optional.
|
440
318
|
#
|
441
|
-
# @raise [Kameleoon::Exception::
|
319
|
+
# @raise [Kameleoon::Exception::VisitorCodeInvalid] If the visitor code is empty or longer than 255 chars.
|
320
|
+
# @raise [JSON::ParserError]
|
442
321
|
#
|
443
|
-
# @return [
|
444
|
-
def
|
445
|
-
|
446
|
-
@experiments.each do |experiment|
|
447
|
-
next unless check_targeting(visitor_code, experiment.id.to_i, experiment)
|
448
|
-
next if only_allocated && calculate_variation_for_experiment(visitor_code, experiment).nil?
|
449
|
-
|
450
|
-
list_ids.push(experiment.id.to_i)
|
451
|
-
end
|
452
|
-
list_ids
|
322
|
+
# @return [Kameleoon::CustomData] A `CustomData` instance confirming that the data has been added to the visitor.
|
323
|
+
def get_visitor_warehouse_audience(visitor_code, custom_data_index, timeout = nil, warehouse_key: nil)
|
324
|
+
@warehouse_manager.get_visitor_warehouse_audience(visitor_code, custom_data_index, warehouse_key, timeout)
|
453
325
|
end
|
454
326
|
|
455
327
|
##
|
@@ -457,24 +329,22 @@ module Kameleoon
|
|
457
329
|
#
|
458
330
|
# @return [Array] array of all feature flag keys
|
459
331
|
def get_feature_list # rubocop:disable Naming/AccessorMethodName
|
460
|
-
@feature_flags.
|
332
|
+
@data_file.feature_flags.keys
|
461
333
|
end
|
462
334
|
|
463
335
|
##
|
464
336
|
# Returns a list of active feature flag keys for a visitor
|
465
337
|
#
|
466
|
-
# @raise [Kameleoon::Exception::
|
338
|
+
# @raise [Kameleoon::Exception::VisitorCodeInvalid] If the visitor code is empty or longer than 255 chars
|
467
339
|
#
|
468
340
|
# @return [Array] array of active feature flag keys for a visitor
|
469
341
|
def get_active_feature_list_for_visitor(visitor_code)
|
470
|
-
|
342
|
+
Utils::VisitorCode.validate(visitor_code)
|
471
343
|
list_keys = []
|
472
|
-
@feature_flags.each do |feature_flag|
|
344
|
+
@data_file.feature_flags.each do |feature_key, feature_flag|
|
473
345
|
variation, rule, = _calculate_variation_key_for_feature(visitor_code, feature_flag)
|
474
346
|
variation_key = _get_variation_key(variation, rule, feature_flag)
|
475
|
-
if variation_key != Kameleoon::Configuration::VariationType::VARIATION_OFF
|
476
|
-
list_keys.push(feature_flag.feature_key)
|
477
|
-
end
|
347
|
+
list_keys.push(feature_key) if variation_key != Kameleoon::Configuration::VariationType::VARIATION_OFF
|
478
348
|
end
|
479
349
|
list_keys
|
480
350
|
end
|
@@ -499,56 +369,49 @@ module Kameleoon
|
|
499
369
|
# @return [String] JavasScript code to be inserted in your page to send automatically
|
500
370
|
# the exposure events to the analytics solution you are using.
|
501
371
|
def get_engine_tracking_code(visitor_code)
|
502
|
-
@
|
372
|
+
visitor_variations = @visitor_manager.get_visitor(visitor_code)&.variations
|
373
|
+
@hybrid_manager.get_engine_tracking_code(visitor_variations)
|
503
374
|
end
|
504
375
|
|
505
376
|
private
|
506
377
|
|
507
|
-
|
508
|
-
CACHE_EXPIRATION_TIMEOUT = 5
|
509
|
-
attr :site_code, :client_id, :client_secret, :access_token, :experiments, :feature_flags, :scheduler, :data,
|
510
|
-
:tracking_url, :default_timeout, :interval, :memory_limit, :verbose_mode
|
511
|
-
|
512
|
-
def read_config(config)
|
513
|
-
@client_id = config.client_id
|
514
|
-
@client_secret = config.client_secret
|
515
|
-
@default_timeout = config.default_timeout # in ms
|
516
|
-
@interval = "#{config.configuration_refresh_interval}m"
|
517
|
-
@data_maximum_size = config.visitor_data_maximum_size
|
518
|
-
@environment = config.environment
|
519
|
-
@verbose_mode = config.verbose_mode
|
520
|
-
if @client_id.nil? || @client_secret.nil?
|
521
|
-
warn 'Kameleoon SDK: Credentials are invalid: client_id or client_secret (or both) are empty'
|
522
|
-
end
|
523
|
-
end
|
378
|
+
HYBRID_EXPIRATION_TIME = 5
|
524
379
|
|
525
|
-
def
|
526
|
-
|
527
|
-
|
528
|
-
|
380
|
+
def fetch_configuration_initially
|
381
|
+
log('Initial configuration fetch is started.')
|
382
|
+
Thread.new do
|
383
|
+
ok = false
|
384
|
+
begin
|
385
|
+
ok = obtain_configuration
|
386
|
+
log('Initial configuration fetch failed') unless ok
|
387
|
+
rescue StandardError => e
|
388
|
+
log("Initial configuration fetch failed: #{e}")
|
389
|
+
end
|
390
|
+
@readiness.set(ok)
|
391
|
+
manage_configuration_update(@data_file.settings.real_time_update) if ok
|
529
392
|
end
|
530
393
|
end
|
531
394
|
|
532
395
|
def fetch_configuration_job(time_stamp = nil)
|
533
|
-
|
396
|
+
Thread.new do
|
397
|
+
ok = false
|
534
398
|
begin
|
535
399
|
ok = obtain_configuration(time_stamp)
|
536
|
-
if !ok && @settings.real_time_update
|
537
|
-
@settings.real_time_update = false
|
538
|
-
log('Switching to polling mode due to failed fetch')
|
539
|
-
end
|
540
400
|
rescue StandardError => e
|
541
401
|
log("Error occurred during configuration fetching: #{e}")
|
542
402
|
end
|
543
|
-
|
544
|
-
|
403
|
+
if !ok && @data_file.settings.real_time_update
|
404
|
+
@data_file.settings.real_time_update = false
|
405
|
+
log('Switching to polling mode due to failed fetch')
|
406
|
+
end
|
407
|
+
manage_configuration_update(@data_file.settings.real_time_update)
|
545
408
|
end
|
546
409
|
end
|
547
410
|
|
548
411
|
def start_configuration_update_job_if_needed
|
549
412
|
return unless @fetch_configuration_update_job.nil?
|
550
413
|
|
551
|
-
@fetch_configuration_update_job = Rufus::Scheduler.singleton.schedule_every @
|
414
|
+
@fetch_configuration_update_job = Rufus::Scheduler.singleton.schedule_every @config.refresh_interval_second do
|
552
415
|
log('Scheduled job to fetch configuration is started.')
|
553
416
|
fetch_configuration_job
|
554
417
|
end
|
@@ -608,12 +471,11 @@ module Kameleoon
|
|
608
471
|
return false unless response
|
609
472
|
|
610
473
|
configuration = JSON.parse(response)
|
611
|
-
@
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
@settings.update(configuration['configuration'])
|
474
|
+
@data_file = Configuration::DataFile.new(@config.environment).init(configuration)
|
475
|
+
@cookie_manager.consent_required =
|
476
|
+
@data_file.settings.is_consent_required && !@data_file.has_any_targeted_delivery_rule
|
477
|
+
@network_manager.url_provider.apply_data_api_domain(@data_file.settings.data_api_domain)
|
478
|
+
|
617
479
|
call_update_handler_if_needed(!time_stamp.nil?)
|
618
480
|
log "Feature flags are fetched: #{response.inspect}"
|
619
481
|
true
|
@@ -629,49 +491,16 @@ module Kameleoon
|
|
629
491
|
@update_configuration_handler.call
|
630
492
|
end
|
631
493
|
|
632
|
-
def
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
return key.to_s.to_i if threshold.negative?
|
637
|
-
end
|
638
|
-
nil
|
639
|
-
end
|
640
|
-
|
641
|
-
def find_feature_flag(feature_key)
|
642
|
-
if feature_key.is_a?(String)
|
643
|
-
feature_flag = @feature_flags.select { |ff| ff.feature_key == feature_key }.first
|
644
|
-
else
|
645
|
-
raise TypeError.new('Feature key should be a String or an Integer.'),
|
646
|
-
'Feature key should be a String or an Integer.'
|
647
|
-
end
|
648
|
-
error_message = "Feature #{feature_key} not found"
|
649
|
-
raise Exception::FeatureConfigurationNotFound.new(feature_key), error_message if feature_flag.nil?
|
650
|
-
|
651
|
-
feature_flag
|
652
|
-
end
|
653
|
-
|
654
|
-
def check_site_code_enable(campaign)
|
655
|
-
raise Exception::SiteCodeDisabled.new(site_code), site_code unless campaign.site_enabled
|
656
|
-
end
|
657
|
-
|
658
|
-
def data_not_sent(visitor_code)
|
659
|
-
@data.key?(visitor_code) ? @data[visitor_code].reject(&:sent) : []
|
494
|
+
def dispose
|
495
|
+
stop_configuration_update_job_if_needed
|
496
|
+
stop_real_time_configuration_service_if_needed
|
497
|
+
@visitor_manager.stop
|
660
498
|
end
|
661
499
|
|
662
500
|
def log(text)
|
663
501
|
print "Kameleoon SDK Log: #{text}\n" if @verbose_mode
|
664
502
|
end
|
665
503
|
|
666
|
-
def add_user_agent_data(visitor_code, user_agent)
|
667
|
-
@user_agents[visitor_code] = user_agent
|
668
|
-
end
|
669
|
-
|
670
|
-
def set_user_agent_to_headers(visitor_code, headers)
|
671
|
-
user_agent = @user_agents[visitor_code]
|
672
|
-
headers['User-Agent'] = user_agent.value unless user_agent.nil?
|
673
|
-
end
|
674
|
-
|
675
504
|
# Uncomment when using storage
|
676
505
|
# def get_valid_saved_variation(visitor_code, experiment)
|
677
506
|
# variation_id = @variation_storage.get_variation_id(visitor_code, experiment.id.to_i)
|
@@ -685,29 +514,38 @@ module Kameleoon
|
|
685
514
|
|
686
515
|
def check_targeting(visitor_code, campaign_id, exp_ff_rule)
|
687
516
|
segment = exp_ff_rule.targeting_segment
|
688
|
-
segment.nil?
|
517
|
+
return true if segment.nil?
|
518
|
+
|
519
|
+
visitor = @visitor_manager.get_visitor(visitor_code)
|
520
|
+
segment.check_tree(->(type) { get_condition_data(type, visitor, visitor_code, campaign_id) })
|
689
521
|
end
|
690
522
|
|
691
|
-
def get_condition_data(type, visitor_code, campaign_id)
|
523
|
+
def get_condition_data(type, visitor, visitor_code, campaign_id)
|
692
524
|
condition_data = nil
|
693
525
|
case type
|
694
|
-
when Kameleoon::Targeting::ConditionType::CUSTOM_DATUM
|
695
|
-
|
696
|
-
|
697
|
-
Kameleoon::Targeting::ConditionType::
|
698
|
-
|
699
|
-
|
700
|
-
condition_data =
|
526
|
+
when Kameleoon::Targeting::ConditionType::CUSTOM_DATUM
|
527
|
+
condition_data = visitor.custom_data unless visitor.nil?
|
528
|
+
when Kameleoon::Targeting::ConditionType::PAGE_TITLE,
|
529
|
+
Kameleoon::Targeting::ConditionType::PAGE_URL
|
530
|
+
condition_data = visitor.page_view_visits unless visitor.nil?
|
531
|
+
when Kameleoon::Targeting::ConditionType::DEVICE_TYPE
|
532
|
+
condition_data = visitor.device unless visitor.nil?
|
533
|
+
when Kameleoon::Targeting::ConditionType::BROWSER
|
534
|
+
condition_data = visitor.browser unless visitor.nil?
|
535
|
+
when Kameleoon::Targeting::ConditionType::CONVERSIONS
|
536
|
+
condition_data = visitor.conversions unless visitor.nil?
|
701
537
|
when Kameleoon::Targeting::ConditionType::SDK_LANGUAGE
|
702
538
|
condition_data = Kameleoon::Targeting::SdkInfo.new(Kameleoon::SDK_NAME, Kameleoon::SDK_VERSION)
|
703
539
|
when Kameleoon::Targeting::ConditionType::VISITOR_CODE
|
704
540
|
condition_data = visitor_code
|
705
541
|
when Kameleoon::Targeting::ConditionType::TARGET_EXPERIMENT
|
706
|
-
condition_data =
|
542
|
+
condition_data = visitor.variations unless visitor.nil?
|
707
543
|
when Kameleoon::Targeting::ConditionType::EXCLUSIVE_EXPERIMENT
|
708
|
-
|
709
|
-
|
710
|
-
|
544
|
+
unless visitor.nil?
|
545
|
+
condition_data = OpenStruct.new
|
546
|
+
condition_data.experiment_id = campaign_id
|
547
|
+
condition_data.storage = visitor.variations
|
548
|
+
end
|
711
549
|
end
|
712
550
|
condition_data
|
713
551
|
end
|
@@ -715,16 +553,21 @@ module Kameleoon
|
|
715
553
|
##
|
716
554
|
# helper method for getting variation key for feature flag
|
717
555
|
def _get_feature_variation_key(visitor_code, feature_key)
|
718
|
-
|
719
|
-
feature_flag =
|
556
|
+
Utils::VisitorCode.validate(visitor_code)
|
557
|
+
feature_flag = @data_file.get_feature_flag(feature_key)
|
720
558
|
variation, rule = _calculate_variation_key_for_feature(visitor_code, feature_flag)
|
721
559
|
variation_key = _get_variation_key(variation, rule, feature_flag)
|
560
|
+
visitor = nil
|
722
561
|
unless rule.nil?
|
723
562
|
experiment_id = rule.experiment_id
|
724
563
|
variation_id = variation.variation_id unless variation.nil?
|
725
|
-
|
564
|
+
visitor = @visitor_manager.get_or_create_visitor(visitor_code)
|
565
|
+
unless experiment_id.nil? || variation_id.nil?
|
566
|
+
as_variation = Kameleoon::DataManager::AssignedVariation.new(experiment_id, variation_id, rule.type)
|
567
|
+
visitor.assign_variation(as_variation)
|
568
|
+
end
|
726
569
|
end
|
727
|
-
_send_tracking_request(visitor_code,
|
570
|
+
_send_tracking_request(visitor_code, visitor)
|
728
571
|
[feature_flag, variation_key]
|
729
572
|
end
|
730
573
|
|
@@ -737,13 +580,13 @@ module Kameleoon
|
|
737
580
|
next unless check_targeting(visitor_code, feature_flag.id, rule)
|
738
581
|
|
739
582
|
# uses for rule exposition
|
740
|
-
hash_rule =
|
583
|
+
hash_rule = Utils::HashDouble.obtain_rule(visitor_code, rule.id, rule.respool_time)
|
741
584
|
# check main expostion for rule with hashRule
|
742
585
|
if hash_rule <= rule.exposition
|
743
586
|
return [rule.variation_by_exposition[0], rule] if rule.targeted_delivery_type?
|
744
587
|
|
745
588
|
# uses for variation's expositions
|
746
|
-
hash_variation =
|
589
|
+
hash_variation = Utils::HashDouble.obtain_rule(visitor_code, rule.experiment_id, rule.respool_time)
|
747
590
|
# get variation key with new hashVariation
|
748
591
|
variation = rule.get_variation(hash_variation)
|
749
592
|
return [variation, rule] unless variation.nil?
|
@@ -755,16 +598,8 @@ module Kameleoon
|
|
755
598
|
[nil, nil]
|
756
599
|
end
|
757
600
|
|
758
|
-
def save_variation(visitor_code, experiment_id, variation_id)
|
759
|
-
return if experiment_id.nil? || variation_id.nil?
|
760
|
-
|
761
|
-
@variation_storage.update_variation(visitor_code, experiment_id, variation_id)
|
762
|
-
@hybrid_manager.add_variation(visitor_code, experiment_id, variation_id)
|
763
|
-
end
|
764
|
-
|
765
601
|
def _get_variation_key(var_by_exp, rule, feature_flag)
|
766
602
|
return var_by_exp.variation_key unless var_by_exp.nil?
|
767
|
-
|
768
603
|
return Kameleoon::Configuration::VariationType::VARIATION_OFF if !rule.nil? && rule.experimentation_type?
|
769
604
|
|
770
605
|
feature_flag.default_variation_key
|
@@ -772,16 +607,37 @@ module Kameleoon
|
|
772
607
|
|
773
608
|
##
|
774
609
|
# helper method for sending tracking requests for new FF
|
775
|
-
def _send_tracking_request(visitor_code,
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
610
|
+
def _send_tracking_request(visitor_code, visitor = nil, force_request = true)
|
611
|
+
if visitor.nil?
|
612
|
+
visitor = @visitor_manager.get_visitor(visitor_code)
|
613
|
+
return if visitor.nil? && @data_file.settings.is_consent_required
|
614
|
+
end
|
615
|
+
consent = consent_provided?(visitor)
|
616
|
+
user_agent = visitor&.user_agent
|
617
|
+
unsent = visitor.nil? ? [] : select_unsent_data(visitor, consent)
|
618
|
+
if unsent.empty?
|
619
|
+
return unless force_request && consent
|
620
|
+
|
621
|
+
unsent.push(Network::ActivityEvent.new)
|
622
|
+
end
|
623
|
+
log "Start post tracking: #{unsent.inspect}"
|
624
|
+
@network_manager.send_tracking_data(visitor_code, unsent, user_agent)
|
625
|
+
end
|
626
|
+
|
627
|
+
def select_unsent_data(visitor, consent)
|
628
|
+
unsent = []
|
629
|
+
if consent
|
630
|
+
visitor.enumerate_sendable_data { |data| unsent.push(data) unless data.sent }
|
780
631
|
else
|
781
|
-
|
632
|
+
visitor.conversions.enumerate { |conversion| unsent.push(conversion) unless conversion.sent }
|
633
|
+
if @data_file.has_any_targeted_delivery_rule
|
634
|
+
visitor.variations.enumerate do |variation|
|
635
|
+
unsent.push(variation) unless
|
636
|
+
variation.sent || (variation.rule_type != Configuration::RuleType::TARGETED_DELIVERY)
|
637
|
+
end
|
638
|
+
end
|
782
639
|
end
|
783
|
-
|
784
|
-
@network_manager.send_tracking_data(visitor_code, data_not_sent, user_agent)
|
640
|
+
unsent
|
785
641
|
end
|
786
642
|
|
787
643
|
##
|
@@ -820,5 +676,9 @@ module Kameleoon
|
|
820
676
|
log("Parsing of visitor data of '#{visitor_code}' failed: #{e}")
|
821
677
|
[]
|
822
678
|
end
|
679
|
+
|
680
|
+
def consent_provided?(visitor)
|
681
|
+
!@data_file.settings.is_consent_required || visitor&.legal_consent
|
682
|
+
end
|
823
683
|
end
|
824
684
|
end
|