kameleoon-client-ruby 3.1.1 → 3.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/kameleoon/configuration/custom_data_info.rb +43 -0
- data/lib/kameleoon/configuration/data_file.rb +31 -2
- data/lib/kameleoon/configuration/rule.rb +4 -1
- data/lib/kameleoon/configuration/variation_exposition.rb +1 -1
- data/lib/kameleoon/data/browser.rb +19 -0
- data/lib/kameleoon/data/cookie.rb +14 -0
- data/lib/kameleoon/data/custom_data.rb +2 -0
- data/lib/kameleoon/data/data.rb +2 -0
- data/lib/kameleoon/data/geolocation.rb +45 -0
- data/lib/kameleoon/data/kcs_heat.rb +12 -0
- data/lib/kameleoon/data/manager/assigned_variation.rb +2 -1
- data/lib/kameleoon/data/manager/page_view_visit.rb +16 -2
- data/lib/kameleoon/data/manager/visitor.rb +71 -14
- data/lib/kameleoon/data/manager/visitor_manager.rb +23 -1
- data/lib/kameleoon/data/operating_system.rb +72 -0
- data/lib/kameleoon/data/page_view.rb +2 -2
- data/lib/kameleoon/data/visitor_visits.rb +20 -0
- data/lib/kameleoon/kameleoon_client.rb +122 -94
- data/lib/kameleoon/managers/remote_data/remote_data_manager.rb +55 -0
- data/lib/kameleoon/managers/remote_data/remote_visitor_data.rb +188 -0
- data/lib/kameleoon/managers/warehouse/warehouse_manager.rb +3 -1
- data/lib/kameleoon/network/access_token_source.rb +22 -5
- data/lib/kameleoon/network/network_manager.rb +10 -10
- data/lib/kameleoon/network/response.rb +1 -1
- data/lib/kameleoon/network/url_provider.rb +16 -12
- data/lib/kameleoon/targeting/condition.rb +24 -2
- data/lib/kameleoon/targeting/condition_factory.rb +39 -6
- data/lib/kameleoon/targeting/conditions/cookie_condition.rb +55 -0
- data/lib/kameleoon/targeting/conditions/exclusive_feature_flag_condition.rb +27 -0
- data/lib/kameleoon/targeting/conditions/geolocation_condition.rb +33 -0
- data/lib/kameleoon/targeting/conditions/kcs_heat_range_condition.rb +37 -0
- data/lib/kameleoon/targeting/conditions/number_condition.rb +38 -0
- data/lib/kameleoon/targeting/conditions/operating_system_condition.rb +27 -0
- data/lib/kameleoon/targeting/conditions/page_view_number_condition.rb +28 -0
- data/lib/kameleoon/targeting/conditions/previous_page_condition.rb +35 -0
- data/lib/kameleoon/targeting/conditions/segment_condition.rb +42 -0
- data/lib/kameleoon/targeting/conditions/target_feature_flag_condition.rb +59 -0
- data/lib/kameleoon/targeting/conditions/time_elapsed_since_visit_condition.rb +29 -0
- data/lib/kameleoon/targeting/conditions/visit_number_today_condition.rb +28 -0
- data/lib/kameleoon/targeting/conditions/visit_number_total_condition.rb +23 -0
- data/lib/kameleoon/targeting/conditions/visitor_new_return_condition.rb +34 -0
- data/lib/kameleoon/targeting/targeting_manager.rb +68 -0
- data/lib/kameleoon/types/remote_visitor_data_filter.rb +29 -0
- data/lib/kameleoon/types/variable.rb +17 -0
- data/lib/kameleoon/types/variation.rb +18 -0
- data/lib/kameleoon/utils.rb +1 -0
- data/lib/kameleoon/version.rb +1 -1
- metadata +28 -4
- data/lib/kameleoon/targeting/conditions/exclusive_experiment.rb +0 -18
- data/lib/kameleoon/targeting/conditions/target_experiment.rb +0 -45
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'kameleoon/client_readiness'
|
4
4
|
require 'kameleoon/targeting/models'
|
5
|
+
require 'kameleoon/targeting/targeting_manager'
|
5
6
|
require 'kameleoon/exceptions'
|
6
7
|
require 'kameleoon/data/custom_data'
|
7
8
|
require 'kameleoon/data/user_agent'
|
@@ -19,6 +20,7 @@ require 'kameleoon/managers/warehouse/warehouse_manager'
|
|
19
20
|
require 'kameleoon/real_time/real_time_configuration_service'
|
20
21
|
require 'kameleoon/hybrid/manager'
|
21
22
|
require 'kameleoon/storage/cache_factory'
|
23
|
+
require 'kameleoon/managers/remote_data/remote_data_manager'
|
22
24
|
require 'rufus/scheduler'
|
23
25
|
require 'yaml'
|
24
26
|
require 'json'
|
@@ -46,8 +48,8 @@ module Kameleoon
|
|
46
48
|
@real_time_configuration_service = nil
|
47
49
|
@update_configuration_handler = nil
|
48
50
|
@fetch_configuration_update_job = nil
|
49
|
-
@data_file = Configuration::DataFile.new(config.environment)
|
50
|
-
@visitor_manager = Kameleoon::DataManager::VisitorManager.new(config.session_duration_second)
|
51
|
+
@data_file = Configuration::DataFile.new(config.environment, method(:log))
|
52
|
+
@visitor_manager = Kameleoon::DataManager::VisitorManager.new(config.session_duration_second, method(:log))
|
51
53
|
@hybrid_manager = Kameleoon::Hybrid::ManagerImpl.new(HYBRID_EXPIRATION_TIME, method(:log))
|
52
54
|
@network_manager = Network::NetworkManager.new(
|
53
55
|
config.environment,
|
@@ -57,8 +59,12 @@ module Kameleoon
|
|
57
59
|
method(:log)
|
58
60
|
)
|
59
61
|
@warehouse_manager = Managers::Warehouse::WarehouseManager.new(@network_manager, @visitor_manager, method(:log))
|
62
|
+
@remote_data_manager = Kameleoon::Managers::RemoteData::RemoteDataManager.new(@network_manager, @visitor_manager,
|
63
|
+
method(:log))
|
60
64
|
@cookie_manager = Network::Cookie::CookieManager.new(config.top_level_domain)
|
61
65
|
@readiness = ClientReadiness.new
|
66
|
+
@targeting_manager = Kameleoon::Targeting::TargetingManager.new(@visitor_manager, @data_file)
|
67
|
+
@visitor_manager.custom_data_info = @data_file.custom_data_info
|
62
68
|
end
|
63
69
|
|
64
70
|
def wait_init
|
@@ -117,8 +123,7 @@ module Kameleoon
|
|
117
123
|
#
|
118
124
|
def add_data(visitor_code, *args)
|
119
125
|
Utils::VisitorCode.validate(visitor_code)
|
120
|
-
|
121
|
-
visitor.add_data(method(:log), *args)
|
126
|
+
@visitor_manager.add_data(visitor_code, *args)
|
122
127
|
end
|
123
128
|
|
124
129
|
##
|
@@ -133,13 +138,15 @@ module Kameleoon
|
|
133
138
|
# @param [String] visitor_code Visitor code
|
134
139
|
# @param [Integer] goal_id Id of the goal
|
135
140
|
# @param [Float] revenue Optional - Revenue of the conversion.
|
141
|
+
# @param [Bool] is_unique_identifier Parameter that specifies whether the visitorCode is a unique identifier.
|
142
|
+
# This field is optional.
|
136
143
|
#
|
137
144
|
# @raise [Kameleoon::Exception::VisitorCodeInvalid] If the visitor code is empty or longer than 255 chars
|
138
145
|
#
|
139
|
-
def track_conversion(visitor_code, goal_id, revenue = 0.0)
|
146
|
+
def track_conversion(visitor_code, goal_id, revenue = 0.0, is_unique_identifier: false)
|
140
147
|
Utils::VisitorCode.validate(visitor_code)
|
141
148
|
add_data(visitor_code, Conversion.new(goal_id, revenue))
|
142
|
-
flush(visitor_code)
|
149
|
+
flush(visitor_code, is_unique_identifier: is_unique_identifier)
|
143
150
|
end
|
144
151
|
|
145
152
|
##
|
@@ -150,15 +157,19 @@ module Kameleoon
|
|
150
157
|
# With this method you can manually send it.
|
151
158
|
#
|
152
159
|
# @param [String] visitor_code Optional field - Visitor code, without visitor code it flush all of the data
|
160
|
+
# @param [Bool] is_unique_identifier Parameter that specifies whether the visitorCode is a unique identifier.
|
161
|
+
# This field is optional.
|
153
162
|
#
|
154
163
|
# @raise [Kameleoon::Exception::VisitorCodeInvalid] If the visitor code is not nil and is empty or longer than 255 chars
|
155
164
|
#
|
156
|
-
def flush(visitor_code = nil)
|
165
|
+
def flush(visitor_code = nil, is_unique_identifier: false)
|
157
166
|
Utils::VisitorCode.validate(visitor_code) unless visitor_code.nil?
|
158
167
|
if visitor_code.nil?
|
159
|
-
@visitor_manager.enumerate
|
168
|
+
@visitor_manager.enumerate do |visitor_code, visitor|
|
169
|
+
_send_tracking_request(visitor_code, visitor, false, is_unique_identifier)
|
170
|
+
end
|
160
171
|
else
|
161
|
-
_send_tracking_request(visitor_code, nil, true)
|
172
|
+
_send_tracking_request(visitor_code, nil, true, is_unique_identifier)
|
162
173
|
end
|
163
174
|
end
|
164
175
|
|
@@ -175,12 +186,14 @@ module Kameleoon
|
|
175
186
|
#
|
176
187
|
# @param [String] visitor_code Unique identifier of the user. This field is mandatory.
|
177
188
|
# @param [String] feature_key Key of the feature flag you want to expose to a user. This field is mandatory.
|
189
|
+
# @param [Bool] is_unique_identifier Parameter that specifies whether the visitorCode is a unique identifier.
|
190
|
+
# This field is optional.
|
178
191
|
#
|
179
192
|
# @raise [Kameleoon::Exception::FeatureNotFound] Feature Flag isn't found in this configuration
|
180
193
|
# @raise [Kameleoon::Exception::VisitorCodeInvalid] If the visitor code is empty or longer than 255 chars
|
181
194
|
#
|
182
|
-
def feature_active?(visitor_code, feature_key)
|
183
|
-
_, variation_key = _get_feature_variation_key(visitor_code, feature_key)
|
195
|
+
def feature_active?(visitor_code, feature_key, is_unique_identifier: false)
|
196
|
+
_, variation_key = _get_feature_variation_key(visitor_code, feature_key, is_unique_identifier)
|
184
197
|
variation_key != Kameleoon::Configuration::VariationType::VARIATION_OFF
|
185
198
|
rescue Exception::FeatureEnvironmentDisabled
|
186
199
|
false
|
@@ -197,14 +210,16 @@ module Kameleoon
|
|
197
210
|
#
|
198
211
|
# @param [String] visitor_code
|
199
212
|
# @param [String] feature_key
|
213
|
+
# @param [Bool] is_unique_identifier Parameter that specifies whether the visitorCode is a unique identifier.
|
214
|
+
# This field is optional.
|
200
215
|
#
|
201
216
|
# @raise [Kameleoon::Exception::FeatureNotFound] Feature Flag isn't found in this configuration
|
202
217
|
# @raise [Kameleoon::Exception::VisitorCodeInvalid] If the visitor code is empty or longer than 255 chars
|
203
218
|
# @raise [Kameleoon::Exception::FeatureEnvironmentDisabled] If the requested feature flag is disabled for
|
204
219
|
# the current environment
|
205
220
|
#
|
206
|
-
def get_feature_variation_key(visitor_code, feature_key)
|
207
|
-
_, variation_key = _get_feature_variation_key(visitor_code, feature_key)
|
221
|
+
def get_feature_variation_key(visitor_code, feature_key, is_unique_identifier: false)
|
222
|
+
_, variation_key = _get_feature_variation_key(visitor_code, feature_key, is_unique_identifier)
|
208
223
|
variation_key
|
209
224
|
end
|
210
225
|
|
@@ -216,6 +231,8 @@ module Kameleoon
|
|
216
231
|
# @param [String] visitor_code
|
217
232
|
# @param [String] feature_key
|
218
233
|
# @param [String] variable_name
|
234
|
+
# @param [Bool] is_unique_identifier Parameter that specifies whether the visitorCode is a unique identifier.
|
235
|
+
# This field is optional.
|
219
236
|
#
|
220
237
|
# @raise [Kameleoon::Exception::FeatureNotFound] Feature Flag isn't found in this configuration
|
221
238
|
# @raise [Kameleoon::Exception::FeatureVariableNotFound]
|
@@ -223,8 +240,8 @@ module Kameleoon
|
|
223
240
|
# @raise [Kameleoon::Exception::FeatureEnvironmentDisabled] If the requested feature flag is disabled for
|
224
241
|
# the current environment
|
225
242
|
#
|
226
|
-
def get_feature_variable(visitor_code, feature_key, variable_name)
|
227
|
-
feature_flag, variation_key = _get_feature_variation_key(visitor_code, feature_key)
|
243
|
+
def get_feature_variable(visitor_code, feature_key, variable_name, is_unique_identifier: false)
|
244
|
+
feature_flag, variation_key = _get_feature_variation_key(visitor_code, feature_key, is_unique_identifier)
|
228
245
|
variation = feature_flag.get_variation_key(variation_key)
|
229
246
|
variable = variation&.get_variable_by_key(variable_name)
|
230
247
|
if variable.nil?
|
@@ -273,8 +290,7 @@ module Kameleoon
|
|
273
290
|
#
|
274
291
|
# @return [Hash] Hash object of the json object.
|
275
292
|
def get_remote_data(key, timeout = @default_timeout)
|
276
|
-
|
277
|
-
JSON.parse(response) if response
|
293
|
+
@remote_data_manager.get_data(key, timeout)
|
278
294
|
end
|
279
295
|
|
280
296
|
##
|
@@ -288,13 +304,12 @@ module Kameleoon
|
|
288
304
|
# for a visitor. If not specified, the default value is `True`. This field is optional.
|
289
305
|
# @param [Integer] timeout Timeout for request (in milliseconds). Equals default_timeout in a config file.
|
290
306
|
# This field is optional.
|
307
|
+
# @param [Bool] is_unique_identifier Parameter that specifies whether the visitorCode is a unique identifier.
|
308
|
+
# This field is optional.
|
291
309
|
#
|
292
310
|
# @return [Array] An array of data assigned to the given visitor.
|
293
|
-
def get_remote_visitor_data(visitor_code, timeout = nil, add_data: true)
|
294
|
-
|
295
|
-
data_array = parse_custom_data_array(visitor_code, response)
|
296
|
-
add_data(visitor_code, *data_array) if add_data && !data_array.empty?
|
297
|
-
data_array
|
311
|
+
def get_remote_visitor_data(visitor_code, timeout = nil, add_data: true, filter: nil, is_unique_identifier: false)
|
312
|
+
@remote_data_manager.get_visitor_data(visitor_code, add_data, filter, is_unique_identifier, timeout)
|
298
313
|
end
|
299
314
|
|
300
315
|
##
|
@@ -338,10 +353,15 @@ module Kameleoon
|
|
338
353
|
# @raise [Kameleoon::Exception::VisitorCodeInvalid] If the visitor code is empty or longer than 255 chars
|
339
354
|
#
|
340
355
|
# @return [Array] array of active feature flag keys for a visitor
|
356
|
+
#
|
357
|
+
# DEPRECATED. Please use `get_active_features` instead.
|
341
358
|
def get_active_feature_list_for_visitor(visitor_code)
|
359
|
+
warn '[DEPRECATION] `get_active_feature_list_for_visitor` is deprecated. Please use `get_active_features` instead.'
|
342
360
|
Utils::VisitorCode.validate(visitor_code)
|
343
361
|
list_keys = []
|
344
362
|
@data_file.feature_flags.each do |feature_key, feature_flag|
|
363
|
+
next unless feature_flag.environment_enabled
|
364
|
+
|
345
365
|
variation, rule, = _calculate_variation_key_for_feature(visitor_code, feature_flag)
|
346
366
|
variation_key = _get_variation_key(variation, rule, feature_flag)
|
347
367
|
list_keys.push(feature_key) if variation_key != Kameleoon::Configuration::VariationType::VARIATION_OFF
|
@@ -349,6 +369,50 @@ module Kameleoon
|
|
349
369
|
list_keys
|
350
370
|
end
|
351
371
|
|
372
|
+
##
|
373
|
+
# Returns a Hash that contains the assigned variations of the active features using the keys
|
374
|
+
# of the corresponding active features.
|
375
|
+
#
|
376
|
+
# @param [String] visitor_code unique identifier of a visitor.
|
377
|
+
#
|
378
|
+
# @raise [Kameleoon::Exception::VisitorCodeInvalid] If the visitor code is empty or longer than 255 chars or is nil
|
379
|
+
#
|
380
|
+
# @return [Hash] Hash of active features for a visitor
|
381
|
+
def get_active_features(visitor_code)
|
382
|
+
Utils::VisitorCode.validate(visitor_code)
|
383
|
+
map_active_features = {}
|
384
|
+
|
385
|
+
@data_file.feature_flags.each_value do |feature_flag|
|
386
|
+
next unless feature_flag.environment_enabled
|
387
|
+
|
388
|
+
var_by_exp, rule = _calculate_variation_key_for_feature(visitor_code, feature_flag)
|
389
|
+
variation_key = _get_variation_key(var_by_exp, rule, feature_flag)
|
390
|
+
|
391
|
+
next if variation_key == Configuration::VariationType::VARIATION_OFF
|
392
|
+
|
393
|
+
variation = feature_flag.get_variation_key(variation_key)
|
394
|
+
variables = {}
|
395
|
+
|
396
|
+
variation&.variables&.each do |variable|
|
397
|
+
variables[variable.key] = Kameleoon::Types::Variable.new(
|
398
|
+
variable.key,
|
399
|
+
variable.type,
|
400
|
+
_parse_feature_variable(variable)
|
401
|
+
)
|
402
|
+
end
|
403
|
+
|
404
|
+
variables.freeze
|
405
|
+
map_active_features[feature_flag.feature_key] = Kameleoon::Types::Variation.new(
|
406
|
+
variation_key,
|
407
|
+
var_by_exp ? var_by_exp.variation_id : nil,
|
408
|
+
rule ? rule.experiment_id : nil,
|
409
|
+
variables
|
410
|
+
)
|
411
|
+
end
|
412
|
+
|
413
|
+
map_active_features.freeze
|
414
|
+
end
|
415
|
+
|
352
416
|
##
|
353
417
|
# The `on_update_configuration()` method allows you to handle the event when configuration
|
354
418
|
# has updated data. It takes one input parameter: callable **handler**. The handler
|
@@ -471,16 +535,21 @@ module Kameleoon
|
|
471
535
|
return false unless response
|
472
536
|
|
473
537
|
configuration = JSON.parse(response)
|
474
|
-
@data_file = Configuration::DataFile.new(@config.environment).init(configuration)
|
475
|
-
@
|
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
|
-
|
538
|
+
@data_file = Configuration::DataFile.new(@config.environment, method(:log)).init(configuration)
|
539
|
+
apply_new_configuration(@data_file)
|
479
540
|
call_update_handler_if_needed(!time_stamp.nil?)
|
480
541
|
log "Feature flags are fetched: #{response.inspect}"
|
481
542
|
true
|
482
543
|
end
|
483
544
|
|
545
|
+
def apply_new_configuration(data_file)
|
546
|
+
@cookie_manager.consent_required =
|
547
|
+
data_file.settings.is_consent_required && !data_file.has_any_targeted_delivery_rule
|
548
|
+
@network_manager.url_provider.apply_data_api_domain(data_file.settings.data_api_domain)
|
549
|
+
@targeting_manager = Kameleoon::Targeting::TargetingManager.new(@visitor_manager, data_file)
|
550
|
+
@visitor_manager.custom_data_info = data_file.custom_data_info
|
551
|
+
end
|
552
|
+
|
484
553
|
##
|
485
554
|
# Call the handler when configuraiton was updated with new time stamp.
|
486
555
|
#
|
@@ -513,47 +582,12 @@ module Kameleoon
|
|
513
582
|
# end
|
514
583
|
|
515
584
|
def check_targeting(visitor_code, campaign_id, exp_ff_rule)
|
516
|
-
|
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) })
|
521
|
-
end
|
522
|
-
|
523
|
-
def get_condition_data(type, visitor, visitor_code, campaign_id)
|
524
|
-
condition_data = nil
|
525
|
-
case type
|
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?
|
537
|
-
when Kameleoon::Targeting::ConditionType::SDK_LANGUAGE
|
538
|
-
condition_data = Kameleoon::Targeting::SdkInfo.new(Kameleoon::SDK_NAME, Kameleoon::SDK_VERSION)
|
539
|
-
when Kameleoon::Targeting::ConditionType::VISITOR_CODE
|
540
|
-
condition_data = visitor_code
|
541
|
-
when Kameleoon::Targeting::ConditionType::TARGET_EXPERIMENT
|
542
|
-
condition_data = visitor.variations unless visitor.nil?
|
543
|
-
when Kameleoon::Targeting::ConditionType::EXCLUSIVE_EXPERIMENT
|
544
|
-
unless visitor.nil?
|
545
|
-
condition_data = OpenStruct.new
|
546
|
-
condition_data.experiment_id = campaign_id
|
547
|
-
condition_data.storage = visitor.variations
|
548
|
-
end
|
549
|
-
end
|
550
|
-
condition_data
|
585
|
+
@targeting_manager.check_targeting(visitor_code, campaign_id, exp_ff_rule)
|
551
586
|
end
|
552
587
|
|
553
588
|
##
|
554
589
|
# helper method for getting variation key for feature flag
|
555
|
-
def _get_feature_variation_key(visitor_code, feature_key)
|
556
|
-
Utils::VisitorCode.validate(visitor_code)
|
590
|
+
def _get_feature_variation_key(visitor_code, feature_key, is_unique_identifier = false)
|
557
591
|
feature_flag = @data_file.get_feature_flag(feature_key)
|
558
592
|
variation, rule = _calculate_variation_key_for_feature(visitor_code, feature_flag)
|
559
593
|
variation_key = _get_variation_key(variation, rule, feature_flag)
|
@@ -567,7 +601,7 @@ module Kameleoon
|
|
567
601
|
visitor.assign_variation(as_variation)
|
568
602
|
end
|
569
603
|
end
|
570
|
-
|
604
|
+
flush(visitor_code, is_unique_identifier: is_unique_identifier)
|
571
605
|
[feature_flag, variation_key]
|
572
606
|
end
|
573
607
|
|
@@ -577,16 +611,19 @@ module Kameleoon
|
|
577
611
|
# no rules -> return default_variation_key
|
578
612
|
feature_flag.rules.each do |rule|
|
579
613
|
# check if visitor is targeted for rule, else next rule
|
580
|
-
next unless check_targeting(visitor_code,
|
614
|
+
next unless check_targeting(visitor_code, rule.experiment_id, rule)
|
581
615
|
|
616
|
+
vis = @visitor_manager.get_visitor(visitor_code)
|
617
|
+
# use mappingIdentifier instead of visitorCode if it was set up
|
618
|
+
code_for_hash = vis&.mapping_identifier || visitor_code
|
582
619
|
# uses for rule exposition
|
583
|
-
hash_rule = Utils::HashDouble.obtain_rule(
|
620
|
+
hash_rule = Utils::HashDouble.obtain_rule(code_for_hash, rule.id, rule.respool_time)
|
584
621
|
# check main expostion for rule with hashRule
|
585
622
|
if hash_rule <= rule.exposition
|
586
|
-
return [rule.
|
623
|
+
return [rule.first_variation, rule] if rule.targeted_delivery_type?
|
587
624
|
|
588
625
|
# uses for variation's expositions
|
589
|
-
hash_variation = Utils::HashDouble.obtain_rule(
|
626
|
+
hash_variation = Utils::HashDouble.obtain_rule(code_for_hash, rule.experiment_id, rule.respool_time)
|
590
627
|
# get variation key with new hashVariation
|
591
628
|
variation = rule.get_variation(hash_variation)
|
592
629
|
return [variation, rule] unless variation.nil?
|
@@ -607,11 +644,12 @@ module Kameleoon
|
|
607
644
|
|
608
645
|
##
|
609
646
|
# helper method for sending tracking requests for new FF
|
610
|
-
def _send_tracking_request(visitor_code, visitor = nil, force_request = true)
|
647
|
+
def _send_tracking_request(visitor_code, visitor = nil, force_request = true, is_unique_identifier = false)
|
611
648
|
if visitor.nil?
|
612
649
|
visitor = @visitor_manager.get_visitor(visitor_code)
|
613
650
|
return if visitor.nil? && @data_file.settings.is_consent_required
|
614
651
|
end
|
652
|
+
use_mapping_value, visitor = create_anonymous_if_required(visitor_code, visitor, is_unique_identifier)
|
615
653
|
consent = consent_provided?(visitor)
|
616
654
|
user_agent = visitor&.user_agent
|
617
655
|
unsent = visitor.nil? ? [] : select_unsent_data(visitor, consent)
|
@@ -621,7 +659,20 @@ module Kameleoon
|
|
621
659
|
unsent.push(Network::ActivityEvent.new)
|
622
660
|
end
|
623
661
|
log "Start post tracking: #{unsent.inspect}"
|
624
|
-
@network_manager.send_tracking_data(visitor_code, unsent, user_agent)
|
662
|
+
@network_manager.send_tracking_data(visitor_code, unsent, user_agent, use_mapping_value)
|
663
|
+
end
|
664
|
+
|
665
|
+
def create_anonymous_if_required(visitor_code, visitor, is_unique_identifier)
|
666
|
+
use_mapping_value = is_unique_identifier && !visitor&.mapping_identifier.nil?
|
667
|
+
# need to find if anonymous visitor is behind unique (anonym doesn't exist if MappingIdentifier == null)
|
668
|
+
if is_unique_identifier && visitor&.mapping_identifier.nil?
|
669
|
+
# We haven't anonymous behind, in this case we should create "fake" anonymous with id == visitorCode
|
670
|
+
# and link it with with mapping value == visitorCode (like we do as we have real anonymous visitor)
|
671
|
+
visitor = @visitor_manager.add_data(visitor_code,
|
672
|
+
CustomData.new(@data_file.custom_data_info.mapping_identifier_index,
|
673
|
+
visitor_code))
|
674
|
+
end
|
675
|
+
[use_mapping_value, visitor]
|
625
676
|
end
|
626
677
|
|
627
678
|
def select_unsent_data(visitor, consent)
|
@@ -654,29 +705,6 @@ module Kameleoon
|
|
654
705
|
end
|
655
706
|
end
|
656
707
|
|
657
|
-
##
|
658
|
-
# helper method used by `get_remote_visitor_data`
|
659
|
-
def parse_custom_data_array(visitor_code, response)
|
660
|
-
return [] unless response
|
661
|
-
|
662
|
-
raw = JSON.parse(response)
|
663
|
-
latest_record = raw['currentVisit']
|
664
|
-
if latest_record.nil?
|
665
|
-
previous_visits = raw['previousVisits']
|
666
|
-
return [] unless previous_visits
|
667
|
-
|
668
|
-
latest_record = previous_visits.first
|
669
|
-
end
|
670
|
-
events = latest_record['customDataEvents']
|
671
|
-
return [] if events.nil?
|
672
|
-
|
673
|
-
events.map { |ev| ev['data'] }.compact
|
674
|
-
.map { |jcd| CustomData.new(jcd['index'] || -1, *(jcd['valuesCountMap'] || {}).keys) }
|
675
|
-
rescue StandardError => e
|
676
|
-
log("Parsing of visitor data of '#{visitor_code}' failed: #{e}")
|
677
|
-
[]
|
678
|
-
end
|
679
|
-
|
680
708
|
def consent_provided?(visitor)
|
681
709
|
!@data_file.settings.is_consent_required || visitor&.legal_consent
|
682
710
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kameleoon/managers/remote_data/remote_visitor_data'
|
4
|
+
require 'kameleoon/types/remote_visitor_data_filter'
|
5
|
+
|
6
|
+
module Kameleoon
|
7
|
+
module Managers
|
8
|
+
module RemoteData
|
9
|
+
class RemoteDataManager
|
10
|
+
attr_reader :network_manager, :visitor_manger, :log_func
|
11
|
+
|
12
|
+
def initialize(network_manager, visitor_manger, log_func = nil)
|
13
|
+
@network_manager = network_manager
|
14
|
+
@visitor_manger = visitor_manger
|
15
|
+
@log_func = log_func
|
16
|
+
end
|
17
|
+
|
18
|
+
def get_data(key, timeout)
|
19
|
+
response = @network_manager.get_remote_data(key, timeout)
|
20
|
+
JSON.parse(response) if response
|
21
|
+
rescue StandardError => e
|
22
|
+
log("Parsing of visitor data of '#{key}' failed: #{e}")
|
23
|
+
raise
|
24
|
+
end
|
25
|
+
|
26
|
+
def get_visitor_data(visitor_code, add_data, filter = nil, is_unique_identifier = false, timeout = nil)
|
27
|
+
Utils::VisitorCode.validate(visitor_code)
|
28
|
+
filter = Kameleoon::Types::RemoteVisitorDataFilter.new unless filter.is_a?(Kameleoon::Types::RemoteVisitorDataFilter)
|
29
|
+
response = @network_manager.get_remote_visitor_data(visitor_code, filter, is_unique_identifier, timeout)
|
30
|
+
(data_to_add, data_to_return) = parse_custom_data_array(visitor_code, response)
|
31
|
+
if add_data && !data_to_add.empty?
|
32
|
+
visitor = @visitor_manger.get_or_create_visitor(visitor_code)
|
33
|
+
visitor.add_data(@log_func, *data_to_add, overwrite: false)
|
34
|
+
end
|
35
|
+
data_to_return
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# helper method used by `get_remote_visitor_data`
|
40
|
+
def parse_custom_data_array(visitor_code, response)
|
41
|
+
remote_visitor_data = Kameleoon::Managers::RemoteData::RemoteVisitorData.new(JSON.parse(response))
|
42
|
+
remote_visitor_data.mark_data_as_sent(@visitor_manger.custom_data_info)
|
43
|
+
[remote_visitor_data.collect_data_to_add, remote_visitor_data.collect_data_to_return]
|
44
|
+
rescue StandardError => e
|
45
|
+
log("Parsing of visitor data of '#{visitor_code}' failed: #{e}")
|
46
|
+
raise
|
47
|
+
end
|
48
|
+
|
49
|
+
def log(text)
|
50
|
+
@log_func&.call(text)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kameleoon/data/manager/page_view_visit'
|
4
|
+
require 'kameleoon/data/manager/assigned_variation'
|
5
|
+
require 'kameleoon/data/visitor_visits'
|
6
|
+
require 'kameleoon/data/browser'
|
7
|
+
require 'kameleoon/data/device'
|
8
|
+
require 'kameleoon/data/conversion'
|
9
|
+
require 'kameleoon/data/custom_data'
|
10
|
+
require 'kameleoon/data/geolocation'
|
11
|
+
require 'kameleoon/data/kcs_heat'
|
12
|
+
require 'kameleoon/data/page_view'
|
13
|
+
|
14
|
+
module Kameleoon
|
15
|
+
module Managers
|
16
|
+
module RemoteData
|
17
|
+
class RemoteVisitorData
|
18
|
+
attr_reader :custom_data_dict, :page_view_visits, :conversions, :experiments, :device, :browser,
|
19
|
+
:operating_system, :geolocation, :previous_visitor_visits, :kcs_heat
|
20
|
+
|
21
|
+
def initialize(hash)
|
22
|
+
current_visit = hash['currentVisit']
|
23
|
+
parse_visit(current_visit) unless current_visit.nil?
|
24
|
+
previous_visits = hash['previousVisits']
|
25
|
+
previous_visits = [] if previous_visits.nil?
|
26
|
+
|
27
|
+
if previous_visits.size.positive?
|
28
|
+
times_started = []
|
29
|
+
previous_visits.each do |visit|
|
30
|
+
times_started.push(visit['timeStarted'])
|
31
|
+
parse_visit(visit)
|
32
|
+
end
|
33
|
+
@previous_visitor_visits = VisitorVisits.new(times_started)
|
34
|
+
end
|
35
|
+
@kcs_heat = parse_kcs_heat(hash['kcs'])
|
36
|
+
end
|
37
|
+
|
38
|
+
def collect_data_to_add
|
39
|
+
data_to_add = []
|
40
|
+
data_to_add.concat(@custom_data_dict.values) unless @custom_data_dict.nil?
|
41
|
+
data_to_add.push(@previous_visitor_visits) unless @previous_visitor_visits.nil?
|
42
|
+
data_to_add.push(@kcs_heat) unless @kcs_heat.nil?
|
43
|
+
data_to_add.concat(@page_view_visits.values) unless @page_view_visits.nil?
|
44
|
+
data_to_add.concat(@experiments.values) unless @experiments.nil?
|
45
|
+
data_to_add.concat(conversions_single_objects)
|
46
|
+
end
|
47
|
+
|
48
|
+
def collect_data_to_return
|
49
|
+
data_to_return = []
|
50
|
+
data_to_return.concat(@custom_data_dict.values) unless @custom_data_dict.nil?
|
51
|
+
@page_view_visits&.each_value do |visit|
|
52
|
+
data_to_return.push(visit.page_view)
|
53
|
+
end
|
54
|
+
data_to_return.concat(conversions_single_objects)
|
55
|
+
end
|
56
|
+
|
57
|
+
def mark_data_as_sent(custom_data_info)
|
58
|
+
@custom_data_dict&.each_value do |data|
|
59
|
+
data.mark_as_sent unless custom_data_info.visitor_scope?(data.id)
|
60
|
+
end
|
61
|
+
@experiments&.each_value(&:mark_as_sent)
|
62
|
+
@page_view_visits&.each_value do |visit|
|
63
|
+
visit.page_view.mark_as_sent
|
64
|
+
end
|
65
|
+
conversions_single_objects.each(&:mark_as_sent)
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def parse_visit(hash)
|
71
|
+
custom_data_events = hash['customDataEvents']
|
72
|
+
parse_custom_data(custom_data_events) if custom_data_events != nil && custom_data_events.size.positive?
|
73
|
+
page_events = hash['pageEvents']
|
74
|
+
parse_pages(page_events) if page_events != nil && page_events.size.positive?
|
75
|
+
experiment_events = hash['experimentEvents']
|
76
|
+
parse_experiments(experiment_events) if experiment_events != nil && experiment_events.size.positive?
|
77
|
+
conversion_events = hash['conversionEvents']
|
78
|
+
parse_conversions(conversion_events) if conversion_events != nil && conversion_events.size.positive?
|
79
|
+
geolocation_events = hash['geolocationEvents']
|
80
|
+
@geolocation = parse_geolocation(geolocation_events) if @geolocation.nil? && geolocation_events != nil && geolocation_events.size.positive?
|
81
|
+
static_data_events = hash['staticDataEvent']
|
82
|
+
parse_static_data(static_data_events) if static_data_events != nil
|
83
|
+
end
|
84
|
+
|
85
|
+
def parse_custom_data(custom_data_events)
|
86
|
+
@custom_data_dict = {} if @custom_data_dict.nil?
|
87
|
+
custom_data_events.reverse_each do |custom_data_event|
|
88
|
+
data = custom_data_event['data']
|
89
|
+
id = data['index']
|
90
|
+
id = -1 if id.nil?
|
91
|
+
@custom_data_dict[id] = CustomData.new(id, *data['valuesCountMap'].keys) unless @custom_data_dict.include?(id)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def parse_pages(page_events)
|
96
|
+
@page_view_visits = {} if @page_view_visits.nil?
|
97
|
+
page_events.reverse_each do |page_event|
|
98
|
+
href = page_event['data']['href']
|
99
|
+
page_view_visit = @page_view_visits[href]
|
100
|
+
if page_view_visit.nil?
|
101
|
+
page_view = PageView.new(href, page_event['data']['title'])
|
102
|
+
@page_view_visits[href] = DataManager::PageViewVisit.new(page_view, 1, page_event['time'])
|
103
|
+
else
|
104
|
+
page_view_visit.increase_page_visits
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def parse_experiments(experiment_events)
|
110
|
+
@experiments = {} if @experiments.nil?
|
111
|
+
experiment_events.reverse_each do |experiment_event|
|
112
|
+
id = experiment_event['data']['id']
|
113
|
+
variation = @experiments[id]
|
114
|
+
if variation.nil?
|
115
|
+
variation = DataManager::AssignedVariation.new(
|
116
|
+
id, experiment_event['data']['variationId'],
|
117
|
+
Configuration::RuleType::UNKNOWN, assignment_time: experiment_event['time']
|
118
|
+
)
|
119
|
+
@experiments[id] = variation
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def parse_conversions(conversion_events)
|
125
|
+
@conversions = [] if @conversions.nil?
|
126
|
+
conversion_events.each do |conversion_event|
|
127
|
+
data = conversion_event['data']
|
128
|
+
conversion = Conversion.new(data['goalId'], data['revenue'], data['negative'])
|
129
|
+
@conversions.push(conversion)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def parse_geolocation(geolocation_events)
|
134
|
+
data = geolocation_events[geolocation_events.size - 1]['data']
|
135
|
+
Geolocation.new(data['country'], data['region'], data['city'])
|
136
|
+
end
|
137
|
+
|
138
|
+
def parse_static_data(static_data_event)
|
139
|
+
return if @device != nil && @browser != nil && @operating_system != nil
|
140
|
+
|
141
|
+
data = static_data_event['data']
|
142
|
+
if @device.nil?
|
143
|
+
device_type = data['deviceType']
|
144
|
+
@device = Device.new(device_type) unless device_type.nil?
|
145
|
+
end
|
146
|
+
if @browser.nil?
|
147
|
+
browser_type = BrowserType.from_name(data['browser'])
|
148
|
+
@browser = Browser.new(browser_type, data['browserVersion']) unless browser_type.nil?
|
149
|
+
end
|
150
|
+
if @operating_system.nil?
|
151
|
+
operating_system_type = OperatingSystemType.from_name(data['os'])
|
152
|
+
@operating_system = OperatingSystem.new(operating_system_type) unless operating_system_type.nil?
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def conversions_single_objects
|
157
|
+
objects = []
|
158
|
+
objects += @conversions unless @conversions.nil?
|
159
|
+
objects.push(@device) unless @device.nil?
|
160
|
+
objects.push(@browser) unless @browser.nil?
|
161
|
+
objects.push(@operating_system) unless @operating_system.nil?
|
162
|
+
objects.push(@geolocation) unless @geolocation.nil?
|
163
|
+
objects
|
164
|
+
end
|
165
|
+
|
166
|
+
def parse_kcs_heat(kcs)
|
167
|
+
return nil if kcs.nil?
|
168
|
+
|
169
|
+
value_map = {}
|
170
|
+
kcs.each do |str_key_moment_id, goal_scores|
|
171
|
+
next unless str_key_moment_id.is_a?(String) && goal_scores.is_a?(Hash)
|
172
|
+
|
173
|
+
goal_score_map = {}
|
174
|
+
goal_scores.each do |str_goal_id, score|
|
175
|
+
next unless str_goal_id.is_a?(String) && (score.is_a?(Float) || score.is_a?(Integer))
|
176
|
+
|
177
|
+
goal_id = str_goal_id.to_i
|
178
|
+
goal_score_map[goal_id] = score
|
179
|
+
end
|
180
|
+
key_moment_id = str_key_moment_id.to_i
|
181
|
+
value_map[key_moment_id] = goal_score_map
|
182
|
+
end
|
183
|
+
KcsHeat.new(value_map)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
@@ -19,7 +19,9 @@ module Kameleoon
|
|
19
19
|
Utils::VisitorCode.validate(visitor_code)
|
20
20
|
remote_data_key = warehouse_key.nil? || warehouse_key.empty? ? visitor_code : warehouse_key
|
21
21
|
response = @network_manager.get_remote_data(remote_data_key, timeout)
|
22
|
-
|
22
|
+
return nil unless response.is_a?(String)
|
23
|
+
|
24
|
+
remote_data = JSON.parse(response)
|
23
25
|
warehouse_audiences = remote_data.is_a?(Hash) ? remote_data[WAREHOUSE_AUDIENCES_FIELD_NAME] : nil
|
24
26
|
data_values = warehouse_audiences.is_a?(Hash) ? warehouse_audiences.keys : []
|
25
27
|
warehouse_audiences_data = CustomData.new(custom_data_index, *data_values)
|