lhc 12.3.0 → 13.4.0.pre.pro1766.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rubocop.yml +15 -0
  3. data/.github/workflows/test.yml +15 -0
  4. data/.rubocop.yml +344 -19
  5. data/.ruby-version +1 -1
  6. data/Gemfile.activesupport5 +1 -1
  7. data/Gemfile.activesupport6 +1 -1
  8. data/README.md +102 -2
  9. data/Rakefile +3 -3
  10. data/lhc.gemspec +6 -3
  11. data/lib/lhc.rb +70 -59
  12. data/lib/lhc/concerns/lhc/fix_invalid_encoding_concern.rb +1 -0
  13. data/lib/lhc/config.rb +16 -0
  14. data/lib/lhc/endpoint.rb +3 -0
  15. data/lib/lhc/error.rb +7 -3
  16. data/lib/lhc/interceptor.rb +4 -0
  17. data/lib/lhc/interceptors.rb +1 -0
  18. data/lib/lhc/interceptors/auth.rb +10 -5
  19. data/lib/lhc/interceptors/caching.rb +70 -44
  20. data/lib/lhc/interceptors/logging.rb +4 -2
  21. data/lib/lhc/interceptors/monitoring.rb +46 -11
  22. data/lib/lhc/interceptors/retry.rb +2 -0
  23. data/lib/lhc/interceptors/rollbar.rb +3 -2
  24. data/lib/lhc/interceptors/throttle.rb +7 -2
  25. data/lib/lhc/interceptors/zipkin.rb +2 -0
  26. data/lib/lhc/railtie.rb +0 -1
  27. data/lib/lhc/request.rb +37 -4
  28. data/lib/lhc/response.rb +1 -0
  29. data/lib/lhc/response/data.rb +1 -1
  30. data/lib/lhc/rspec.rb +1 -2
  31. data/lib/lhc/scrubber.rb +45 -0
  32. data/lib/lhc/scrubbers/auth_scrubber.rb +32 -0
  33. data/lib/lhc/scrubbers/body_scrubber.rb +30 -0
  34. data/lib/lhc/scrubbers/headers_scrubber.rb +40 -0
  35. data/lib/lhc/scrubbers/params_scrubber.rb +14 -0
  36. data/lib/lhc/version.rb +1 -1
  37. data/spec/config/scrubs_spec.rb +108 -0
  38. data/spec/error/to_s_spec.rb +13 -8
  39. data/spec/formats/multipart_spec.rb +2 -2
  40. data/spec/formats/plain_spec.rb +1 -1
  41. data/spec/interceptors/after_response_spec.rb +1 -1
  42. data/spec/interceptors/caching/main_spec.rb +2 -2
  43. data/spec/interceptors/caching/multilevel_cache_spec.rb +139 -0
  44. data/spec/interceptors/caching/options_spec.rb +0 -11
  45. data/spec/interceptors/define_spec.rb +1 -0
  46. data/spec/interceptors/logging/main_spec.rb +21 -1
  47. data/spec/interceptors/monitoring/caching_spec.rb +66 -0
  48. data/spec/interceptors/response_competition_spec.rb +2 -2
  49. data/spec/interceptors/return_response_spec.rb +2 -2
  50. data/spec/interceptors/rollbar/main_spec.rb +27 -15
  51. data/spec/request/scrubbed_headers_spec.rb +101 -0
  52. data/spec/request/scrubbed_options_spec.rb +185 -0
  53. data/spec/request/scrubbed_params_spec.rb +25 -0
  54. data/spec/response/data_spec.rb +2 -2
  55. data/spec/spec_helper.rb +1 -0
  56. data/spec/support/zipkin_mock.rb +1 -0
  57. metadata +59 -26
  58. data/.rubocop.localch.yml +0 -325
  59. data/Gemfile.activesupport4 +0 -4
  60. data/cider-ci.yml +0 -6
  61. data/cider-ci/bin/bundle +0 -51
  62. data/cider-ci/bin/ruby_install +0 -8
  63. data/cider-ci/bin/ruby_version +0 -25
  64. data/cider-ci/jobs/rspec-activesupport-4.yml +0 -28
  65. data/cider-ci/jobs/rspec-activesupport-5.yml +0 -27
  66. data/cider-ci/jobs/rspec-activesupport-6.yml +0 -28
  67. data/cider-ci/jobs/rubocop.yml +0 -18
  68. data/cider-ci/task_components/bundle.yml +0 -22
  69. data/cider-ci/task_components/rspec.yml +0 -36
  70. data/cider-ci/task_components/rubocop.yml +0 -29
  71. data/cider-ci/task_components/ruby.yml +0 -15
@@ -12,6 +12,7 @@ class LHC::Zipkin < LHC::Interceptor
12
12
 
13
13
  def before_request
14
14
  return if !dependencies? || !tracing?
15
+
15
16
  ZipkinTracer::TraceContainer.with_trace_id(trace_id) do
16
17
  # add headers even if the current trace_id should not be sampled
17
18
  B3_HEADERS.each { |method, header| request.headers[header] = trace_id.send(method).to_s }
@@ -23,6 +24,7 @@ class LHC::Zipkin < LHC::Interceptor
23
24
  def after_response
24
25
  # only sample the current call if we're instructed to do so
25
26
  return unless dependencies? && trace_id.sampled?
27
+
26
28
  end_trace!
27
29
  end
28
30
 
data/lib/lhc/railtie.rb CHANGED
@@ -4,7 +4,6 @@ module LHC
4
4
  class Railtie < Rails::Railtie
5
5
  initializer "lhc.configure_rails_initialization" do
6
6
  LHC::Caching.cache ||= Rails.cache
7
- LHC::Caching.logger ||= Rails.logger
8
7
  end
9
8
  end
10
9
  end
data/lib/lhc/request.rb CHANGED
@@ -12,7 +12,13 @@ class LHC::Request
12
12
 
13
13
  TYPHOEUS_OPTIONS ||= [:params, :method, :body, :headers, :follow_location, :params_encoding]
14
14
 
15
- attr_accessor :response, :options, :raw, :format, :error_handler, :errors_ignored, :source
15
+ attr_accessor :response,
16
+ :options,
17
+ :raw,
18
+ :format,
19
+ :error_handler,
20
+ :errors_ignored,
21
+ :source
16
22
 
17
23
  def initialize(options, self_executing = true)
18
24
  self.errors_ignored = (options.fetch(:ignore, []) || []).to_a.compact
@@ -25,7 +31,11 @@ class LHC::Request
25
31
  interceptors.intercept(:before_raw_request)
26
32
  self.raw = create_request
27
33
  interceptors.intercept(:before_request)
28
- run! if self_executing && !response
34
+ if self_executing && !response
35
+ run!
36
+ elsif response
37
+ on_complete(response)
38
+ end
29
39
  end
30
40
 
31
41
  def url
@@ -52,6 +62,23 @@ class LHC::Request
52
62
  raw.run
53
63
  end
54
64
 
65
+ def scrubbed_params
66
+ LHC::ParamsScrubber.new(params.deep_dup).scrubbed
67
+ end
68
+
69
+ def scrubbed_headers
70
+ LHC::HeadersScrubber.new(headers.deep_dup, options[:auth]).scrubbed
71
+ end
72
+
73
+ def scrubbed_options
74
+ scrubbed_options = options.deep_dup
75
+ scrubbed_options[:params] = LHC::ParamsScrubber.new(scrubbed_options[:params]).scrubbed
76
+ scrubbed_options[:headers] = LHC::HeadersScrubber.new(scrubbed_options[:headers], scrubbed_options[:auth]).scrubbed
77
+ scrubbed_options[:auth] = LHC::AuthScrubber.new(scrubbed_options[:auth]).scrubbed
78
+ scrubbed_options[:body] = LHC::BodyScrubber.new(scrubbed_options[:body]).scrubbed
79
+ scrubbed_options
80
+ end
81
+
55
82
  private
56
83
 
57
84
  attr_accessor :interceptors
@@ -63,6 +90,7 @@ class LHC::Request
63
90
 
64
91
  def optionally_encoded_url(options)
65
92
  return options[:url] unless options.fetch(:url_encoding, true)
93
+
66
94
  encode_url(options[:url])
67
95
  end
68
96
 
@@ -81,13 +109,15 @@ class LHC::Request
81
109
 
82
110
  def translate_body(options)
83
111
  return options if options.fetch(:body, nil).blank?
112
+
84
113
  options[:body] = format.to_body(options[:body])
85
114
  options
86
115
  end
87
116
 
88
117
  def encode_url(url)
89
118
  return url if url.nil?
90
- URI.escape(url)
119
+
120
+ Addressable::URI.escape(url)
91
121
  end
92
122
 
93
123
  def typhoeusize(options)
@@ -96,6 +126,7 @@ class LHC::Request
96
126
  options.delete(:url)
97
127
  options.each do |key, _v|
98
128
  next if TYPHOEUS_OPTIONS.include? key
129
+
99
130
  method = "#{key}="
100
131
  options.delete key unless easy.respond_to?(method)
101
132
  end
@@ -107,6 +138,7 @@ class LHC::Request
107
138
  def use_configured_endpoint!
108
139
  endpoint = LHC.config.endpoints[options[:url]]
109
140
  return unless endpoint
141
+
110
142
  # explicit options override endpoint options
111
143
  new_options = endpoint.options.deep_merge(options)
112
144
  # set new options
@@ -128,13 +160,14 @@ class LHC::Request
128
160
  end
129
161
 
130
162
  def on_complete(response)
131
- self.response = LHC::Response.new(response, self)
163
+ self.response = response.is_a?(LHC::Response) ? response : LHC::Response.new(response, self)
132
164
  interceptors.intercept(:after_response)
133
165
  handle_error(self.response) unless self.response.success?
134
166
  end
135
167
 
136
168
  def handle_error(response)
137
169
  return if ignore_error?
170
+
138
171
  throw_error(response) unless error_handler
139
172
  response.body_replacement = error_handler.call(response)
140
173
  end
data/lib/lhc/response.rb CHANGED
@@ -50,6 +50,7 @@ class LHC::Response
50
50
 
51
51
  def format
52
52
  return LHC::Formats::JSON.new if request.nil?
53
+
53
54
  request.format
54
55
  end
55
56
 
@@ -18,7 +18,7 @@ class LHC::Response::Data
18
18
  end
19
19
  end
20
20
 
21
- def method_missing(method, *args, &block) # rubocop:disable Style/MethodMissingSuper
21
+ def method_missing(method, *args, &block)
22
22
  @base.send(method, *args, &block)
23
23
  end
24
24
 
data/lib/lhc/rspec.rb CHANGED
@@ -3,9 +3,8 @@
3
3
  require 'lhc'
4
4
 
5
5
  RSpec.configure do |config|
6
- LHC::Caching.cache = ActiveSupport::Cache::MemoryStore.new
7
-
8
6
  config.before(:each) do
7
+ LHC::Caching.cache = ActiveSupport::Cache::MemoryStore.new
9
8
  LHC::Caching.cache.clear
10
9
  LHC::Throttle.track = nil
11
10
  end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ class LHC::Scrubber
4
+ attr_accessor :scrubbed
5
+
6
+ SCRUB_DISPLAY = '[FILTERED]'
7
+
8
+ def initialize(data)
9
+ @scrubbed = data.deep_dup
10
+ end
11
+
12
+ private
13
+
14
+ def scrub_auth_elements
15
+ LHC.config.scrubs.dig(:auth)
16
+ end
17
+
18
+ def scrub!
19
+ return if scrub_elements.blank?
20
+ return if scrubbed.blank?
21
+
22
+ LHC::Scrubber.scrub_hash!(scrub_elements, scrubbed) if scrubbed.is_a?(Hash)
23
+ LHC::Scrubber.scrub_array!(scrub_elements, scrubbed) if scrubbed.is_a?(Array)
24
+ end
25
+
26
+ def self.scrub_array!(scrub_elements, scrubbed)
27
+ scrubbed.each do |scrubbed_hash|
28
+ LHC::Scrubber.scrub_hash!(scrub_elements, scrubbed_hash)
29
+ end
30
+ end
31
+
32
+ def self.scrub_hash!(scrub_elements, scrubbed)
33
+ scrub_elements.each do |scrub_element|
34
+ if scrubbed.key?(scrub_element.to_s)
35
+ key = scrub_element.to_s
36
+ elsif scrubbed.key?(scrub_element.to_sym)
37
+ key = scrub_element.to_sym
38
+ end
39
+ next if key.blank?
40
+
41
+ scrubbed[key] = SCRUB_DISPLAY
42
+ end
43
+ scrubbed.values.each { |v| LHC::Scrubber.scrub_hash!(scrub_elements, v) if v.instance_of?(Hash) }
44
+ end
45
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ class LHC::AuthScrubber < LHC::Scrubber
4
+ def initialize(data)
5
+ super(data)
6
+ scrub_auth_options!
7
+ end
8
+
9
+ private
10
+
11
+ def scrub_auth_options!
12
+ return if scrubbed.blank?
13
+ return if scrub_auth_elements.blank?
14
+
15
+ scrub_basic_auth_options! if scrub_auth_elements.include?(:basic)
16
+ scrub_bearer_auth_options! if scrub_auth_elements.include?(:bearer)
17
+ end
18
+
19
+ def scrub_basic_auth_options!
20
+ return if scrubbed[:basic].blank?
21
+
22
+ scrubbed[:basic][:username] = SCRUB_DISPLAY
23
+ scrubbed[:basic][:password] = SCRUB_DISPLAY
24
+ scrubbed[:basic][:base_64_encoded_credentials] = SCRUB_DISPLAY
25
+ end
26
+
27
+ def scrub_bearer_auth_options!
28
+ return if scrubbed[:bearer].blank?
29
+
30
+ scrubbed[:bearer_token] = SCRUB_DISPLAY
31
+ end
32
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ class LHC::BodyScrubber < LHC::Scrubber
4
+ def initialize(data)
5
+ super(data)
6
+ parse!
7
+ scrub!
8
+ end
9
+
10
+ private
11
+
12
+ def scrub_elements
13
+ LHC.config.scrubs[:body]
14
+ end
15
+
16
+ def parse!
17
+ return if scrubbed.nil?
18
+ return if scrubbed.is_a?(Hash)
19
+ return if scrubbed.is_a?(Array)
20
+
21
+ if scrubbed.is_a?(String)
22
+ json = scrubbed
23
+ else
24
+ json = scrubbed.to_json
25
+ end
26
+
27
+ parsed = JSON.parse(json)
28
+ self.scrubbed = parsed if parsed.is_a?(Hash) || parsed.is_a?(Array)
29
+ end
30
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ class LHC::HeadersScrubber < LHC::Scrubber
4
+ def initialize(data, auth_options)
5
+ super(data)
6
+ @auth_options = auth_options
7
+ scrub!
8
+ scrub_auth_headers!
9
+ end
10
+
11
+ private
12
+
13
+ attr_reader :auth_options
14
+
15
+ def scrub_elements
16
+ LHC.config.scrubs[:headers]
17
+ end
18
+
19
+ def scrub_auth_headers!
20
+ return if scrub_auth_elements.blank?
21
+ return if auth_options.blank?
22
+
23
+ scrub_basic_authentication_headers! if scrub_auth_elements.include?(:basic)
24
+ scrub_bearer_authentication_headers! if scrub_auth_elements.include?(:bearer)
25
+ end
26
+
27
+ def scrub_basic_authentication_headers!
28
+ return if auth_options[:basic].blank?
29
+ return if scrubbed['Authorization'].blank?
30
+
31
+ scrubbed['Authorization'] = scrubbed['Authorization'].gsub(auth_options[:basic][:base_64_encoded_credentials], SCRUB_DISPLAY)
32
+ end
33
+
34
+ def scrub_bearer_authentication_headers!
35
+ return if @auth_options[:bearer].blank?
36
+ return if @scrubbed['Authorization'].blank?
37
+
38
+ @scrubbed['Authorization'] = scrubbed['Authorization'].gsub(auth_options[:bearer_token], SCRUB_DISPLAY)
39
+ end
40
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class LHC::ParamsScrubber < LHC::Scrubber
4
+ def initialize(data)
5
+ super(data)
6
+ scrub!
7
+ end
8
+
9
+ private
10
+
11
+ def scrub_elements
12
+ LHC.config.scrubs[:params]
13
+ end
14
+ end
data/lib/lhc/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LHC
4
- VERSION ||= '12.3.0'
4
+ VERSION ||= '13.4.0-pro1766.1'
5
5
  end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe LHC do
6
+ it 'has a default value for scrubs' do
7
+ expect(LHC.config.scrubs[:auth]).to eq [:bearer, :basic]
8
+ expect(LHC.config.scrubs[:params]).to eq []
9
+ expect(LHC.config.scrubs[:headers]).to eq []
10
+ expect(LHC.config.scrubs[:body]).to eq ['password', 'password_confirmation']
11
+ end
12
+
13
+ describe 'auth' do
14
+ context 'when only bearer auth should get scrubbed' do
15
+ before(:each) do
16
+ LHC.configure do |c|
17
+ c.scrubs[:auth] = [:bearer]
18
+ end
19
+ end
20
+
21
+ it 'has only bearer auth in scrubs' do
22
+ expect(LHC.config.scrubs[:auth]).to eq([:bearer])
23
+ expect(LHC.config.scrubs[:params]).to eq []
24
+ expect(LHC.config.scrubs[:headers]).to eq []
25
+ expect(LHC.config.scrubs[:body]).to eq ['password', 'password_confirmation']
26
+ end
27
+ end
28
+ end
29
+
30
+ context 'params' do
31
+ context 'when additional param "api_key" should be scrubbed' do
32
+ before(:each) do
33
+ LHC.configure do |c|
34
+ c.scrubs[:params] << 'api_key'
35
+ end
36
+ end
37
+
38
+ it 'has "api_key" in scrubs' do
39
+ expect(LHC.config.scrubs[:auth]).to eq [:bearer, :basic]
40
+ expect(LHC.config.scrubs[:params]).to eq ['api_key']
41
+ expect(LHC.config.scrubs[:headers]).to eq []
42
+ expect(LHC.config.scrubs[:body]).to eq ['password', 'password_confirmation']
43
+ end
44
+ end
45
+ end
46
+
47
+ context 'headers' do
48
+ context 'when additional header "private_key" should be scrubbed' do
49
+ before(:each) do
50
+ LHC.configure do |c|
51
+ c.scrubs[:headers] << 'private_key'
52
+ end
53
+ end
54
+
55
+ it 'has "private_key" in scrubs' do
56
+ expect(LHC.config.scrubs[:auth]).to eq [:bearer, :basic]
57
+ expect(LHC.config.scrubs[:params]).to eq []
58
+ expect(LHC.config.scrubs[:headers]).to eq ['private_key']
59
+ expect(LHC.config.scrubs[:body]).to eq ['password', 'password_confirmation']
60
+ end
61
+ end
62
+ end
63
+
64
+ context 'body' do
65
+ context 'when only password should get scrubbed' do
66
+ before(:each) do
67
+ LHC.configure do |c|
68
+ c.scrubs[:body] = ['password']
69
+ end
70
+ end
71
+
72
+ it 'has password in scrubs' do
73
+ expect(LHC.config.scrubs[:auth]).to eq [:bearer, :basic]
74
+ expect(LHC.config.scrubs[:params]).to eq []
75
+ expect(LHC.config.scrubs[:headers]).to eq []
76
+ expect(LHC.config.scrubs[:body]).to eq(['password'])
77
+ end
78
+ end
79
+
80
+ context 'when "user_token" should be scrubbed' do
81
+ before(:each) do
82
+ LHC.configure do |c|
83
+ c.scrubs[:body] << 'user_token'
84
+ end
85
+ end
86
+
87
+ it 'has user_token in scrubs' do
88
+ expect(LHC.config.scrubs[:auth]).to eq [:bearer, :basic]
89
+ expect(LHC.config.scrubs[:params]).to eq []
90
+ expect(LHC.config.scrubs[:headers]).to eq []
91
+ expect(LHC.config.scrubs[:body]).to eq(['password', 'password_confirmation', 'user_token'])
92
+ end
93
+ end
94
+ end
95
+
96
+ context 'when nothing should be scrubbed' do
97
+ before(:each) do
98
+ LHC.configure do |c|
99
+ c.scrubs = {}
100
+ end
101
+ end
102
+
103
+ it 'does not have scrubs' do
104
+ expect(LHC.config.scrubs.blank?).to be true
105
+ expect(LHC.config.scrubs[:auth]).to be nil
106
+ end
107
+ end
108
+ end