liveqa 1.8.3 → 1.9.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
  SHA1:
3
- metadata.gz: eedcd2fa28e813d0ebde999b52507b9dd8517668
4
- data.tar.gz: a7739558d0b587260136c1d9a364dead5c283d43
3
+ metadata.gz: 9e2af9cc4ebfb7e97a8330c769db5ffb49989ec9
4
+ data.tar.gz: 4261ba5bbe8cbebd535f876fe9f8762e460286cf
5
5
  SHA512:
6
- metadata.gz: fd51dba298a81bc4f67a98db6ec8a2adc0cb418f9c474f787c39b6ffa9ed57393dff40c4d1e333f9afdebac25003872c9cbac51e34c53dddb754e18bde58eeb1
7
- data.tar.gz: ecbb7fa596f45776d43abb4b7fc3c2e1f2d5b4e2853b6702da72a045c22d963a32a964e108c7075fe228dbbd09715130b4eb4147c4820fc640d1ac86d1ad78bf
6
+ metadata.gz: 0f5fc2bd622bdf4e98573f3205ced2fce4ae9b569e398ab28633654cffaf2a3c1e2532c63da9f8352bcc74482d787fd6958104d0c02d52afe83baf3596374595
7
+ data.tar.gz: 7e2fccdb0c9a117a852989f3ab3e7744b958a0f31f3014cff519614492b3d3cdc862956e375ef6f715c747908170674708ca034a2b578d41201b3c9034f84a91
data/.rubocop.yml CHANGED
@@ -22,7 +22,13 @@ Layout/EmptyLinesAroundBlockBody:
22
22
 
23
23
  Style/FrozenStringLiteralComment:
24
24
  Enabled: false
25
- Style/MethodMissing:
25
+ Style/MissingRespondToMissing:
26
+ Enabled: false
27
+ Style/TrailingCommaInHashLiteral:
28
+ EnforcedStyleForMultiline: comma
29
+ Style/TrailingCommaInArrayLiteral:
30
+ EnforcedStyleForMultiline: comma
31
+ Style/TrailingCommaInArguments:
26
32
  Enabled: false
27
33
 
28
34
  Documentation:
@@ -32,7 +38,7 @@ Bundler/DuplicatedGem:
32
38
  Enabled: false
33
39
 
34
40
  AllCops:
35
- TargetRubyVersion: 2.3
41
+ TargetRubyVersion: 2.2
36
42
  Exclude:
37
43
  - 'spec/**/*'
38
44
  Exclude:
data/.travis.yml CHANGED
@@ -1,7 +1,6 @@
1
1
  cache: bundler
2
2
  language: ruby
3
3
  rvm:
4
- - 2.1
5
4
  - 2.2
6
5
  - 2.3
7
6
  - 2.4
data/LICENCE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2018- LiveQA, Inc. (https://www.liveqa.com)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -1,127 +1,20 @@
1
1
  # LiveQA
2
2
 
3
- [![Build Status](https://travis-ci.org/arkes/liveqa-ruby.svg?branch=master)](https://travis-ci.org/arkes/liveqa-ruby)
3
+ [![Build Status](https://travis-ci.org/LiveQA/liveqa-ruby.svg?branch=master)](https://travis-ci.org/LiveQA/liveqa-ruby)
4
4
 
5
- LiveQA ruby integration for [LiveQA](https://www.liveqa.io)
6
-
7
- ## Installation
8
-
9
- ```sh
10
- gem install liveqa
11
- ```
12
-
13
- For Rails in your Gemfile
14
-
15
- ```ruby
16
- gem 'liveqa'
17
- ```
18
-
19
- ## Configuration
20
-
21
- ```ruby
22
- LiveQA.configure do |config|
23
- ##
24
- # Account token can be found inside your environment settings
25
- config.account_token = 'acc_xx'
26
-
27
- ##
28
- # The name of your space
29
- config.space_name = 'LiveQA'
30
-
31
- ##
32
- # The name of your environement
33
- config.environment_name = 'production'
34
-
35
- ##
36
- # If you use a proxy.
37
- # default: nil
38
- # config.proxy_url = ENV['HTTP_PROXY']
39
-
40
- ##
41
- # If enabled is set to false nothing is send to the server
42
- # default: true
43
- # config.enabled = true
44
-
45
- ##
46
- # Extra attributes to be obfuscated
47
- # default: []
48
- # config.obfuscated_fields = ['credit_card_number']
49
-
50
- ##
51
- # Use an async handler to send data to the liveqa to
52
- # avoid slowing down your application
53
- # available option: :sidekiq
54
- # default: nil
55
- # config.async_handler = :sidekiq
56
-
57
- ##
58
- # Options to be passed to the async handler
59
- # default: {}
60
- # config.async_options = { queue: 'liveqa' }
5
+ [LiveQA](https://www.liveqa.io) Ruby library
61
6
 
62
- ##
63
- # Metadata is passed with every request to the server
64
- # default: nil
65
- # config.metadata = {
66
- #. customer: -> { current_customer.id},
67
- #. version: 42
68
- # }
69
- #
70
- end
71
- ```
7
+ Our Ruby library let's you interact easily with our API. All the request will go to our servers and will be display processed and display inside our debugger.
72
8
 
73
- ## Usage
9
+ ## Setup
74
10
 
75
- ### Track
11
+ - [Start by creating an account](https://www.liveqa.io/register)
12
+ - Follow the [Quick Start Guide](https://docs.liveqa.io/docs/quick-start) to get set up
13
+ - Use our [Ruby Library](https://docs.liveqa.io/docs/ruby) to install and configure LiveQA for your platform.
76
14
 
77
- Track an event
78
15
 
79
- Attributes:
16
+ ## Documentation & Usage
80
17
 
81
- * `String` event name
82
- * `Hash` event attributes
18
+ Documentation is available at [docs.liveqa.io/docs/ruby](https://docs.liveqa.io/docs/ruby)
83
19
 
84
- ```ruby
85
- LiveQA.track('my event',
86
- user_id: 42,
87
- properties: {
88
- order_id: 84
89
- }
90
- );
91
- ```
92
-
93
- ### Identify
94
-
95
- Identify a user
96
-
97
- Attributes:
98
-
99
- * `String` user id from your database
100
- * `Hash` user attributes
101
-
102
- ```ruby
103
- LiveQA.identify(42,
104
- properties: {
105
- name: 'John Doe'
106
- }
107
- );
108
- ```
109
-
110
- ### Watch
111
-
112
- Create a watcher
113
-
114
- Attributes:
115
-
116
- * `String|Integer` template flow name or id
117
- * `Hash` watcher attributes
118
-
119
- ```ruby
120
- LiveQA.watch('My Flow',
121
- expected_times: 42
122
- );
123
- ```
124
-
125
- ## Issues
126
-
127
- If you have any issue you can report them on github, or contact support@liveqa.io
20
+ LiveQA ruby integration for [LiveQA](https://www.liveqa.io)
@@ -60,7 +60,9 @@ module LiveQA
60
60
  #
61
61
  # @return [Hash] response
62
62
  # @raise [LiveQA::RequestError] if the request is invalid
63
+ # rubocop:disable Metrics/AbcSize
63
64
  def request(method, path, payload = {}, options = {})
65
+ payload[:sent_at] = Time.now.utc.iso8601(3)
64
66
  payload = Util.deep_obfuscate_value(payload, configurations.obfuscated_fields)
65
67
  url_params = Util.encode_parameters(payload) if method == :get
66
68
  uri = build_endpoint_url(path, url_params)
@@ -76,7 +78,9 @@ module LiveQA
76
78
  Request.execute(request_options).body
77
79
  rescue LiveQA::RequestError => error
78
80
  return error.http_body if error.http_status == 422
81
+ raise
79
82
  end
83
+ # rubocop:enable Metrics/AbcSize
80
84
 
81
85
  private
82
86
 
@@ -99,7 +103,7 @@ module LiveQA
99
103
  return {} unless configurations.http_secure
100
104
  {
101
105
  ca_file: File.expand_path(File.dirname(__FILE__) + '/../../vendor/cacert.pem'),
102
- verify_mode: OpenSSL::SSL::VERIFY_PEER
106
+ verify_mode: OpenSSL::SSL::VERIFY_PEER,
103
107
  }
104
108
  end
105
109
 
@@ -110,8 +114,8 @@ module LiveQA
110
114
  content_type: 'application/json',
111
115
  x_account_token: options.delete(:account_token) || configurations.account_token,
112
116
  x_space_name: options.delete(:space_name) || configurations.space_name,
113
- x_environment_name: options.delete(:environment_name) || configurations.environment_name
114
- }
117
+ x_environment_name: options.delete(:environment_name) || configurations.environment_name,
118
+ },
115
119
  }
116
120
  end
117
121
 
@@ -0,0 +1,18 @@
1
+ module LiveQA
2
+ ##
3
+ # == LiveQA \Batch
4
+ #
5
+ # Accepted Methods:
6
+ #
7
+ # * create
8
+ #
9
+ # @example: Usage
10
+ #
11
+ # request = LiveQA::Batch.create(...) #=> #<LiveQA::Response...>
12
+ #
13
+ class Batch < APIResource
14
+ @resource_name = 'batches'
15
+
16
+ include LiveQA::APIOperation::Save
17
+ end
18
+ end
data/lib/liveqa/config.rb CHANGED
@@ -5,13 +5,6 @@ module LiveQA
5
5
  # Represent the LiveQA configuration for the API
6
6
  class Config
7
7
 
8
- ASYNC_HANDLERS = {
9
- sidekiq: {
10
- class: 'LiveQA::AsyncHandlers::Sidekiq',
11
- require: 'liveqa/async_handlers/sidekiq'
12
- }
13
- }.freeze
14
-
15
8
  DEFAULT_OBFUSCATED_FIELDS = %w[
16
9
  password
17
10
  password_confirmation
@@ -63,12 +56,8 @@ module LiveQA
63
56
  attr_accessor :obfuscated_fields
64
57
 
65
58
  ##
66
- # @return [Null|Symbol|Proc] asynchronous handler
67
- attr_accessor :async_handler
68
-
69
- ##
70
- # @return [Hash] options for asynchronous handler
71
- attr_accessor :async_options
59
+ # @return [Boolean] send
60
+ attr_accessor :async
72
61
 
73
62
  ##
74
63
  # @return [Hash] custom object properties
@@ -78,6 +67,14 @@ module LiveQA
78
67
  # @return [Hash] metadata to be attach to the payload
79
68
  attr_accessor :metadata
80
69
 
70
+ ##
71
+ # @return [Boolean] enable the logger
72
+ attr_accessor :log
73
+
74
+ ##
75
+ # @return [Logger] logger
76
+ attr_accessor :logger
77
+
81
78
  ##
82
79
  # @param [Hash{Symbol=>Object}]
83
80
  # Initialize and validate the configuration
@@ -93,10 +90,11 @@ module LiveQA
93
90
  self.http_secure = options[:http_secure] || true
94
91
  self.enabled = options[:enabled] || true
95
92
  self.obfuscated_fields = options[:obfuscated_fields] || []
96
- self.async_handler = options[:async_handler]
97
- self.async_options = options[:async_options] || {}
93
+ self.async = options[:async] || true
98
94
  self.custom_object_properties = options[:custom_object_properties] || {}
99
95
  self.metadata = options[:metadata]
96
+ self.log = options[:log] || true
97
+ self.logger = FormatedLogger.build(options[:logger])
100
98
  end
101
99
  # rubocop:enable Metrics/CyclomaticComplexity
102
100
  # rubocop:enable Metrics/PerceivedComplexity
@@ -121,15 +119,9 @@ module LiveQA
121
119
  # Format configuration fields
122
120
  #
123
121
  # * Set obfuscated_fields to string
124
- # * Change to the class for async handler
125
122
  #
126
123
  def format!
127
124
  self.obfuscated_fields = (obfuscated_fields.map(&:to_s) + DEFAULT_OBFUSCATED_FIELDS).uniq
128
-
129
- return unless ASYNC_HANDLERS[async_handler]
130
-
131
- require ASYNC_HANDLERS[async_handler][:require]
132
- self.async_handler = Object.const_get(ASYNC_HANDLERS[async_handler][:class]).new(async_options)
133
125
  end
134
126
 
135
127
  def validate_presence(field)
data/lib/liveqa/event.rb CHANGED
@@ -4,7 +4,7 @@ module LiveQA
4
4
  #
5
5
  # Accepted Methods:
6
6
  #
7
- # * update
7
+ # * create
8
8
  #
9
9
  # @example: Usage
10
10
  #
@@ -0,0 +1,45 @@
1
+ module LiveQA
2
+ ##
3
+ # Display formated log
4
+ class FormatedLogger
5
+
6
+ class << self
7
+
8
+ def build(logger)
9
+ return new(logger) if logger
10
+ return new(Rails.logger) if defined?(Rails)
11
+
12
+ new(::Logger.new(STDOUT))
13
+ end
14
+
15
+ end
16
+
17
+ attr_reader :logger
18
+
19
+ PREFIX = '[LiveQA]'.freeze
20
+ METHODS = %i[debug info warn error].freeze
21
+
22
+ def initialize(logger)
23
+ @logger = logger
24
+ end
25
+
26
+ METHODS.each do |method|
27
+ define_method(method) do |message|
28
+ log(method, message)
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def log(type, message)
35
+ return unless configurations.log
36
+
37
+ logger.send(type, "#{PREFIX} #{message}")
38
+ end
39
+
40
+ def configurations
41
+ LiveQA.configurations
42
+ end
43
+
44
+ end
45
+ end
@@ -37,7 +37,7 @@ module LiveQA
37
37
  {
38
38
  name: LiveQA::LIBRARY_NAME,
39
39
  language: 'ruby',
40
- version: LiveQA::VERSION
40
+ version: LiveQA::VERSION,
41
41
  }
42
42
  end
43
43
 
@@ -45,7 +45,7 @@ module LiveQA
45
45
  {
46
46
  host: Socket.gethostname,
47
47
  pid: Process.pid,
48
- software: LiveQA::Store.get(:server_software)
48
+ software: LiveQA::Store.get(:server_software),
49
49
  }
50
50
  end
51
51
 
@@ -56,7 +56,7 @@ module LiveQA
56
56
  if value.is_a?(Proc)
57
57
  begin
58
58
  hash[key] = value.call
59
- rescue
59
+ rescue StandardError => _e
60
60
  nil
61
61
  end
62
62
  next
@@ -90,7 +90,7 @@ module LiveQA
90
90
  uri.merge(
91
91
  "?#{::Rack::Utils.build_query(params)}"
92
92
  ).to_s
93
- rescue
93
+ rescue StandardError => _e
94
94
  ''
95
95
  end
96
96
 
@@ -99,7 +99,7 @@ module LiveQA
99
99
  request.send(type),
100
100
  LiveQA.configurations.obfuscated_fields
101
101
  )
102
- rescue
102
+ rescue StandardError => _e
103
103
  {}
104
104
  end
105
105
 
@@ -125,7 +125,7 @@ module LiveQA
125
125
  headers,
126
126
  LiveQA.configurations.obfuscated_fields
127
127
  )
128
- rescue
128
+ rescue StandardError => _e
129
129
  {}
130
130
  end
131
131
 
@@ -37,7 +37,7 @@ module LiveQA
37
37
 
38
38
  def convert_to_iso8601(int_time)
39
39
  Time.at(int_time).utc.iso8601(3)
40
- rescue
40
+ rescue StandardError => _e
41
41
  nil
42
42
  end
43
43
 
@@ -0,0 +1,48 @@
1
+ module LiveQA
2
+ module Processor
3
+ class Async
4
+
5
+ def initialize
6
+ @max_queue_size = 10_000
7
+ @queue = Queue.new
8
+ @worker = Worker.new(@queue)
9
+ @worker_mutex = Mutex.new
10
+
11
+ at_exit do
12
+ @worker_thread && @worker_thread[:should_exit] = true
13
+ end
14
+ end
15
+
16
+ def enqueue(attributes)
17
+ return false if @queue.length > @max_queue_size
18
+
19
+ @queue << attributes
20
+ ensure_worker_running
21
+
22
+ true
23
+ end
24
+
25
+ def flush
26
+ while !@queue.empty? || @worker.is_requesting?
27
+ ensure_worker_running
28
+ sleep(0.1)
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def ensure_worker_running
35
+ return if worker_running?
36
+ @worker_mutex.synchronize do
37
+ return if worker_running?
38
+ @worker_thread = Thread.new { @worker.run }
39
+ end
40
+ end
41
+
42
+ def worker_running?
43
+ @worker_thread && @worker_thread.alive?
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,81 @@
1
+ module LiveQA
2
+ module Processor
3
+ class Batch
4
+
5
+ attr_reader :messages
6
+
7
+ MAX_BYTES = 204_800 # 200Kb
8
+ MAX_MESSAGES = 100
9
+ MAX_MESSAGE_BYTES = 32_768 # 32Kb
10
+
11
+ MAX_RETRY = 10
12
+ RETRY_MAP = {
13
+ 1 => 2,
14
+ 2 => 5,
15
+ 3 => 10,
16
+ 4 => 20,
17
+ 5 => 30,
18
+ 6 => 30,
19
+ 7 => 30,
20
+ 8 => 30,
21
+ 9 => 30,
22
+ 10 => 30,
23
+ }.freeze
24
+
25
+ def initialize
26
+ @retry_count = 0
27
+ @total_bytes = 0
28
+ @messages = []
29
+ end
30
+
31
+ def <<(message)
32
+ message_json_size = message.to_json.bytesize
33
+
34
+ if max_message_reached?(message_json_size)
35
+ LiveQA.configurations.logger.error('Message is too big to be send')
36
+ return false
37
+ end
38
+
39
+ @total_bytes += message_json_size
40
+
41
+ @messages << message
42
+
43
+ true
44
+ end
45
+
46
+ def full?
47
+ max_messages_reached? || max_size_reached?
48
+ end
49
+
50
+ def update_retry
51
+ @retry_count += 1
52
+ @retry_at = Time.now + (RETRY_MAP[@retry_count] || 120)
53
+ end
54
+
55
+ def can_run?
56
+ return true if @retry_count.zero?
57
+
58
+ Time.now >= @retry_at
59
+ end
60
+
61
+ def can_retry?
62
+ @retry_count < MAX_RETRY
63
+ end
64
+
65
+ private
66
+
67
+ def max_messages_reached?
68
+ @messages.length >= MAX_MESSAGES
69
+ end
70
+
71
+ def max_size_reached?
72
+ @total_bytes >= MAX_BYTES
73
+ end
74
+
75
+ def max_message_reached?(message_json_size)
76
+ message_json_size > MAX_MESSAGE_BYTES
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,66 @@
1
+ module LiveQA
2
+ module Processor
3
+ class Worker
4
+
5
+ DEFAULT_WAIT_TIME = 2
6
+
7
+ attr_reader :queue
8
+ attr_reader :lock
9
+ attr_reader :batches
10
+
11
+ def initialize(queue)
12
+ @queue = queue
13
+ @lock = Mutex.new
14
+ @batches = []
15
+ end
16
+
17
+ def run
18
+ until Thread.current[:should_exit]
19
+ return if queue.empty? && batches.empty?
20
+
21
+ sleep(DEFAULT_WAIT_TIME) if queue.empty?
22
+
23
+ create_new_batch unless queue.empty?
24
+
25
+ send_batches
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def create_new_batch
32
+ batch = Batch.new
33
+ lock.synchronize do
34
+ batch << queue.pop until batch.full? || queue.empty?
35
+ end
36
+ batches.push(batch)
37
+ end
38
+
39
+ def send_batches
40
+ batches.dup.each do
41
+ batch = batches.pop
42
+
43
+ unless batch.can_run?
44
+ batches.push(batch)
45
+ next
46
+ end
47
+
48
+ begin
49
+ LiveQA::Batch.create(Message.base.merge(data: batch.messages))
50
+ rescue LiveQA::RequestError => error
51
+ return LiveQA.configurations.logger.error(error.message) if [401, 404].include?(error.http_status)
52
+ batch_retry(batch)
53
+ rescue Errno::ECONNREFUSED => _error
54
+ batch_retry(batch)
55
+ end
56
+ end
57
+ end
58
+
59
+ def batch_retry(batch)
60
+ batch.update_retry
61
+ batches.push(batch) if batch.can_retry?
62
+ end
63
+
64
+ end
65
+ end
66
+ end
data/lib/liveqa/util.rb CHANGED
@@ -94,7 +94,7 @@ module LiveQA
94
94
  deep_transform_keys_in_object(hash_object) do |key|
95
95
  begin
96
96
  underscore(key).to_sym
97
- rescue
97
+ rescue StandardError => _e
98
98
  key
99
99
  end
100
100
  end
@@ -110,7 +110,7 @@ module LiveQA
110
110
  deep_transform_keys_in_object(hash_object) do |key|
111
111
  begin
112
112
  key.to_s
113
- rescue
113
+ rescue StandardError => _e
114
114
  key
115
115
  end
116
116
  end
@@ -126,7 +126,7 @@ module LiveQA
126
126
  deep_transform_keys_in_object(hash_object) do |key|
127
127
  begin
128
128
  key.to_sym
129
- rescue
129
+ rescue StandardError => _e
130
130
  key
131
131
  end
132
132
  end
@@ -1,3 +1,3 @@
1
1
  module LiveQA # :nodoc:
2
- VERSION = '1.8.3'.freeze
2
+ VERSION = '1.9.0'.freeze
3
3
  end