kameleoon-client-ruby 3.1.0 → 3.2.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 +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/net_provider.rb +2 -2
- data/lib/kameleoon/network/network_manager.rb +15 -17
- 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
|
@@ -15,9 +15,9 @@ module Kameleoon
|
|
15
15
|
private
|
16
16
|
|
17
17
|
def collect_headers(request)
|
18
|
-
headers = request.
|
18
|
+
headers = { 'Content-Type' => request.content_type }
|
19
|
+
headers.merge!(request.extra_headers) unless request.extra_headers.nil?
|
19
20
|
headers['Authorization'] = "Bearer #{request.access_token}" unless request.access_token.nil?
|
20
|
-
headers['Content-Type'] = request.content_type
|
21
21
|
headers['User-Agent'] = request.user_agent unless request.user_agent.nil?
|
22
22
|
headers
|
23
23
|
end
|