kameleoon-client-ruby 3.1.1 → 3.2.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/custom_data_info.rb +43 -0
- data/lib/kameleoon/configuration/data_file.rb +30 -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/manager/assigned_variation.rb +2 -1
- data/lib/kameleoon/data/manager/page_view_visit.rb +16 -2
- data/lib/kameleoon/data/manager/visitor.rb +64 -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 +13 -0
- data/lib/kameleoon/kameleoon_client.rb +75 -94
- data/lib/kameleoon/managers/remote_data/remote_data_manager.rb +57 -0
- data/lib/kameleoon/managers/remote_data/remote_visitor_data.rb +162 -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 +15 -12
- data/lib/kameleoon/targeting/condition.rb +22 -2
- data/lib/kameleoon/targeting/condition_factory.rb +36 -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/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 +63 -0
- data/lib/kameleoon/targeting/conditions/time_elapsed_since_visit_condition.rb +30 -0
- data/lib/kameleoon/targeting/conditions/visit_number_today_condition.rb +28 -0
- data/lib/kameleoon/targeting/conditions/visit_number_total_condition.rb +21 -0
- data/lib/kameleoon/targeting/conditions/visitor_new_return_condition.rb +33 -0
- data/lib/kameleoon/targeting/targeting_manager.rb +66 -0
- data/lib/kameleoon/types/remote_visitor_data_filter.rb +24 -0
- data/lib/kameleoon/utils.rb +1 -0
- data/lib/kameleoon/version.rb +1 -1
- metadata +24 -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
|
##
|
@@ -342,6 +357,8 @@ module Kameleoon
|
|
342
357
|
Utils::VisitorCode.validate(visitor_code)
|
343
358
|
list_keys = []
|
344
359
|
@data_file.feature_flags.each do |feature_key, feature_flag|
|
360
|
+
next unless feature_flag.environment_enabled
|
361
|
+
|
345
362
|
variation, rule, = _calculate_variation_key_for_feature(visitor_code, feature_flag)
|
346
363
|
variation_key = _get_variation_key(variation, rule, feature_flag)
|
347
364
|
list_keys.push(feature_key) if variation_key != Kameleoon::Configuration::VariationType::VARIATION_OFF
|
@@ -471,16 +488,21 @@ module Kameleoon
|
|
471
488
|
return false unless response
|
472
489
|
|
473
490
|
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
|
-
|
491
|
+
@data_file = Configuration::DataFile.new(@config.environment, method(:log)).init(configuration)
|
492
|
+
apply_new_configuration(@data_file)
|
479
493
|
call_update_handler_if_needed(!time_stamp.nil?)
|
480
494
|
log "Feature flags are fetched: #{response.inspect}"
|
481
495
|
true
|
482
496
|
end
|
483
497
|
|
498
|
+
def apply_new_configuration(data_file)
|
499
|
+
@cookie_manager.consent_required =
|
500
|
+
data_file.settings.is_consent_required && !data_file.has_any_targeted_delivery_rule
|
501
|
+
@network_manager.url_provider.apply_data_api_domain(data_file.settings.data_api_domain)
|
502
|
+
@targeting_manager = Kameleoon::Targeting::TargetingManager.new(@visitor_manager, data_file)
|
503
|
+
@visitor_manager.custom_data_info = data_file.custom_data_info
|
504
|
+
end
|
505
|
+
|
484
506
|
##
|
485
507
|
# Call the handler when configuraiton was updated with new time stamp.
|
486
508
|
#
|
@@ -513,47 +535,12 @@ module Kameleoon
|
|
513
535
|
# end
|
514
536
|
|
515
537
|
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
|
538
|
+
@targeting_manager.check_targeting(visitor_code, campaign_id, exp_ff_rule)
|
551
539
|
end
|
552
540
|
|
553
541
|
##
|
554
542
|
# 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)
|
543
|
+
def _get_feature_variation_key(visitor_code, feature_key, is_unique_identifier = false)
|
557
544
|
feature_flag = @data_file.get_feature_flag(feature_key)
|
558
545
|
variation, rule = _calculate_variation_key_for_feature(visitor_code, feature_flag)
|
559
546
|
variation_key = _get_variation_key(variation, rule, feature_flag)
|
@@ -567,7 +554,7 @@ module Kameleoon
|
|
567
554
|
visitor.assign_variation(as_variation)
|
568
555
|
end
|
569
556
|
end
|
570
|
-
|
557
|
+
flush(visitor_code, is_unique_identifier: is_unique_identifier)
|
571
558
|
[feature_flag, variation_key]
|
572
559
|
end
|
573
560
|
|
@@ -577,16 +564,19 @@ module Kameleoon
|
|
577
564
|
# no rules -> return default_variation_key
|
578
565
|
feature_flag.rules.each do |rule|
|
579
566
|
# check if visitor is targeted for rule, else next rule
|
580
|
-
next unless check_targeting(visitor_code,
|
567
|
+
next unless check_targeting(visitor_code, rule.experiment_id, rule)
|
581
568
|
|
569
|
+
vis = @visitor_manager.get_visitor(visitor_code)
|
570
|
+
# use mappingIdentifier instead of visitorCode if it was set up
|
571
|
+
code_for_hash = vis&.mapping_identifier || visitor_code
|
582
572
|
# uses for rule exposition
|
583
|
-
hash_rule = Utils::HashDouble.obtain_rule(
|
573
|
+
hash_rule = Utils::HashDouble.obtain_rule(code_for_hash, rule.id, rule.respool_time)
|
584
574
|
# check main expostion for rule with hashRule
|
585
575
|
if hash_rule <= rule.exposition
|
586
|
-
return [rule.
|
576
|
+
return [rule.first_variation, rule] if rule.targeted_delivery_type?
|
587
577
|
|
588
578
|
# uses for variation's expositions
|
589
|
-
hash_variation = Utils::HashDouble.obtain_rule(
|
579
|
+
hash_variation = Utils::HashDouble.obtain_rule(code_for_hash, rule.experiment_id, rule.respool_time)
|
590
580
|
# get variation key with new hashVariation
|
591
581
|
variation = rule.get_variation(hash_variation)
|
592
582
|
return [variation, rule] unless variation.nil?
|
@@ -607,11 +597,12 @@ module Kameleoon
|
|
607
597
|
|
608
598
|
##
|
609
599
|
# helper method for sending tracking requests for new FF
|
610
|
-
def _send_tracking_request(visitor_code, visitor = nil, force_request = true)
|
600
|
+
def _send_tracking_request(visitor_code, visitor = nil, force_request = true, is_unique_identifier = false)
|
611
601
|
if visitor.nil?
|
612
602
|
visitor = @visitor_manager.get_visitor(visitor_code)
|
613
603
|
return if visitor.nil? && @data_file.settings.is_consent_required
|
614
604
|
end
|
605
|
+
use_mapping_value, visitor = create_anonymous_if_required(visitor_code, visitor, is_unique_identifier)
|
615
606
|
consent = consent_provided?(visitor)
|
616
607
|
user_agent = visitor&.user_agent
|
617
608
|
unsent = visitor.nil? ? [] : select_unsent_data(visitor, consent)
|
@@ -621,7 +612,20 @@ module Kameleoon
|
|
621
612
|
unsent.push(Network::ActivityEvent.new)
|
622
613
|
end
|
623
614
|
log "Start post tracking: #{unsent.inspect}"
|
624
|
-
@network_manager.send_tracking_data(visitor_code, unsent, user_agent)
|
615
|
+
@network_manager.send_tracking_data(visitor_code, unsent, user_agent, use_mapping_value)
|
616
|
+
end
|
617
|
+
|
618
|
+
def create_anonymous_if_required(visitor_code, visitor, is_unique_identifier)
|
619
|
+
use_mapping_value = is_unique_identifier && !visitor&.mapping_identifier.nil?
|
620
|
+
# need to find if anonymous visitor is behind unique (anonym doesn't exist if MappingIdentifier == null)
|
621
|
+
if is_unique_identifier && visitor&.mapping_identifier.nil?
|
622
|
+
# We haven't anonymous behind, in this case we should create "fake" anonymous with id == visitorCode
|
623
|
+
# and link it with with mapping value == visitorCode (like we do as we have real anonymous visitor)
|
624
|
+
visitor = @visitor_manager.add_data(visitor_code,
|
625
|
+
CustomData.new(@data_file.custom_data_info.mapping_identifier_index,
|
626
|
+
visitor_code))
|
627
|
+
end
|
628
|
+
[use_mapping_value, visitor]
|
625
629
|
end
|
626
630
|
|
627
631
|
def select_unsent_data(visitor, consent)
|
@@ -654,29 +658,6 @@ module Kameleoon
|
|
654
658
|
end
|
655
659
|
end
|
656
660
|
|
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
661
|
def consent_provided?(visitor)
|
681
662
|
!@data_file.settings.is_consent_required || visitor&.legal_consent
|
682
663
|
end
|
@@ -0,0 +1,57 @@
|
|
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
|
+
|
10
|
+
class RemoteDataManager
|
11
|
+
attr_reader :network_manager, :visitor_manger, :log_func
|
12
|
+
|
13
|
+
def initialize(network_manager, visitor_manger, log_func = nil)
|
14
|
+
@network_manager = network_manager
|
15
|
+
@visitor_manger = visitor_manger
|
16
|
+
@log_func = log_func
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_data(key, timeout)
|
20
|
+
response = @network_manager.get_remote_data(key, timeout)
|
21
|
+
JSON.parse(response) if response
|
22
|
+
rescue StandardError => e
|
23
|
+
log("Parsing of visitor data of '#{key}' failed: #{e}")
|
24
|
+
raise
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_visitor_data(visitor_code, add_data, filter = nil, is_unique_identifier = false, timeout = nil)
|
28
|
+
Utils::VisitorCode.validate(visitor_code)
|
29
|
+
filter = Kameleoon::Types::RemoteVisitorDataFilter.new unless filter.is_a?(Kameleoon::Types::RemoteVisitorDataFilter)
|
30
|
+
response = @network_manager.get_remote_visitor_data(visitor_code, filter, is_unique_identifier, timeout)
|
31
|
+
(data_to_add, data_to_return) = parse_custom_data_array(visitor_code, response)
|
32
|
+
if add_data && !data_to_add.empty?
|
33
|
+
visitor = @visitor_manger.get_or_create_visitor(visitor_code)
|
34
|
+
visitor.add_data(@log_func, *data_to_add, overwrite: false)
|
35
|
+
end
|
36
|
+
data_to_return
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
##
|
41
|
+
# helper method used by `get_remote_visitor_data`
|
42
|
+
def parse_custom_data_array(visitor_code, response)
|
43
|
+
remote_visitor_data = Kameleoon::Managers::RemoteData::RemoteVisitorData.new(JSON.parse(response))
|
44
|
+
remote_visitor_data.mark_data_as_sent(@visitor_manger.custom_data_info)
|
45
|
+
[remote_visitor_data.collect_data_to_add, remote_visitor_data.collect_data_to_return]
|
46
|
+
rescue StandardError => e
|
47
|
+
log("Parsing of visitor data of '#{visitor_code}' failed: #{e}")
|
48
|
+
raise
|
49
|
+
end
|
50
|
+
|
51
|
+
def log(text)
|
52
|
+
@log_func&.call(text)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,162 @@
|
|
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/page_view'
|
12
|
+
|
13
|
+
module Kameleoon
|
14
|
+
module Managers
|
15
|
+
module RemoteData
|
16
|
+
class RemoteVisitorData
|
17
|
+
attr_reader :custom_data_dict, :page_view_visits, :conversions, :experiments, :device, :browser, :operating_system, :geolocation, :previous_visitor_visits
|
18
|
+
|
19
|
+
def initialize(hash)
|
20
|
+
current_visit = hash['currentVisit']
|
21
|
+
parse_visit(current_visit) unless current_visit.nil?
|
22
|
+
previous_visits = hash['previousVisits']
|
23
|
+
previous_visits = [] if previous_visits.nil?
|
24
|
+
|
25
|
+
if previous_visits.size.positive?
|
26
|
+
times_started = []
|
27
|
+
previous_visits.each do |visit|
|
28
|
+
times_started.push(visit['timeStarted'])
|
29
|
+
parse_visit(visit)
|
30
|
+
end
|
31
|
+
@previous_visitor_visits = VisitorVisits.new(times_started)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def collect_data_to_add
|
36
|
+
data_to_add = []
|
37
|
+
data_to_add.concat(@custom_data_dict.values) unless @custom_data_dict.nil?
|
38
|
+
data_to_add.push(@previous_visitor_visits) unless @previous_visitor_visits.nil?
|
39
|
+
data_to_add.concat(@page_view_visits.values) unless @page_view_visits.nil?
|
40
|
+
data_to_add.concat(@experiments.values) unless @experiments.nil?
|
41
|
+
data_to_add.concat(conversions_single_objects)
|
42
|
+
end
|
43
|
+
|
44
|
+
def collect_data_to_return
|
45
|
+
data_to_return = []
|
46
|
+
data_to_return.concat(@custom_data_dict.values) unless @custom_data_dict.nil?
|
47
|
+
@page_view_visits&.each_value do |visit|
|
48
|
+
data_to_return.push(visit.page_view)
|
49
|
+
end
|
50
|
+
data_to_return.concat(conversions_single_objects)
|
51
|
+
end
|
52
|
+
|
53
|
+
def mark_data_as_sent(custom_data_info)
|
54
|
+
@custom_data_dict&.each_value do |data|
|
55
|
+
data.mark_as_sent unless custom_data_info.visitor_scope?(data.id)
|
56
|
+
end
|
57
|
+
@experiments&.each_value(&:mark_as_sent)
|
58
|
+
@page_view_visits&.each_value do |visit|
|
59
|
+
visit.page_view.mark_as_sent
|
60
|
+
end
|
61
|
+
conversions_single_objects.each(&:mark_as_sent)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def parse_visit(hash)
|
67
|
+
custom_data_events = hash['customDataEvents']
|
68
|
+
parse_custom_data(custom_data_events) if custom_data_events != nil && custom_data_events.size.positive?
|
69
|
+
page_events = hash['pageEvents']
|
70
|
+
parse_pages(page_events) if page_events != nil && page_events.size.positive?
|
71
|
+
experiment_events = hash['experimentEvents']
|
72
|
+
parse_experiments(experiment_events) if experiment_events != nil && experiment_events.size.positive?
|
73
|
+
conversion_events = hash['conversionEvents']
|
74
|
+
parse_conversions(conversion_events) if conversion_events != nil && conversion_events.size.positive?
|
75
|
+
geolocation_events = hash['geolocationEvents']
|
76
|
+
@geolocation = parse_geolocation(geolocation_events) if @geolocation.nil? && geolocation_events != nil && geolocation_events.size.positive?
|
77
|
+
static_data_events = hash['staticDataEvent']
|
78
|
+
parse_static_data(static_data_events) if static_data_events != nil
|
79
|
+
end
|
80
|
+
|
81
|
+
def parse_custom_data(custom_data_events)
|
82
|
+
@custom_data_dict = {} if @custom_data_dict.nil?
|
83
|
+
custom_data_events.reverse_each do |custom_data_event|
|
84
|
+
data = custom_data_event['data']
|
85
|
+
id = data['index']
|
86
|
+
id = -1 if id.nil?
|
87
|
+
@custom_data_dict[id] = CustomData.new(id, *data['valuesCountMap'].keys) unless @custom_data_dict.include?(id)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def parse_pages(page_events)
|
92
|
+
@page_view_visits = {} if @page_view_visits.nil?
|
93
|
+
page_events.reverse_each do |page_event|
|
94
|
+
href = page_event['data']['href']
|
95
|
+
page_view_visit = @page_view_visits[href]
|
96
|
+
if page_view_visit.nil?
|
97
|
+
page_view = PageView.new(href, page_event['data']['title'])
|
98
|
+
@page_view_visits[href] = Kameleoon::DataManager::PageViewVisit.new(page_view, 1, page_event['time'])
|
99
|
+
else
|
100
|
+
page_view_visit.increase_page_visits
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def parse_experiments(experiment_events)
|
106
|
+
@experiments = {} if @experiments.nil?
|
107
|
+
experiment_events.reverse_each do |experiment_event|
|
108
|
+
id = experiment_event['data']['id']
|
109
|
+
variation = @experiments[id]
|
110
|
+
if variation.nil?
|
111
|
+
variation = Kameleoon::DataManager::AssignedVariation.new(id, experiment_event['data']['variationId'], Kameleoon::Configuration::RuleType::UNKNOWN, assignment_time: experiment_event['time'])
|
112
|
+
@experiments[id] = variation
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def parse_conversions(conversion_events)
|
118
|
+
@conversions = [] if @conversions.nil?
|
119
|
+
conversion_events.each do |conversion_event|
|
120
|
+
data = conversion_event['data']
|
121
|
+
conversion = Conversion.new(data['goalId'], data['revenue'], data['negative'])
|
122
|
+
@conversions.push(conversion)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def parse_geolocation(geolocation_events)
|
127
|
+
data = geolocation_events[geolocation_events.size - 1]['data']
|
128
|
+
Geolocation.new(data['country'], data['region'], data['city'])
|
129
|
+
end
|
130
|
+
|
131
|
+
def parse_static_data(static_data_event)
|
132
|
+
return if @device != nil && @browser != nil && @operating_system != nil
|
133
|
+
data = static_data_event['data']
|
134
|
+
if @device == nil
|
135
|
+
device_type = data['deviceType']
|
136
|
+
@device = Device.new(device_type) if device_type != nil
|
137
|
+
end
|
138
|
+
if @browser == nil
|
139
|
+
browser_type = Kameleoon::BrowserType.from_name(data['browser'])
|
140
|
+
@browser = Browser.new(browser_type, data['browserVersion']) if browser_type != nil
|
141
|
+
end
|
142
|
+
if @operating_system == nil
|
143
|
+
operating_system_type = Kameleoon::OperatingSystemType.from_name(data['os'])
|
144
|
+
@operating_system = OperatingSystem.new(operating_system_type) if operating_system_type != nil
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def conversions_single_objects
|
149
|
+
objects = []
|
150
|
+
unless @conversions.nil?
|
151
|
+
objects += @conversions
|
152
|
+
end
|
153
|
+
objects.push(@device) unless @device.nil?
|
154
|
+
objects.push(@browser) unless @browser.nil?
|
155
|
+
objects.push(@operating_system) unless @operating_system.nil?
|
156
|
+
objects.push(@geolocation) unless @geolocation.nil?
|
157
|
+
objects
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
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)
|
@@ -20,12 +20,12 @@ module Kameleoon
|
|
20
20
|
@log_func = log_func
|
21
21
|
end
|
22
22
|
|
23
|
-
def get_token
|
23
|
+
def get_token(timeout = nil)
|
24
24
|
now = Time.new.to_i
|
25
25
|
token = @cached_token
|
26
|
-
return
|
26
|
+
return call_fetch_token(timeout) if token.nil? || token.expired?(now)
|
27
27
|
|
28
|
-
|
28
|
+
run_fetch_token if !@fetching && token.obsolete?(now)
|
29
29
|
token.value
|
30
30
|
end
|
31
31
|
|
@@ -35,9 +35,26 @@ module Kameleoon
|
|
35
35
|
|
36
36
|
private
|
37
37
|
|
38
|
-
def
|
38
|
+
def call_fetch_token(timeout)
|
39
39
|
@fetching = true
|
40
|
-
|
40
|
+
fetch_token(timeout)
|
41
|
+
rescue StandardError => e
|
42
|
+
@fetching = false
|
43
|
+
@log_func&.call("Failed to call access token fetching: #{e}")
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def run_fetch_token
|
48
|
+
# setting `@fetching` here to reduce the number of requests until new thread started
|
49
|
+
@fetching = true
|
50
|
+
Thread.new { fetch_token }
|
51
|
+
rescue StandardError => e
|
52
|
+
@fetching = false
|
53
|
+
@log_func&.call("Failed to run access token fetching: #{e}")
|
54
|
+
end
|
55
|
+
|
56
|
+
def fetch_token(timeout = nil)
|
57
|
+
response_content = @network_manager.fetch_access_jwtoken(@client_id, @client_secret, timeout)
|
41
58
|
unless response_content
|
42
59
|
@log_func&.call('Failed to fetch access JWT')
|
43
60
|
return nil
|