kameleoon-client-ruby 2.1.0 → 2.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/client.rb +105 -151
- data/lib/kameleoon/configuration/rule.rb +1 -1
- data/lib/kameleoon/data/browser.rb +39 -0
- data/lib/kameleoon/data/conversion.rb +33 -0
- data/lib/kameleoon/data/custom_data.rb +60 -0
- data/lib/kameleoon/data/data.rb +38 -0
- data/lib/kameleoon/data/device.rb +31 -0
- data/lib/kameleoon/data/page_view.rb +34 -0
- data/lib/kameleoon/data/user_agent.rb +14 -0
- data/lib/kameleoon/network/activity_event.rb +31 -0
- data/lib/kameleoon/network/content_type.rb +11 -0
- data/lib/kameleoon/network/experiment_event.rb +35 -0
- data/lib/kameleoon/network/method.rb +10 -0
- data/lib/kameleoon/network/net_provider.rb +91 -0
- data/lib/kameleoon/network/network_manager.rb +114 -0
- data/lib/kameleoon/network/request.rb +20 -0
- data/lib/kameleoon/network/response.rb +30 -0
- data/lib/kameleoon/network/uri_helper.rb +37 -0
- data/lib/kameleoon/network/url_provider.rb +71 -0
- data/lib/kameleoon/targeting/condition.rb +40 -11
- data/lib/kameleoon/targeting/condition_factory.rb +35 -12
- data/lib/kameleoon/targeting/conditions/browser_condition.rb +71 -0
- data/lib/kameleoon/targeting/conditions/conversion_condition.rb +21 -0
- data/lib/kameleoon/targeting/conditions/custom_datum.rb +60 -65
- data/lib/kameleoon/targeting/conditions/device_condition.rb +21 -0
- data/lib/kameleoon/targeting/conditions/exclusive_experiment.rb +0 -12
- data/lib/kameleoon/targeting/conditions/page_title_condition.rb +21 -0
- data/lib/kameleoon/targeting/conditions/page_url_condition.rb +21 -0
- data/lib/kameleoon/targeting/conditions/sdk_language_condition.rb +65 -0
- data/lib/kameleoon/targeting/conditions/string_value_condition.rb +40 -0
- data/lib/kameleoon/targeting/conditions/target_experiment.rb +4 -8
- data/lib/kameleoon/targeting/conditions/unknown_condition.rb +15 -0
- data/lib/kameleoon/targeting/conditions/visitor_code_condition.rb +16 -0
- data/lib/kameleoon/targeting/models.rb +0 -24
- data/lib/kameleoon/utils.rb +1 -1
- data/lib/kameleoon/version.rb +28 -1
- metadata +28 -4
- data/lib/kameleoon/data.rb +0 -175
- data/lib/kameleoon/request.rb +0 -90
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c3ada1f2651143c67ac458a8adab629e1d6ad17e95a204809a0a2b44f0ee3a28
|
4
|
+
data.tar.gz: 306fab877c00913cc4b13cecfdf9e773c2bb735be110747df37d509b537d2e01
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 38ff8932ce2f9a0e9d72644ae47de0d0f34434943f33328f0459607e34769c83e64ff6325989f9722cc8cb202d440d5d8891dea073179d269e01d6b49fa361f9
|
7
|
+
data.tar.gz: b3ee0f99356eac881b01970269cde022435872c270268ffc6ec8c0dc0d31abe3816914a497edb15e744810f60190165c84424f4799876ff56d1e24e1590d56f8
|
data/lib/kameleoon/client.rb
CHANGED
@@ -1,12 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'kameleoon/targeting/models'
|
4
|
-
require 'kameleoon/request'
|
5
4
|
require 'kameleoon/exceptions'
|
6
5
|
require 'kameleoon/cookie'
|
6
|
+
require 'kameleoon/data/custom_data'
|
7
|
+
require 'kameleoon/data/user_agent'
|
7
8
|
require 'kameleoon/configuration/feature_flag'
|
8
9
|
require 'kameleoon/configuration/variation'
|
9
10
|
require 'kameleoon/configuration/settings'
|
11
|
+
require 'kameleoon/network/activity_event'
|
12
|
+
require 'kameleoon/network/experiment_event'
|
13
|
+
require 'kameleoon/network/url_provider'
|
14
|
+
require 'kameleoon/network/network_manager'
|
10
15
|
require 'kameleoon/real_time/real_time_configuration_service'
|
11
16
|
require 'kameleoon/storage/variation_storage'
|
12
17
|
require 'kameleoon/hybrid/manager'
|
@@ -26,7 +31,6 @@ module Kameleoon
|
|
26
31
|
# Client for Kameleoon
|
27
32
|
#
|
28
33
|
class Client
|
29
|
-
include Request
|
30
34
|
include Cookie
|
31
35
|
include Exception
|
32
36
|
|
@@ -39,9 +43,6 @@ module Kameleoon
|
|
39
43
|
@default_timeout = config['default_timeout'] || default_timeout # in ms
|
40
44
|
refresh_interval = config['actions_configuration_refresh_interval']
|
41
45
|
@interval = refresh_interval.nil? ? interval : "#{refresh_interval}m"
|
42
|
-
@tracking_url = config['tracking_url'] || API_SSX_URL
|
43
|
-
@api_data_url = 'https://api-data.kameleoon.com'
|
44
|
-
@events_url = 'https://events.kameleoon.com:8110/'
|
45
46
|
@real_time_configuration_service = nil
|
46
47
|
@update_configuration_handler = nil
|
47
48
|
@fetch_configuration_update_job = nil
|
@@ -62,6 +63,12 @@ module Kameleoon
|
|
62
63
|
Kameleoon::Storage::CacheFactoryImpl.new,
|
63
64
|
method(:log)
|
64
65
|
)
|
66
|
+
@network_manager = Network::NetworkManager.new(
|
67
|
+
@environment,
|
68
|
+
@default_timeout,
|
69
|
+
Network::UrlProvider.new(@site_code, Network::UrlProvider::DEFAULT_DATA_API_URL),
|
70
|
+
method(:log)
|
71
|
+
)
|
65
72
|
end
|
66
73
|
|
67
74
|
##
|
@@ -129,22 +136,22 @@ module Kameleoon
|
|
129
136
|
"Experiment #{experiment_id} is not found"
|
130
137
|
end
|
131
138
|
check_site_code_enable(experiment)
|
132
|
-
|
139
|
+
targeted = check_targeting(visitor_code, experiment_id, experiment)
|
140
|
+
if targeted
|
133
141
|
# saved_variation = get_valid_saved_variation(visitor_code, experiment)
|
134
142
|
variation_id = calculate_variation_for_experiment(visitor_code, experiment)
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
else
|
140
|
-
track_experiment(visitor_code, experiment_id, REFERENCE, true)
|
141
|
-
raise Exception::NotAllocated.new(visitor_code),
|
142
|
-
"Experiment #{experiment_id} is not active for visitor #{visitor_code}"
|
143
|
-
end
|
144
|
-
else
|
143
|
+
save_variation(visitor_code, experiment_id, variation_id)
|
144
|
+
end
|
145
|
+
_send_tracking_request(visitor_code, experiment_id, variation_id)
|
146
|
+
unless targeted
|
145
147
|
raise Exception::NotTargeted.new(visitor_code),
|
146
148
|
"Experiment #{experiment_id} is not targeted for visitor #{visitor_code}"
|
147
149
|
end
|
150
|
+
if variation_id.nil?
|
151
|
+
raise Exception::NotAllocated.new(visitor_code),
|
152
|
+
"Experiment #{experiment_id} is not active for visitor #{visitor_code}"
|
153
|
+
end
|
154
|
+
variation_id
|
148
155
|
end
|
149
156
|
|
150
157
|
##
|
@@ -208,7 +215,7 @@ module Kameleoon
|
|
208
215
|
def flush(visitor_code = nil)
|
209
216
|
check_visitor_code(visitor_code) unless visitor_code.nil?
|
210
217
|
if !visitor_code.nil?
|
211
|
-
|
218
|
+
_send_tracking_request(visitor_code)
|
212
219
|
else
|
213
220
|
@data.select { |_, values| values.any? { |data| !data.sent } }.each_key { |key| flush(key) }
|
214
221
|
end
|
@@ -265,7 +272,7 @@ module Kameleoon
|
|
265
272
|
# associated targeting segment conditions were not fulfilled. He should see the reference variation
|
266
273
|
# @raise [Kameleoon::Exception::VisitorCodeNotValid] If the visitor code is empty or longer than 255 chars
|
267
274
|
#
|
268
|
-
# DEPRECATED. Please use `
|
275
|
+
# DEPRECATED. Please use `feature_active?` instead.
|
269
276
|
def activate_feature(visitor_code, feature_key)
|
270
277
|
warn '[DEPRECATION] `activate_feature` is deprecated. Please use `feature_active?` instead.'
|
271
278
|
feature_active?(visitor_code, feature_key)
|
@@ -384,7 +391,7 @@ module Kameleoon
|
|
384
391
|
end
|
385
392
|
|
386
393
|
##
|
387
|
-
# The
|
394
|
+
# The get_remote_data method allows you to retrieve data (according to a key passed as argument)
|
388
395
|
# stored on a remote Kameleoon server. Usually data will be stored on our remote
|
389
396
|
# servers via the use of our Data API.
|
390
397
|
# This method, along with the availability of our highly scalable servers for this purpose, provides a convenient
|
@@ -395,21 +402,36 @@ module Kameleoon
|
|
395
402
|
# This field is optional.
|
396
403
|
#
|
397
404
|
# @return [Hash] Hash object of the json object.
|
398
|
-
def
|
399
|
-
|
400
|
-
|
401
|
-
log "Retrieve API Data connexion: #{connexion_options.inspect}"
|
402
|
-
response = get_sync(@api_data_url + path, connexion_options)
|
403
|
-
return nil unless successful_sync?(response)
|
404
|
-
|
405
|
-
JSON.parse(response.body) unless response.nil?
|
405
|
+
def get_remote_data(key, timeout = @default_timeout)
|
406
|
+
response = @network_manager.get_remote_data(key, timeout)
|
407
|
+
JSON.parse(response) if response
|
406
408
|
end
|
407
409
|
|
408
410
|
##
|
409
411
|
# DEPRECATED. Please use `get_feature_variable` instead.
|
410
412
|
def retrieve_data_from_remote_source(key, timeout = @default_timeout)
|
411
413
|
warn '[DEPRECATION] `retrieve_data_from_remote_source` is deprecated. Please use `get_remote_date` instead.'
|
412
|
-
|
414
|
+
get_remote_data(key, timeout)
|
415
|
+
end
|
416
|
+
|
417
|
+
##
|
418
|
+
# The get_remote_visitor_data is a method for retrieving custom data for
|
419
|
+
# the latest visit of `visitor_code` from Kameleoon Data API and optionally adding it
|
420
|
+
# to the storage so that other methods could decide whether the current visitor is targeted or not.
|
421
|
+
#
|
422
|
+
# @param [String] visitor_code The visitor code for which you want to retrieve the assigned data.
|
423
|
+
# This field is mandatory.
|
424
|
+
# @param [Bool] add_data A boolean indicating whether the method should automatically add retrieved data
|
425
|
+
# for a visitor. If not specified, the default value is `True`. This field is optional.
|
426
|
+
# @param [Int] timeout Timeout for request (in milliseconds). Equals default_timeout in a config file.
|
427
|
+
# This field is optional.
|
428
|
+
#
|
429
|
+
# @return [Array] An array of data assigned to the given visitor.
|
430
|
+
def get_remote_visitor_data(visitor_code, timeout = nil, add_data: true)
|
431
|
+
response = @network_manager.get_remote_visitor_data(visitor_code, timeout)
|
432
|
+
data_array = parse_custom_data_array(visitor_code, response)
|
433
|
+
add_data(visitor_code, *data_array) if add_data && !data_array.empty?
|
434
|
+
data_array
|
413
435
|
end
|
414
436
|
|
415
437
|
##
|
@@ -490,7 +512,6 @@ module Kameleoon
|
|
490
512
|
|
491
513
|
private
|
492
514
|
|
493
|
-
API_SSX_URL = 'https://api-ssx.kameleoon.com'
|
494
515
|
REFERENCE = 0
|
495
516
|
DEFAULT_ENVIRONMENT = 'production'
|
496
517
|
CACHE_EXPIRATION_TIMEOUT = 5
|
@@ -507,7 +528,7 @@ module Kameleoon
|
|
507
528
|
def fetch_configuration_job(time_stamp = nil)
|
508
529
|
EM.synchrony do
|
509
530
|
begin
|
510
|
-
ok = obtain_configuration(
|
531
|
+
ok = obtain_configuration(time_stamp)
|
511
532
|
if !ok && @settings.real_time_update
|
512
533
|
@settings.real_time_update = false
|
513
534
|
log('Switching to polling mode due to failed fetch')
|
@@ -540,10 +561,10 @@ module Kameleoon
|
|
540
561
|
def start_real_time_configuration_service_if_needed
|
541
562
|
return unless @real_time_configuration_service.nil?
|
542
563
|
|
543
|
-
|
564
|
+
url = @network_manager.url_provider.make_real_time_url
|
544
565
|
fetch_func = proc { |real_time_event| fetch_configuration_job(real_time_event.time_stamp) }
|
545
566
|
@real_time_configuration_service =
|
546
|
-
Kameleoon::RealTime::RealTimeConfigurationService.new(
|
567
|
+
Kameleoon::RealTime::RealTimeConfigurationService.new(url, fetch_func, method(:log))
|
547
568
|
end
|
548
569
|
|
549
570
|
def stop_real_time_configuration_service_if_needed
|
@@ -577,15 +598,12 @@ module Kameleoon
|
|
577
598
|
# { 'field' => field, 'operator' => operator, 'parameters' => parameters }
|
578
599
|
# end
|
579
600
|
|
580
|
-
def obtain_configuration(
|
601
|
+
def obtain_configuration(time_stamp = nil)
|
581
602
|
log 'Fetching configuration from Client-Config service'
|
582
|
-
|
583
|
-
|
584
|
-
request_path += "&ts=#{time_stamp}" unless time_stamp.nil?
|
585
|
-
request = request_configuration(request_path)
|
586
|
-
return false unless request
|
603
|
+
response = @network_manager.fetch_configuration(time_stamp)
|
604
|
+
return false unless response
|
587
605
|
|
588
|
-
configuration = JSON.parse(
|
606
|
+
configuration = JSON.parse(response)
|
589
607
|
@experiments = Kameleoon::Configuration::Experiment.create_from_array(configuration['experiments']) ||
|
590
608
|
@experiments
|
591
609
|
@feature_flags = Kameleoon::Configuration::FeatureFlag.create_from_array(
|
@@ -593,7 +611,7 @@ module Kameleoon
|
|
593
611
|
)
|
594
612
|
@settings.update(configuration['configuration'])
|
595
613
|
call_update_handler_if_needed(!time_stamp.nil?)
|
596
|
-
log "Feature flags are fetched: #{
|
614
|
+
log "Feature flags are fetched: #{response.inspect}"
|
597
615
|
true
|
598
616
|
end
|
599
617
|
|
@@ -616,46 +634,6 @@ module Kameleoon
|
|
616
634
|
nil
|
617
635
|
end
|
618
636
|
|
619
|
-
def request_configuration(path)
|
620
|
-
request = EM::Synchrony.sync get({ path: path }, CLIENT_CONFIG_URL)
|
621
|
-
unless successful?(request)
|
622
|
-
log "Failed to fetch #{request.inspect}"
|
623
|
-
return false
|
624
|
-
end
|
625
|
-
request
|
626
|
-
end
|
627
|
-
|
628
|
-
def get_common_ssx_parameters(visitor_code)
|
629
|
-
{
|
630
|
-
nonce: Kameleoon::Utils.generate_random_string(16),
|
631
|
-
siteCode: @site_code,
|
632
|
-
visitorCode: visitor_code
|
633
|
-
}
|
634
|
-
end
|
635
|
-
|
636
|
-
def get_experiment_register_url(visitor_code, experiment_id, variation_id = nil, none_variation = false)
|
637
|
-
url = "/experimentTracking?#{URI.encode_www_form(get_common_ssx_parameters(visitor_code))}"
|
638
|
-
url += "&experimentId=#{experiment_id}"
|
639
|
-
return url if variation_id.nil?
|
640
|
-
|
641
|
-
url += "&variationId=#{variation_id}"
|
642
|
-
url += '&noneVariation=true' if none_variation
|
643
|
-
|
644
|
-
url
|
645
|
-
end
|
646
|
-
|
647
|
-
def get_data_register_url(visitor_code)
|
648
|
-
"/dataTracking?#{URI.encode_www_form(get_common_ssx_parameters(visitor_code))}"
|
649
|
-
end
|
650
|
-
|
651
|
-
def get_api_data_request_url(key)
|
652
|
-
mapKey = {
|
653
|
-
siteCode: site_code,
|
654
|
-
key: key
|
655
|
-
}
|
656
|
-
"/data?#{URI.encode_www_form(mapKey)}"
|
657
|
-
end
|
658
|
-
|
659
637
|
def find_feature_flag(feature_key)
|
660
638
|
if feature_key.is_a?(String)
|
661
639
|
feature_flag = @feature_flags.select { |ff| ff.feature_key == feature_key }.first
|
@@ -669,67 +647,6 @@ module Kameleoon
|
|
669
647
|
feature_flag
|
670
648
|
end
|
671
649
|
|
672
|
-
def track_experiment(visitor_code, experiment_id, variation_id = nil, none_variation: false)
|
673
|
-
data_not_sent = data_not_sent(visitor_code)
|
674
|
-
options = {
|
675
|
-
path: get_experiment_register_url(visitor_code, experiment_id, variation_id, none_variation),
|
676
|
-
body: (data_not_sent.map(&:obtain_full_post_text_line).join("\n") || '').encode('UTF-8'),
|
677
|
-
head: { 'Content-Type': 'text/plain' }
|
678
|
-
}
|
679
|
-
set_user_agent_to_headers(visitor_code, options[:head])
|
680
|
-
trial = 0
|
681
|
-
success = false
|
682
|
-
log "Start post tracking experiment: #{data_not_sent.inspect}"
|
683
|
-
Thread.new do
|
684
|
-
while trial < 10
|
685
|
-
log "Send Experiment Tracking #{options.inspect}"
|
686
|
-
response = post_sync(options, @tracking_url)
|
687
|
-
log "Response #{response.inspect}"
|
688
|
-
if successful_sync?(response)
|
689
|
-
data_not_sent.each { |it| it.sent = true }
|
690
|
-
success = true
|
691
|
-
break
|
692
|
-
end
|
693
|
-
trial += 1
|
694
|
-
end
|
695
|
-
if success
|
696
|
-
log "Post to experiment tracking is done after #{trial + 1} trials"
|
697
|
-
else
|
698
|
-
log "Post to experiment tracking is failed after #{trial} trials"
|
699
|
-
end
|
700
|
-
end
|
701
|
-
end
|
702
|
-
|
703
|
-
def track_data(visitor_code)
|
704
|
-
Thread.new do
|
705
|
-
trials = 0
|
706
|
-
data_not_sent = data_not_sent(visitor_code)
|
707
|
-
log "Start post tracking data: #{data_not_sent.inspect}"
|
708
|
-
while trials < 10 && !data_not_sent.empty?
|
709
|
-
options = {
|
710
|
-
path: get_data_register_url(visitor_code),
|
711
|
-
body: (data_not_sent.map(&:obtain_full_post_text_line).join("\n") || '').encode('UTF-8'),
|
712
|
-
head: { 'Content-Type': 'text/plain' }
|
713
|
-
}
|
714
|
-
set_user_agent_to_headers(visitor_code, options[:head])
|
715
|
-
log "Post tracking data for visitor_code: #{visitor_code} with options: #{options.inspect}"
|
716
|
-
response = post_sync(options, @tracking_url)
|
717
|
-
log "Response #{response.inspect}"
|
718
|
-
if successful_sync?(response)
|
719
|
-
data_not_sent.each { |it| it.sent = true }
|
720
|
-
success = true
|
721
|
-
break
|
722
|
-
end
|
723
|
-
trials += 1
|
724
|
-
end
|
725
|
-
if success
|
726
|
-
log "Post to data tracking is done after #{trials + 1} trials"
|
727
|
-
else
|
728
|
-
log "Post to data tracking is failed after #{trials} trials"
|
729
|
-
end
|
730
|
-
end
|
731
|
-
end
|
732
|
-
|
733
650
|
def check_site_code_enable(campaign)
|
734
651
|
raise Exception::SiteCodeDisabled.new(site_code), site_code unless campaign.site_enabled
|
735
652
|
end
|
@@ -770,8 +687,17 @@ module Kameleoon
|
|
770
687
|
def get_condition_data(type, visitor_code, campaign_id)
|
771
688
|
condition_data = nil
|
772
689
|
case type
|
773
|
-
when Kameleoon::Targeting::ConditionType::CUSTOM_DATUM
|
690
|
+
when Kameleoon::Targeting::ConditionType::CUSTOM_DATUM,
|
691
|
+
Kameleoon::Targeting::ConditionType::PAGE_TITLE,
|
692
|
+
Kameleoon::Targeting::ConditionType::PAGE_URL,
|
693
|
+
Kameleoon::Targeting::ConditionType::DEVICE_TYPE,
|
694
|
+
Kameleoon::Targeting::ConditionType::BROWSER,
|
695
|
+
Kameleoon::Targeting::ConditionType::CONVERSIONS
|
774
696
|
condition_data = (@data[visitor_code] || []).flatten
|
697
|
+
when Kameleoon::Targeting::ConditionType::SDK_LANGUAGE
|
698
|
+
condition_data = Kameleoon::Targeting::SdkInfo.new(Kameleoon::SDK_NAME, Kameleoon::SDK_VERSION)
|
699
|
+
when Kameleoon::Targeting::ConditionType::VISITOR_CODE
|
700
|
+
condition_data = visitor_code
|
775
701
|
when Kameleoon::Targeting::ConditionType::TARGET_EXPERIMENT
|
776
702
|
condition_data = @variation_storage.get_hash_saved_variation_id(visitor_code)
|
777
703
|
when Kameleoon::Targeting::ConditionType::EXCLUSIVE_EXPERIMENT
|
@@ -790,10 +716,11 @@ module Kameleoon
|
|
790
716
|
variation, rule = _calculate_variation_key_for_feature(visitor_code, feature_flag)
|
791
717
|
variation_key = _get_variation_key(variation, rule, feature_flag)
|
792
718
|
unless rule.nil?
|
793
|
-
|
719
|
+
experiment_id = rule.experiment_id
|
794
720
|
variation_id = variation.variation_id unless variation.nil?
|
795
|
-
|
721
|
+
save_variation(visitor_code, experiment_id, variation_id)
|
796
722
|
end
|
723
|
+
_send_tracking_request(visitor_code, experiment_id, variation_id)
|
797
724
|
[feature_flag, variation_key]
|
798
725
|
end
|
799
726
|
|
@@ -809,11 +736,12 @@ module Kameleoon
|
|
809
736
|
hash_rule = obtain_hash_double_rule(visitor_code, rule.id, rule.respool_time)
|
810
737
|
# check main expostion for rule with hashRule
|
811
738
|
if hash_rule <= rule.exposition
|
739
|
+
return [rule.variation_by_exposition[0], rule] if rule.targeted_delivery_type?
|
740
|
+
|
812
741
|
# uses for variation's expositions
|
813
742
|
hash_variation = obtain_hash_double_rule(visitor_code, rule.experiment_id, rule.respool_time)
|
814
743
|
# get variation key with new hashVariation
|
815
744
|
variation = rule.get_variation(hash_variation)
|
816
|
-
# variation_key can be nil for experiment rules only, for targeted rule will be always exist
|
817
745
|
return [variation, rule] unless variation.nil?
|
818
746
|
# if visitor is targeted for targeted rule then break cycle -> return default
|
819
747
|
elsif rule.targeted_delivery_type?
|
@@ -833,20 +761,23 @@ module Kameleoon
|
|
833
761
|
def _get_variation_key(var_by_exp, rule, feature_flag)
|
834
762
|
return var_by_exp.variation_key unless var_by_exp.nil?
|
835
763
|
|
836
|
-
return Kameleoon::Configuration::VariationType::VARIATION_OFF if !rule.nil? && rule.
|
764
|
+
return Kameleoon::Configuration::VariationType::VARIATION_OFF if !rule.nil? && rule.experimentation_type?
|
837
765
|
|
838
766
|
feature_flag.default_variation_key
|
839
767
|
end
|
840
768
|
|
841
769
|
##
|
842
770
|
# helper method for sending tracking requests for new FF
|
843
|
-
def _send_tracking_request(visitor_code, experiment_id, variation_id)
|
844
|
-
|
845
|
-
|
846
|
-
|
771
|
+
def _send_tracking_request(visitor_code, experiment_id = nil, variation_id = nil)
|
772
|
+
user_agent = @user_agents[visitor_code]&.value
|
773
|
+
data_not_sent = data_not_sent(visitor_code)
|
774
|
+
if experiment_id.nil? || variation_id.nil?
|
775
|
+
data_not_sent.append(Network::ActivityEvent.new) if data_not_sent.empty?
|
847
776
|
else
|
848
|
-
|
777
|
+
data_not_sent.append(Network::ExperimentEvent.new(experiment_id, variation_id))
|
849
778
|
end
|
779
|
+
log "Start post tracking: #{data_not_sent.inspect}"
|
780
|
+
@network_manager.send_tracking_data(visitor_code, data_not_sent, user_agent)
|
850
781
|
end
|
851
782
|
|
852
783
|
##
|
@@ -862,5 +793,28 @@ module Kameleoon
|
|
862
793
|
'Unknown type for feature variable'
|
863
794
|
end
|
864
795
|
end
|
796
|
+
|
797
|
+
##
|
798
|
+
# helper method used by `get_remote_visitor_data`
|
799
|
+
def parse_custom_data_array(visitor_code, response)
|
800
|
+
return [] unless response
|
801
|
+
|
802
|
+
raw = JSON.parse(response)
|
803
|
+
latest_record = raw['currentVisit']
|
804
|
+
if latest_record.nil?
|
805
|
+
previous_visits = raw['previousVisits']
|
806
|
+
return [] unless previous_visits
|
807
|
+
|
808
|
+
latest_record = previous_visits.first
|
809
|
+
end
|
810
|
+
events = latest_record['customDataEvents']
|
811
|
+
return [] if events.nil?
|
812
|
+
|
813
|
+
events.map { |ev| ev['data'] }.compact
|
814
|
+
.map { |jcd| CustomData.new(jcd['index'] || -1, *(jcd['valuesCountMap'] || {}).keys) }
|
815
|
+
rescue StandardError => e
|
816
|
+
log("Parsing of visitor data of '#{visitor_code}' failed: #{e}")
|
817
|
+
[]
|
818
|
+
end
|
865
819
|
end
|
866
820
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'kameleoon/network/uri_helper'
|
5
|
+
require_relative 'data'
|
6
|
+
|
7
|
+
module Kameleoon
|
8
|
+
module BrowserType
|
9
|
+
CHROME = 0
|
10
|
+
INTERNET_EXPLORER = 1
|
11
|
+
FIREFOX = 2
|
12
|
+
SAFARI = 3
|
13
|
+
OPERA = 4
|
14
|
+
OTHER = 5
|
15
|
+
end
|
16
|
+
|
17
|
+
# Represents browser data for tracking calls
|
18
|
+
class Browser < Data
|
19
|
+
attr_reader :type, :version
|
20
|
+
|
21
|
+
# @param [BrowserType] browser_type Browser type, can be: CHROME, INTERNET_EXPLORER, FIREFOX, SAFARI, OPERA, OTHER
|
22
|
+
# @param [float] version Version of browser
|
23
|
+
def initialize(browser_type, version = Float::NAN)
|
24
|
+
super(DataType::BROWSER)
|
25
|
+
@type = browser_type
|
26
|
+
@version = version
|
27
|
+
end
|
28
|
+
|
29
|
+
def obtain_full_post_text_line
|
30
|
+
params = {
|
31
|
+
eventType: 'staticData',
|
32
|
+
browserIndex: @type,
|
33
|
+
nonce: nonce
|
34
|
+
}
|
35
|
+
params[:browserVersion] = @version if @version.is_a?(Integer) || (@version.is_a?(Float) && !@version.nan?)
|
36
|
+
Kameleoon::Network::UriHelper.encode_query(params)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'kameleoon/network/uri_helper'
|
5
|
+
require_relative 'data'
|
6
|
+
|
7
|
+
module Kameleoon
|
8
|
+
# Conversion class uses for tracking conversion
|
9
|
+
class Conversion < Data
|
10
|
+
attr_reader :goal_id, :revenue, :negative
|
11
|
+
|
12
|
+
# @param [Integer] goal_id Id of the goal associated to the conversion
|
13
|
+
# @param [Float] revenue Optional field - Revenue associated to the conversion.
|
14
|
+
# @param [Boolean] negative Optional field - If the revenue is negative. By default it's positive.
|
15
|
+
def initialize(goal_id, revenue = 0.0, negative = false)
|
16
|
+
super(DataType::CONVERSION)
|
17
|
+
@goal_id = goal_id
|
18
|
+
@revenue = revenue || 0.0
|
19
|
+
@negative = negative || false
|
20
|
+
end
|
21
|
+
|
22
|
+
def obtain_full_post_text_line
|
23
|
+
params = {
|
24
|
+
eventType: 'conversion',
|
25
|
+
goalId: @goal_id,
|
26
|
+
revenue: @revenue,
|
27
|
+
negative: @negative,
|
28
|
+
nonce: nonce
|
29
|
+
}
|
30
|
+
Kameleoon::Network::UriHelper.encode_query(params)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'kameleoon/network/uri_helper'
|
5
|
+
require_relative 'data'
|
6
|
+
|
7
|
+
module Kameleoon
|
8
|
+
# Represents any custom data for targeting conditions
|
9
|
+
class CustomData < Data
|
10
|
+
attr_reader :id, :values
|
11
|
+
|
12
|
+
# @param [Integer] id Id of the custom data
|
13
|
+
# @param [String] value Value of the custom data
|
14
|
+
# @param [Array] values Array of values of the custom data
|
15
|
+
#
|
16
|
+
# @overload
|
17
|
+
# @param [Hash] hash Json value encoded in a hash.
|
18
|
+
# rubocop:disable Metrics/MethodLength
|
19
|
+
def initialize(arg0, *args)
|
20
|
+
super(DataType::CUSTOM)
|
21
|
+
if arg0.is_a?(Hash)
|
22
|
+
hash = arg0
|
23
|
+
id = hash['id']
|
24
|
+
raise Kameleoon::Exception::NotFound.new('id'), '"id" is mandatory' if id.nil?
|
25
|
+
|
26
|
+
@id = id.to_s
|
27
|
+
value = hash['value']
|
28
|
+
values = hash['values']
|
29
|
+
if values.nil? && value.nil?
|
30
|
+
raise Kameleoon::Exception::NotFound.new('value or values'), '"value" or "values" is mandatory'
|
31
|
+
end
|
32
|
+
|
33
|
+
if values.nil?
|
34
|
+
@values = [value]
|
35
|
+
else
|
36
|
+
@values = values.is_a?(Array) ? values.dup : [values]
|
37
|
+
@values.append(value) unless value.nil?
|
38
|
+
end
|
39
|
+
else
|
40
|
+
@id = arg0.to_s
|
41
|
+
@values = args
|
42
|
+
end
|
43
|
+
end
|
44
|
+
# rubocop:enable Metrics/MethodLength
|
45
|
+
|
46
|
+
def obtain_full_post_text_line
|
47
|
+
return '' if @values.empty?
|
48
|
+
|
49
|
+
str_values = JSON.generate(Hash[@values.collect { |k| [k, 1] }])
|
50
|
+
params = {
|
51
|
+
eventType: 'customData',
|
52
|
+
index: @id,
|
53
|
+
valuesCountMap: str_values,
|
54
|
+
overwrite: 'true',
|
55
|
+
nonce: nonce
|
56
|
+
}
|
57
|
+
Kameleoon::Network::UriHelper.encode_query(params)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'kameleoon/exceptions'
|
5
|
+
|
6
|
+
module Kameleoon
|
7
|
+
NONCE_LENGTH = 16
|
8
|
+
|
9
|
+
module DataType
|
10
|
+
CUSTOM = 'CUSTOM'
|
11
|
+
BROWSER = 'BROWSER'
|
12
|
+
CONVERSION = 'CONVERSION'
|
13
|
+
DEVICE = 'DEVICE'
|
14
|
+
PAGE_VIEW = 'PAGE_VIEW'
|
15
|
+
end
|
16
|
+
|
17
|
+
# Represents base class for any Kameleoon data
|
18
|
+
class Data
|
19
|
+
attr_reader :instance
|
20
|
+
attr_accessor :sent
|
21
|
+
|
22
|
+
def initialize(data_type)
|
23
|
+
@instance = data_type
|
24
|
+
@sent = false
|
25
|
+
end
|
26
|
+
|
27
|
+
def obtain_full_post_text_line
|
28
|
+
raise KameleoonError.new('ToDo: implement this method.'), 'ToDo: implement this method.'
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def nonce
|
34
|
+
@nonce = Kameleoon::Utils.generate_random_string(NONCE_LENGTH) if @nonce.nil?
|
35
|
+
@nonce
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'kameleoon/network/uri_helper'
|
5
|
+
require_relative 'data'
|
6
|
+
|
7
|
+
module Kameleoon
|
8
|
+
module DeviceType
|
9
|
+
PHONE = 'PHONE'
|
10
|
+
TABLET = 'TABLET'
|
11
|
+
DESKTOP = 'DESKTOP'
|
12
|
+
end
|
13
|
+
|
14
|
+
# Device uses for sending deviceType parameter for tracking calls
|
15
|
+
class Device < Data
|
16
|
+
attr_reader :device_type
|
17
|
+
def initialize(device_type)
|
18
|
+
super(DataType::DEVICE)
|
19
|
+
@device_type = device_type
|
20
|
+
end
|
21
|
+
|
22
|
+
def obtain_full_post_text_line
|
23
|
+
params = {
|
24
|
+
eventType: 'staticData',
|
25
|
+
deviceType: @device_type,
|
26
|
+
nonce: nonce
|
27
|
+
}
|
28
|
+
Kameleoon::Network::UriHelper.encode_query(params)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|