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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1aef3f7b97d4a32024bec989ed4356cc1d10425c16b419b55f524cf1abfd4f52
4
- data.tar.gz: 5705b067a3effc12b7f38ccef23e01ee28e370b99a212fd371ce0c863184c3ec
3
+ metadata.gz: c3ada1f2651143c67ac458a8adab629e1d6ad17e95a204809a0a2b44f0ee3a28
4
+ data.tar.gz: 306fab877c00913cc4b13cecfdf9e773c2bb735be110747df37d509b537d2e01
5
5
  SHA512:
6
- metadata.gz: 99cc273ca6010c9ce2c451e7a7cb3795e4b264b670fb8009436e5b9ddbaacf388e894f0922926bf92113ebf9f211636f4b4fc2a34fc01babcf848327bba798f8
7
- data.tar.gz: 3686a34890b5438e5d0ca878cbb0bfb89386f2150267c73ef27754d207c6f918aad3355a312045251d3dea15fc2faa6558433f172b907ae4e913c7b60cc204f4
6
+ metadata.gz: 38ff8932ce2f9a0e9d72644ae47de0d0f34434943f33328f0459607e34769c83e64ff6325989f9722cc8cb202d440d5d8891dea073179d269e01d6b49fa361f9
7
+ data.tar.gz: b3ee0f99356eac881b01970269cde022435872c270268ffc6ec8c0dc0d31abe3816914a497edb15e744810f60190165c84424f4799876ff56d1e24e1590d56f8
@@ -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 `is_feature_active` instead.
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
- connexion_options = { connect_timeout: (timeout.to_f / 1000.0) }
403
- url = @url_provider.make_api_data_get_request_url(key)
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(@environment, time_stamp)
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(environment = @environment, time_stamp = nil)
601
+ def obtain_configuration(time_stamp = nil)
583
602
  log 'Fetching configuration from Client-Config service'
584
- url = @url_provider.make_configuration_url(environment, time_stamp)
585
- request = request_configuration(url)
586
- return false unless request
603
+ response = @network_manager.fetch_configuration(time_stamp)
604
+ return false unless response
587
605
 
588
- configuration = JSON.parse(request.response)
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: #{request.inspect}"
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
- Thread.new do
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
- nonce = Kameleoon::Utils.generate_random_string(NONCE_LENGTH)
28
- url = "eventType=staticData&browserIndex=#{@type}&nonce=#{nonce}"
29
- url.concat("&browserVersion=#{@version}") if @version.is_a?(Integer) || (@version.is_a?(Float) && !@version.nan?)
30
- url
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
- nonce = Kameleoon::Utils.generate_random_string(NONCE_LENGTH)
23
- "eventType=conversion&goalId=#{@goal_id}&revenue=#{@revenue}&negative=#{@negative}&nonce=#{nonce}"
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
- nonce = Kameleoon::Utils.generate_random_string(NONCE_LENGTH)
50
- "eventType=customData&index=#{@id}&valuesCountMap=#{self.class.encode(str_values)}&overwrite=true&nonce=#{nonce}"
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
@@ -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
- def self.encode(url)
32
- Network::UriHelper.encode_uri(url)
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
- nonce = Kameleoon::Utils.generate_random_string(NONCE_LENGTH)
23
- "eventType=staticData&deviceType=#{@device_type}&nonce=#{nonce}"
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
- nonce = Kameleoon::Utils.generate_random_string(NONCE_LENGTH)
23
- referrer_text = if !@referrers.nil? && !@referrers.empty?
24
- "&referrersIndices=%5B#{@referrers.each(&:to_s).join('%2C')}%5D"
25
- else
26
- ''
27
- end
28
- "eventType=page&href=#{self.class.encode(@url)}&title=#{self.class.encode(@title)}#{referrer_text}&nonce=#{nonce}"
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,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kameleoon
4
+ module Network
5
+ module ContentType
6
+ TEXT = 'text/plain'
7
+ JSON = 'application/json'
8
+ FORM = 'application/x-www-form-urlencoded'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kameleoon
4
+ module Network
5
+ module Method
6
+ GET = 'GET'
7
+ POST = 'POST'
8
+ end
9
+ end
10
+ 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
@@ -21,6 +21,7 @@ module Kameleoon
21
21
  end
22
22
 
23
23
  def self.encode_query(params)
24
+ params.delete_if { |_k, v| v.nil? || (v == '') }
24
25
  encoded = URI.encode_www_form(params)
25
26
  encoded.gsub!(/%21|%27|%28|%29|%7E|\+/,
26
27
  '%21' => '!',
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Kameleoon
4
4
  SDK_NAME = 'RUBY'
5
- SDK_VERSION = '2.1.1'
5
+ SDK_VERSION = '2.2.0'
6
6
 
7
7
  # SdkManager is a helper method for fetching / obtaining version of SDK from string
8
8
  class SdkVersion
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.1.1
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-08-28 00:00:00.000000000 Z
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
@@ -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