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 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