kameleoon-client-ruby 2.1.1 → 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 +63 -55
- data/lib/kameleoon/data/browser.rb +10 -4
- data/lib/kameleoon/data/conversion.rb +9 -2
- data/lib/kameleoon/data/custom_data.rb +9 -2
- data/lib/kameleoon/data/data.rb +5 -2
- data/lib/kameleoon/data/device.rb +7 -2
- data/lib/kameleoon/data/page_view.rb +10 -7
- data/lib/kameleoon/network/content_type.rb +11 -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 +1 -0
- data/lib/kameleoon/version.rb +1 -1
- metadata +8 -3
- data/lib/kameleoon/request.rb +0 -76
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,9 +1,9 @@
|
|
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
7
|
require 'kameleoon/data/user_agent'
|
8
8
|
require 'kameleoon/configuration/feature_flag'
|
9
9
|
require 'kameleoon/configuration/variation'
|
@@ -11,6 +11,7 @@ require 'kameleoon/configuration/settings'
|
|
11
11
|
require 'kameleoon/network/activity_event'
|
12
12
|
require 'kameleoon/network/experiment_event'
|
13
13
|
require 'kameleoon/network/url_provider'
|
14
|
+
require 'kameleoon/network/network_manager'
|
14
15
|
require 'kameleoon/real_time/real_time_configuration_service'
|
15
16
|
require 'kameleoon/storage/variation_storage'
|
16
17
|
require 'kameleoon/hybrid/manager'
|
@@ -30,7 +31,6 @@ module Kameleoon
|
|
30
31
|
# Client for Kameleoon
|
31
32
|
#
|
32
33
|
class Client
|
33
|
-
include Request
|
34
34
|
include Cookie
|
35
35
|
include Exception
|
36
36
|
|
@@ -43,8 +43,6 @@ module Kameleoon
|
|
43
43
|
@default_timeout = config['default_timeout'] || default_timeout # in ms
|
44
44
|
refresh_interval = config['actions_configuration_refresh_interval']
|
45
45
|
@interval = refresh_interval.nil? ? interval : "#{refresh_interval}m"
|
46
|
-
data_api_url = config['tracking_url'] || Network::UrlProvider::DEFAULT_DATA_API_URL
|
47
|
-
@url_provider = Network::UrlProvider.new(@site_code, data_api_url)
|
48
46
|
@real_time_configuration_service = nil
|
49
47
|
@update_configuration_handler = nil
|
50
48
|
@fetch_configuration_update_job = nil
|
@@ -65,6 +63,12 @@ module Kameleoon
|
|
65
63
|
Kameleoon::Storage::CacheFactoryImpl.new,
|
66
64
|
method(:log)
|
67
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
|
+
)
|
68
72
|
end
|
69
73
|
|
70
74
|
##
|
@@ -268,7 +272,7 @@ module Kameleoon
|
|
268
272
|
# associated targeting segment conditions were not fulfilled. He should see the reference variation
|
269
273
|
# @raise [Kameleoon::Exception::VisitorCodeNotValid] If the visitor code is empty or longer than 255 chars
|
270
274
|
#
|
271
|
-
# DEPRECATED. Please use `
|
275
|
+
# DEPRECATED. Please use `feature_active?` instead.
|
272
276
|
def activate_feature(visitor_code, feature_key)
|
273
277
|
warn '[DEPRECATION] `activate_feature` is deprecated. Please use `feature_active?` instead.'
|
274
278
|
feature_active?(visitor_code, feature_key)
|
@@ -399,13 +403,8 @@ module Kameleoon
|
|
399
403
|
#
|
400
404
|
# @return [Hash] Hash object of the json object.
|
401
405
|
def get_remote_data(key, timeout = @default_timeout)
|
402
|
-
|
403
|
-
|
404
|
-
log "Retrieve API Data connexion: #{connexion_options.inspect}"
|
405
|
-
response = get_sync(url, connexion_options)
|
406
|
-
return nil unless successful_sync?(response)
|
407
|
-
|
408
|
-
JSON.parse(response.body) unless response.nil?
|
406
|
+
response = @network_manager.get_remote_data(key, timeout)
|
407
|
+
JSON.parse(response) if response
|
409
408
|
end
|
410
409
|
|
411
410
|
##
|
@@ -415,6 +414,26 @@ module Kameleoon
|
|
415
414
|
get_remote_data(key, timeout)
|
416
415
|
end
|
417
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
|
435
|
+
end
|
436
|
+
|
418
437
|
##
|
419
438
|
# Returns a list of all experiment ids
|
420
439
|
#
|
@@ -509,7 +528,7 @@ module Kameleoon
|
|
509
528
|
def fetch_configuration_job(time_stamp = nil)
|
510
529
|
EM.synchrony do
|
511
530
|
begin
|
512
|
-
ok = obtain_configuration(
|
531
|
+
ok = obtain_configuration(time_stamp)
|
513
532
|
if !ok && @settings.real_time_update
|
514
533
|
@settings.real_time_update = false
|
515
534
|
log('Switching to polling mode due to failed fetch')
|
@@ -542,7 +561,7 @@ module Kameleoon
|
|
542
561
|
def start_real_time_configuration_service_if_needed
|
543
562
|
return unless @real_time_configuration_service.nil?
|
544
563
|
|
545
|
-
url = @url_provider.make_real_time_url
|
564
|
+
url = @network_manager.url_provider.make_real_time_url
|
546
565
|
fetch_func = proc { |real_time_event| fetch_configuration_job(real_time_event.time_stamp) }
|
547
566
|
@real_time_configuration_service =
|
548
567
|
Kameleoon::RealTime::RealTimeConfigurationService.new(url, fetch_func, method(:log))
|
@@ -579,13 +598,12 @@ module Kameleoon
|
|
579
598
|
# { 'field' => field, 'operator' => operator, 'parameters' => parameters }
|
580
599
|
# end
|
581
600
|
|
582
|
-
def obtain_configuration(
|
601
|
+
def obtain_configuration(time_stamp = nil)
|
583
602
|
log 'Fetching configuration from Client-Config service'
|
584
|
-
|
585
|
-
|
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,15 +634,6 @@ module Kameleoon
|
|
616
634
|
nil
|
617
635
|
end
|
618
636
|
|
619
|
-
def request_configuration(url)
|
620
|
-
request = EM::Synchrony.sync get({}, url)
|
621
|
-
unless successful?(request)
|
622
|
-
log "Failed to fetch #{request.inspect}"
|
623
|
-
return false
|
624
|
-
end
|
625
|
-
request
|
626
|
-
end
|
627
|
-
|
628
637
|
def find_feature_flag(feature_key)
|
629
638
|
if feature_key.is_a?(String)
|
630
639
|
feature_flag = @feature_flags.select { |ff| ff.feature_key == feature_key }.first
|
@@ -760,39 +769,15 @@ module Kameleoon
|
|
760
769
|
##
|
761
770
|
# helper method for sending tracking requests for new FF
|
762
771
|
def _send_tracking_request(visitor_code, experiment_id = nil, variation_id = nil)
|
772
|
+
user_agent = @user_agents[visitor_code]&.value
|
763
773
|
data_not_sent = data_not_sent(visitor_code)
|
764
774
|
if experiment_id.nil? || variation_id.nil?
|
765
775
|
data_not_sent.append(Network::ActivityEvent.new) if data_not_sent.empty?
|
766
776
|
else
|
767
777
|
data_not_sent.append(Network::ExperimentEvent.new(experiment_id, variation_id))
|
768
778
|
end
|
769
|
-
options = {
|
770
|
-
body: (data_not_sent.map(&:obtain_full_post_text_line).join("\n") || '').encode('UTF-8'),
|
771
|
-
head: { 'Content-Type': 'text/plain' }
|
772
|
-
}
|
773
|
-
set_user_agent_to_headers(visitor_code, options[:head])
|
774
|
-
url = @url_provider.make_tracking_url(visitor_code)
|
775
|
-
trial = 0
|
776
|
-
success = false
|
777
779
|
log "Start post tracking: #{data_not_sent.inspect}"
|
778
|
-
|
779
|
-
while trial < 10
|
780
|
-
log "Send tracking #{options.inspect}"
|
781
|
-
response = post_sync(options, url)
|
782
|
-
log "Response #{response.inspect}"
|
783
|
-
if successful_sync?(response)
|
784
|
-
data_not_sent.each { |it| it.sent = true }
|
785
|
-
success = true
|
786
|
-
break
|
787
|
-
end
|
788
|
-
trial += 1
|
789
|
-
end
|
790
|
-
if success
|
791
|
-
log "Post to tracking is done after #{trial + 1} trials"
|
792
|
-
else
|
793
|
-
log "Post to tracking is failed after #{trial} trials"
|
794
|
-
end
|
795
|
-
end
|
780
|
+
@network_manager.send_tracking_data(visitor_code, data_not_sent, user_agent)
|
796
781
|
end
|
797
782
|
|
798
783
|
##
|
@@ -808,5 +793,28 @@ module Kameleoon
|
|
808
793
|
'Unknown type for feature variable'
|
809
794
|
end
|
810
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
|
811
819
|
end
|
812
820
|
end
|
@@ -1,4 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'json'
|
4
|
+
require 'kameleoon/network/uri_helper'
|
2
5
|
require_relative 'data'
|
3
6
|
|
4
7
|
module Kameleoon
|
@@ -24,10 +27,13 @@ module Kameleoon
|
|
24
27
|
end
|
25
28
|
|
26
29
|
def obtain_full_post_text_line
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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)
|
31
37
|
end
|
32
38
|
end
|
33
39
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'json'
|
4
|
+
require 'kameleoon/network/uri_helper'
|
4
5
|
require_relative 'data'
|
5
6
|
|
6
7
|
module Kameleoon
|
@@ -19,8 +20,14 @@ module Kameleoon
|
|
19
20
|
end
|
20
21
|
|
21
22
|
def obtain_full_post_text_line
|
22
|
-
|
23
|
-
|
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)
|
24
31
|
end
|
25
32
|
end
|
26
33
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'json'
|
4
|
+
require 'kameleoon/network/uri_helper'
|
4
5
|
require_relative 'data'
|
5
6
|
|
6
7
|
module Kameleoon
|
@@ -46,8 +47,14 @@ module Kameleoon
|
|
46
47
|
return '' if @values.empty?
|
47
48
|
|
48
49
|
str_values = JSON.generate(Hash[@values.collect { |k| [k, 1] }])
|
49
|
-
|
50
|
-
|
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)
|
51
58
|
end
|
52
59
|
end
|
53
60
|
end
|
data/lib/kameleoon/data/data.rb
CHANGED
@@ -28,8 +28,11 @@ module Kameleoon
|
|
28
28
|
raise KameleoonError.new('ToDo: implement this method.'), 'ToDo: implement this method.'
|
29
29
|
end
|
30
30
|
|
31
|
-
|
32
|
-
|
31
|
+
private
|
32
|
+
|
33
|
+
def nonce
|
34
|
+
@nonce = Kameleoon::Utils.generate_random_string(NONCE_LENGTH) if @nonce.nil?
|
35
|
+
@nonce
|
33
36
|
end
|
34
37
|
end
|
35
38
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'json'
|
4
|
+
require 'kameleoon/network/uri_helper'
|
4
5
|
require_relative 'data'
|
5
6
|
|
6
7
|
module Kameleoon
|
@@ -19,8 +20,12 @@ module Kameleoon
|
|
19
20
|
end
|
20
21
|
|
21
22
|
def obtain_full_post_text_line
|
22
|
-
|
23
|
-
|
23
|
+
params = {
|
24
|
+
eventType: 'staticData',
|
25
|
+
deviceType: @device_type,
|
26
|
+
nonce: nonce
|
27
|
+
}
|
28
|
+
Kameleoon::Network::UriHelper.encode_query(params)
|
24
29
|
end
|
25
30
|
end
|
26
31
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'json'
|
4
|
+
require 'kameleoon/network/uri_helper'
|
4
5
|
require_relative 'data'
|
5
6
|
|
6
7
|
module Kameleoon
|
@@ -19,13 +20,15 @@ module Kameleoon
|
|
19
20
|
end
|
20
21
|
|
21
22
|
def obtain_full_post_text_line
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
"
|
23
|
+
params = {
|
24
|
+
eventType: 'page',
|
25
|
+
href: @url,
|
26
|
+
title: @title,
|
27
|
+
nonce: nonce
|
28
|
+
}
|
29
|
+
params[:referrersIndices] = "[#{@referrers.each(&:to_s).join(',')}]" if
|
30
|
+
!@referrers.nil? && !@referrers.empty?
|
31
|
+
Kameleoon::Network::UriHelper.encode_query(params)
|
29
32
|
end
|
30
33
|
end
|
31
34
|
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'em-synchrony/em-http'
|
4
|
+
require 'net/http'
|
5
|
+
require 'kameleoon/version'
|
6
|
+
require 'kameleoon/network/response'
|
7
|
+
require 'kameleoon/exceptions'
|
8
|
+
|
9
|
+
module Kameleoon
|
10
|
+
module Network
|
11
|
+
class NetProvider
|
12
|
+
def make_request(_request)
|
13
|
+
raise KameleoonError, 'Call of not implemented method!'
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def collect_headers(request)
|
19
|
+
headers = { 'Content-Type': request.content_type }
|
20
|
+
headers['User-Agent'] = request.user_agent unless request.user_agent.nil?
|
21
|
+
headers
|
22
|
+
end
|
23
|
+
|
24
|
+
def unknown_method_response(method, request)
|
25
|
+
Response.new("Unknown request method '#{method}'", nil, nil, request)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class EMNetProvider < NetProvider
|
30
|
+
def make_request(request)
|
31
|
+
connetion_options = {
|
32
|
+
tls: { verify_peer: false },
|
33
|
+
connect_timeout: request.timeout,
|
34
|
+
inactivity_timeout: request.timeout
|
35
|
+
}
|
36
|
+
headers = collect_headers(request)
|
37
|
+
request_options = { head: headers, body: request.data }
|
38
|
+
begin
|
39
|
+
case request.method
|
40
|
+
when Method::POST
|
41
|
+
EventMachine::HttpRequest.new(request.url, connetion_options).apost(request_options)
|
42
|
+
when Method::GET
|
43
|
+
EventMachine::HttpRequest.new(request.url, connetion_options).aget(request_options)
|
44
|
+
else
|
45
|
+
dfr = DeferrableResponse.new
|
46
|
+
dfr.response = unknown_method_response(request.method, request)
|
47
|
+
dfr
|
48
|
+
end
|
49
|
+
rescue => e
|
50
|
+
dfr = DeferrableResponse.new
|
51
|
+
dfr.response = Response.new(e, nil, nil, request)
|
52
|
+
dfr
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.em_resp_to_response(request, resp)
|
57
|
+
return resp if resp.is_a?(Response)
|
58
|
+
|
59
|
+
Response.new(nil, resp.response_header.status, resp.response, request)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class SyncNetProvider < NetProvider
|
64
|
+
def make_request(request)
|
65
|
+
resp = nil
|
66
|
+
begin
|
67
|
+
case request.method
|
68
|
+
when Method::GET
|
69
|
+
req = Net::HTTP::Get.new(request.url)
|
70
|
+
when Method::POST
|
71
|
+
req = Net::HTTP::Post.new(request.url)
|
72
|
+
req.body = request.data
|
73
|
+
else
|
74
|
+
return unknown_method_response(request.method, request)
|
75
|
+
end
|
76
|
+
timeout = request.timeout.to_f / 1000.0
|
77
|
+
headers = collect_headers(request)
|
78
|
+
headers.each { |k, v| req[k] = v }
|
79
|
+
uri = URI(request.url)
|
80
|
+
Net::HTTP.start(uri.hostname, uri.port, use_ssl: true, open_timeout: timeout,
|
81
|
+
read_timeout: timeout, ssl_timeout: timeout) do |http|
|
82
|
+
resp = http.request(req)
|
83
|
+
end
|
84
|
+
rescue => e
|
85
|
+
return Response.new(e, nil, nil, request)
|
86
|
+
end
|
87
|
+
Response.new(nil, resp.code.to_i, resp.body, request)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'em-synchrony'
|
4
|
+
require 'kameleoon/network/content_type'
|
5
|
+
require 'kameleoon/network/method'
|
6
|
+
require 'kameleoon/network/request'
|
7
|
+
require 'kameleoon/network/net_provider'
|
8
|
+
|
9
|
+
module Kameleoon
|
10
|
+
module Network
|
11
|
+
##
|
12
|
+
# NetworkManager is used to make API calls.
|
13
|
+
class NetworkManager
|
14
|
+
FETCH_CONFIGURATION_ATTEMPT_NUMBER = 4
|
15
|
+
TRACKING_CALL_ATTEMPT_NUMBER = 4
|
16
|
+
TRACKING_CALL_RETRY_DELAY = 5.0 # in seconds
|
17
|
+
|
18
|
+
attr_reader :environment, :default_timeout, :url_provider
|
19
|
+
|
20
|
+
def initialize(environment, default_timeout, url_provider, log_func = nil)
|
21
|
+
@environment = environment
|
22
|
+
@default_timeout = default_timeout
|
23
|
+
@url_provider = url_provider
|
24
|
+
@em_net_provider = EMNetProvider.new
|
25
|
+
@sync_net_provider = SyncNetProvider.new
|
26
|
+
@log_func = log_func
|
27
|
+
end
|
28
|
+
|
29
|
+
def fetch_configuration(timestamp = nil, timeout = nil)
|
30
|
+
url = @url_provider.make_configuration_url(@environment, timestamp)
|
31
|
+
timeout = ensure_timeout(timeout)
|
32
|
+
request = Request.new(Method::GET, url, ContentType::JSON, timeout)
|
33
|
+
attempts_left = FETCH_CONFIGURATION_ATTEMPT_NUMBER
|
34
|
+
while attempts_left.positive?
|
35
|
+
em_resp = EM::Synchrony.sync(@em_net_provider.make_request(request))
|
36
|
+
response = EMNetProvider.em_resp_to_response(request, em_resp)
|
37
|
+
result = handle_response(response)
|
38
|
+
return result if result
|
39
|
+
|
40
|
+
attempts_left -= 1
|
41
|
+
end
|
42
|
+
false
|
43
|
+
end
|
44
|
+
|
45
|
+
def get_remote_data(key, timeout = nil)
|
46
|
+
url = @url_provider.make_api_data_get_request_url(key)
|
47
|
+
timeout = ensure_timeout(timeout)
|
48
|
+
request = Request.new(Method::GET, url, ContentType::JSON, timeout)
|
49
|
+
response = @sync_net_provider.make_request(request)
|
50
|
+
handle_response(response)
|
51
|
+
end
|
52
|
+
|
53
|
+
def get_remote_visitor_data(visitor_code, timeout = nil)
|
54
|
+
url = @url_provider.make_visitor_data_get_url(visitor_code)
|
55
|
+
timeout = ensure_timeout(timeout)
|
56
|
+
request = Request.new(Method::GET, url, ContentType::JSON, timeout)
|
57
|
+
response = @sync_net_provider.make_request(request)
|
58
|
+
handle_response(response)
|
59
|
+
end
|
60
|
+
|
61
|
+
def send_tracking_data(visitor_code, lines, user_agent, timeout = nil)
|
62
|
+
return if lines.nil? || lines.empty?
|
63
|
+
|
64
|
+
url = @url_provider.make_tracking_url(visitor_code)
|
65
|
+
timeout = ensure_timeout(timeout)
|
66
|
+
data = (lines.map(&:obtain_full_post_text_line).join("\n") || '').encode('UTF-8')
|
67
|
+
request = Request.new(Method::POST, url, ContentType::TEXT, timeout, user_agent, data)
|
68
|
+
Thread.new do
|
69
|
+
attempts_left = TRACKING_CALL_ATTEMPT_NUMBER
|
70
|
+
loop do
|
71
|
+
response = @sync_net_provider.make_request(request)
|
72
|
+
if handle_response(response) != false
|
73
|
+
lines.each { |line| line.sent = true }
|
74
|
+
break
|
75
|
+
end
|
76
|
+
attempts_left -= 1
|
77
|
+
break unless attempts_left.positive?
|
78
|
+
|
79
|
+
delay(TRACKING_CALL_RETRY_DELAY)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def log_failure(request, message)
|
87
|
+
return if @log_func.nil?
|
88
|
+
|
89
|
+
premsg = "#{request.method} call '#{request.url}' "
|
90
|
+
premsg += request.data.nil? ? 'failed' : "(data '#{request.data}') failed"
|
91
|
+
@log_func.call("#{premsg}: #{message}")
|
92
|
+
end
|
93
|
+
|
94
|
+
def delay(period)
|
95
|
+
sleep(period)
|
96
|
+
end
|
97
|
+
|
98
|
+
def ensure_timeout(timeout)
|
99
|
+
timeout.nil? ? @default_timeout : timeout
|
100
|
+
end
|
101
|
+
|
102
|
+
def handle_response(response)
|
103
|
+
if !response.error.nil?
|
104
|
+
log_failure(response.request, "Error occurred during request: #{response.error}")
|
105
|
+
elsif response.code / 100 != 2
|
106
|
+
log_failure(response.request, "Received unexpected status code '#{response.code}'")
|
107
|
+
else
|
108
|
+
return response.body
|
109
|
+
end
|
110
|
+
false
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kameleoon
|
4
|
+
module Network
|
5
|
+
##
|
6
|
+
# Request represents HTTP request.
|
7
|
+
class Request
|
8
|
+
attr_reader :method, :url, :content_type, :timeout, :user_agent, :data
|
9
|
+
|
10
|
+
def initialize(method, url, content_type, timeout, user_agent = nil, data = nil)
|
11
|
+
@method = method
|
12
|
+
@url = url
|
13
|
+
@content_type = content_type
|
14
|
+
@timeout = timeout
|
15
|
+
@user_agent = user_agent
|
16
|
+
@data = !data.nil? && data.is_a?(String) ? data.encode('UTF-8') : data
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kameleoon
|
4
|
+
module Network
|
5
|
+
##
|
6
|
+
# Response represents HTTP response.
|
7
|
+
class Response
|
8
|
+
attr_reader :error, :code, :body, :request
|
9
|
+
|
10
|
+
def initialize(error, code, body, request)
|
11
|
+
@error = error
|
12
|
+
@code = code
|
13
|
+
@body = body
|
14
|
+
@request = request
|
15
|
+
end
|
16
|
+
|
17
|
+
def success?
|
18
|
+
@error.nil? && (@code / 100 == 2)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class DeferrableResponse < EventMachine::DefaultDeferrable
|
23
|
+
attr_writer :response
|
24
|
+
|
25
|
+
def callback(&blk)
|
26
|
+
blk.call(@response)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/kameleoon/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kameleoon-client-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kameleoon
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-09-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: em-http-request
|
@@ -95,7 +95,13 @@ files:
|
|
95
95
|
- lib/kameleoon/factory.rb
|
96
96
|
- lib/kameleoon/hybrid/manager.rb
|
97
97
|
- lib/kameleoon/network/activity_event.rb
|
98
|
+
- lib/kameleoon/network/content_type.rb
|
98
99
|
- lib/kameleoon/network/experiment_event.rb
|
100
|
+
- lib/kameleoon/network/method.rb
|
101
|
+
- lib/kameleoon/network/net_provider.rb
|
102
|
+
- lib/kameleoon/network/network_manager.rb
|
103
|
+
- lib/kameleoon/network/request.rb
|
104
|
+
- lib/kameleoon/network/response.rb
|
99
105
|
- lib/kameleoon/network/uri_helper.rb
|
100
106
|
- lib/kameleoon/network/url_provider.rb
|
101
107
|
- lib/kameleoon/real_time/real_time_configuration_service.rb
|
@@ -103,7 +109,6 @@ files:
|
|
103
109
|
- lib/kameleoon/real_time/sse_client.rb
|
104
110
|
- lib/kameleoon/real_time/sse_message.rb
|
105
111
|
- lib/kameleoon/real_time/sse_request.rb
|
106
|
-
- lib/kameleoon/request.rb
|
107
112
|
- lib/kameleoon/storage/cache.rb
|
108
113
|
- lib/kameleoon/storage/cache_factory.rb
|
109
114
|
- lib/kameleoon/storage/variation_storage.rb
|
data/lib/kameleoon/request.rb
DELETED
@@ -1,76 +0,0 @@
|
|
1
|
-
require 'em-synchrony/em-http'
|
2
|
-
require 'kameleoon/version'
|
3
|
-
require 'net/http'
|
4
|
-
|
5
|
-
module Kameleoon
|
6
|
-
# @api private
|
7
|
-
module Request
|
8
|
-
protected
|
9
|
-
|
10
|
-
module Method
|
11
|
-
GET = 'get'.freeze
|
12
|
-
POST = 'post'.freeze
|
13
|
-
end
|
14
|
-
|
15
|
-
def get(request_options, url, connexion_options = {})
|
16
|
-
request(Method::GET, request_options, url, connexion_options)
|
17
|
-
end
|
18
|
-
|
19
|
-
def post(request_options, url, connexion_options = {})
|
20
|
-
request(Method::POST, request_options, url, connexion_options)
|
21
|
-
end
|
22
|
-
|
23
|
-
def get_sync(url, connexion_options = {})
|
24
|
-
request_sync(Method::GET, url, connexion_options, {})
|
25
|
-
end
|
26
|
-
|
27
|
-
def post_sync(request_options, url, connexion_options = {})
|
28
|
-
request_sync(Method::POST, url, connexion_options, request_options)
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def request(method, request_options, url, connexion_options)
|
34
|
-
connexion_options[:tls] = { verify_peer: false }
|
35
|
-
case method
|
36
|
-
when Method::POST then
|
37
|
-
return EventMachine::HttpRequest.new(url, connexion_options).apost request_options
|
38
|
-
when Method::GET then
|
39
|
-
return EventMachine::HttpRequest.new(url, connexion_options).get request_options
|
40
|
-
else
|
41
|
-
print 'Unknown request type'
|
42
|
-
return false
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def request_sync(method, url, connexion_options, request_options)
|
47
|
-
request_options = {} if request_options.nil?
|
48
|
-
case method
|
49
|
-
when Method::GET then
|
50
|
-
uri = URI(url)
|
51
|
-
request = Net::HTTP.new(url)
|
52
|
-
response = Net::HTTP.get_response(uri)
|
53
|
-
response
|
54
|
-
when Method::POST then
|
55
|
-
uri = URI.parse(url)
|
56
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
57
|
-
http.use_ssl = true
|
58
|
-
request = Net::HTTP::Post.new(uri, initheader = request_options[:head])
|
59
|
-
request.body = request_options[:body]
|
60
|
-
response = http.request(request)
|
61
|
-
response
|
62
|
-
else
|
63
|
-
print "Unknown request type"
|
64
|
-
return false
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def successful?(request)
|
69
|
-
!request.nil? && request != false && /20\d/.match(request.response_header.status.to_s)
|
70
|
-
end
|
71
|
-
|
72
|
-
def successful_sync?(response)
|
73
|
-
!response.nil? && response != false && response.is_a?(Net::HTTPSuccess)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|