kameleoon-client-ruby 2.1.1 → 2.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/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
|