lhc 13.0.0 → 14.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) 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/README.md +44 -0
  7. data/Rakefile +3 -3
  8. data/lhc.gemspec +3 -1
  9. data/lib/lhc.rb +59 -59
  10. data/lib/lhc/concerns/lhc/fix_invalid_encoding_concern.rb +1 -0
  11. data/lib/lhc/config.rb +3 -0
  12. data/lib/lhc/endpoint.rb +3 -0
  13. data/lib/lhc/error.rb +5 -1
  14. data/lib/lhc/interceptor.rb +4 -0
  15. data/lib/lhc/interceptors.rb +1 -0
  16. data/lib/lhc/interceptors/auth.rb +3 -4
  17. data/lib/lhc/interceptors/caching.rb +14 -3
  18. data/lib/lhc/interceptors/logging.rb +2 -0
  19. data/lib/lhc/interceptors/monitoring.rb +46 -11
  20. data/lib/lhc/interceptors/retry.rb +2 -0
  21. data/lib/lhc/interceptors/rollbar.rb +1 -0
  22. data/lib/lhc/interceptors/throttle.rb +7 -2
  23. data/lib/lhc/interceptors/zipkin.rb +2 -0
  24. data/lib/lhc/request.rb +13 -3
  25. data/lib/lhc/response.rb +1 -0
  26. data/lib/lhc/response/data.rb +1 -1
  27. data/lib/lhc/version.rb +1 -1
  28. data/spec/error/to_s_spec.rb +7 -2
  29. data/spec/formats/multipart_spec.rb +2 -2
  30. data/spec/formats/plain_spec.rb +1 -1
  31. data/spec/interceptors/after_response_spec.rb +1 -1
  32. data/spec/interceptors/caching/main_spec.rb +2 -2
  33. data/spec/interceptors/caching/multilevel_cache_spec.rb +2 -1
  34. data/spec/interceptors/define_spec.rb +1 -0
  35. data/spec/interceptors/monitoring/caching_spec.rb +66 -0
  36. data/spec/interceptors/response_competition_spec.rb +2 -2
  37. data/spec/interceptors/return_response_spec.rb +2 -2
  38. data/spec/response/data_spec.rb +2 -2
  39. data/spec/support/zipkin_mock.rb +1 -0
  40. metadata +27 -21
  41. data/.rubocop.localch.yml +0 -325
  42. data/cider-ci.yml +0 -5
  43. data/cider-ci/bin/bundle +0 -51
  44. data/cider-ci/bin/ruby_install +0 -8
  45. data/cider-ci/bin/ruby_version +0 -25
  46. data/cider-ci/jobs/rspec-activesupport-5.yml +0 -27
  47. data/cider-ci/jobs/rspec-activesupport-6.yml +0 -28
  48. data/cider-ci/jobs/rubocop.yml +0 -18
  49. data/cider-ci/task_components/bundle.yml +0 -22
  50. data/cider-ci/task_components/rspec.yml +0 -36
  51. data/cider-ci/task_components/rubocop.yml +0 -29
  52. data/cider-ci/task_components/ruby.yml +0 -15
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- ruby-2.6.5
1
+ ruby-2.7.2
data/README.md CHANGED
@@ -73,6 +73,10 @@ use it like:
73
73
  * [Installation](#installation-1)
74
74
  * [Environment](#environment)
75
75
  * [What it tracks](#what-it-tracks)
76
+ * [Before and after request tracking](#before-and-after-request-tracking)
77
+ * [Response tracking](#response-tracking)
78
+ * [Timeout tracking](#timeout-tracking)
79
+ * [Caching tracking](#caching-tracking)
76
80
  * [Configure](#configure-1)
77
81
  * [Prometheus Interceptor](#prometheus-interceptor)
78
82
  * [Retry Interceptor](#retry-interceptor)
@@ -95,6 +99,7 @@ use it like:
95
99
 
96
100
 
97
101
 
102
+
98
103
  ## Basic methods
99
104
 
100
105
  Available are `get`, `post`, `put` & `delete`.
@@ -744,11 +749,15 @@ It tracks request attempts with `before_request` and `after_request` (counts).
744
749
  In case your workers/processes are getting killed due limited time constraints,
745
750
  you are able to detect deltas with relying on "before_request", and "after_request" counts:
746
751
 
752
+ ###### Before and after request tracking
753
+
747
754
  ```ruby
748
755
  "lhc.<app_name>.<env>.<host>.<http_method>.before_request", 1
749
756
  "lhc.<app_name>.<env>.<host>.<http_method>.after_request", 1
750
757
  ```
751
758
 
759
+ ###### Response tracking
760
+
752
761
  In case of a successful response it reports the response code with a count and the response time with a gauge value.
753
762
 
754
763
  ```ruby
@@ -759,6 +768,17 @@ In case of a successful response it reports the response code with a count and t
759
768
  "lhc.<app_name>.<env>.<host>.<http_method>.time", 43
760
769
  ```
761
770
 
771
+ In case of a unsuccessful response it reports the response code with a count but no time:
772
+
773
+ ```ruby
774
+ LHC.get('http://local.ch')
775
+
776
+ "lhc.<app_name>.<env>.<host>.<http_method>.count", 1
777
+ "lhc.<app_name>.<env>.<host>.<http_method>.500", 1
778
+ ```
779
+
780
+ ###### Timeout tracking
781
+
762
782
  Timeouts are also reported:
763
783
 
764
784
  ```ruby
@@ -767,6 +787,30 @@ Timeouts are also reported:
767
787
 
768
788
  All the dots in the host are getting replaced with underscore, because dot is the default separator in graphite.
769
789
 
790
+ ###### Caching tracking
791
+
792
+ When you want to track caching stats please make sure you have enabled the `LHC::Caching` and the `LHC::Monitoring` interceptor.
793
+
794
+ Make sure that the `LHC::Caching` is listed before `LHC::Monitoring` interceptor when configuring interceptors:
795
+
796
+ ```ruby
797
+ LHC.configure do |c|
798
+ c.interceptors = [LHC::Caching, LHC::Monitoring]
799
+ end
800
+ ```
801
+
802
+ If a response was served from cache it tracks:
803
+
804
+ ```ruby
805
+ "lhc.<app_name>.<env>.<host>.<http_method>.cache.hit", 1
806
+ ```
807
+
808
+ If a response was not served from cache it tracks:
809
+
810
+ ```ruby
811
+ "lhc.<app_name>.<env>.<host>.<http_method>.cache.miss", 1
812
+ ```
813
+
770
814
  ##### Configure
771
815
 
772
816
  It is possible to set the key for Monitoring Interceptor on per request basis:
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'bundler/setup'
3
5
  rescue LoadError
@@ -17,9 +19,7 @@ end
17
19
  begin
18
20
  require 'rspec/core/rake_task'
19
21
  RSpec::Core::RakeTask.new(:spec)
20
- task :default => :spec
21
- rescue LoadError
22
- # no rspec available
22
+ task default: :spec
23
23
  end
24
24
 
25
25
  Bundler::GemHelper.install_tasks
data/lhc.gemspec CHANGED
@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
20
20
  s.require_paths = ['lib']
21
21
 
22
22
  s.requirements << 'Ruby >= 2.0.0'
23
+ s.required_ruby_version = '>= 2.7' # Needed for rubocop
23
24
 
24
25
  s.add_dependency 'activesupport', '>= 5.2'
25
26
  s.add_dependency 'addressable'
@@ -31,7 +32,8 @@ Gem::Specification.new do |s|
31
32
  s.add_development_dependency 'rails', '>= 5.2'
32
33
  s.add_development_dependency 'redis'
33
34
  s.add_development_dependency 'rspec-rails', '>= 3.0.0'
34
- s.add_development_dependency 'rubocop', '~> 0.57.1'
35
+ s.add_development_dependency 'rubocop', '~> 1.0'
36
+ s.add_development_dependency 'rubocop-performance', '~> 1.0'
35
37
  s.add_development_dependency 'rubocop-rspec', '~> 1.26.0'
36
38
  s.add_development_dependency 'timecop'
37
39
  s.add_development_dependency 'webmock'
data/lib/lhc.rb CHANGED
@@ -6,131 +6,131 @@ require 'active_support/core_ext/hash/keys'
6
6
 
7
7
  module LHC
8
8
  autoload :BasicMethodsConcern,
9
- 'lhc/concerns/lhc/basic_methods_concern'
9
+ 'lhc/concerns/lhc/basic_methods_concern'
10
10
  autoload :ConfigurationConcern,
11
- 'lhc/concerns/lhc/configuration_concern'
11
+ 'lhc/concerns/lhc/configuration_concern'
12
12
  autoload :FixInvalidEncodingConcern,
13
- 'lhc/concerns/lhc/fix_invalid_encoding_concern'
13
+ 'lhc/concerns/lhc/fix_invalid_encoding_concern'
14
14
  autoload :FormatsConcern,
15
- 'lhc/concerns/lhc/formats_concern'
15
+ 'lhc/concerns/lhc/formats_concern'
16
16
 
17
17
  include BasicMethodsConcern
18
18
  include ConfigurationConcern
19
19
  include FormatsConcern
20
20
 
21
21
  autoload :Auth,
22
- 'lhc/interceptors/auth'
22
+ 'lhc/interceptors/auth'
23
23
  autoload :Caching,
24
- 'lhc/interceptors/caching'
24
+ 'lhc/interceptors/caching'
25
25
  autoload :DefaultTimeout,
26
- 'lhc/interceptors/default_timeout'
26
+ 'lhc/interceptors/default_timeout'
27
27
  autoload :Logging,
28
- 'lhc/interceptors/logging'
28
+ 'lhc/interceptors/logging'
29
29
  autoload :Prometheus,
30
- 'lhc/interceptors/prometheus'
30
+ 'lhc/interceptors/prometheus'
31
31
  autoload :Retry,
32
- 'lhc/interceptors/retry'
32
+ 'lhc/interceptors/retry'
33
33
  autoload :Throttle,
34
- 'lhc/interceptors/throttle'
34
+ 'lhc/interceptors/throttle'
35
35
 
36
36
  autoload :Config,
37
- 'lhc/config'
37
+ 'lhc/config'
38
38
  autoload :Endpoint,
39
- 'lhc/endpoint'
39
+ 'lhc/endpoint'
40
40
 
41
41
  autoload :Error,
42
- 'lhc/error'
42
+ 'lhc/error'
43
43
  autoload :ClientError,
44
- 'lhc/errors/client_error'
44
+ 'lhc/errors/client_error'
45
45
  autoload :BadRequest,
46
- 'lhc/errors/client_error'
46
+ 'lhc/errors/client_error'
47
47
  autoload :Unauthorized,
48
- 'lhc/errors/client_error'
48
+ 'lhc/errors/client_error'
49
49
  autoload :PaymentRequired,
50
- 'lhc/errors/client_error'
50
+ 'lhc/errors/client_error'
51
51
  autoload :Forbidden,
52
- 'lhc/errors/client_error'
52
+ 'lhc/errors/client_error'
53
53
  autoload :Forbidden,
54
- 'lhc/errors/client_error'
54
+ 'lhc/errors/client_error'
55
55
  autoload :NotFound,
56
- 'lhc/errors/client_error'
56
+ 'lhc/errors/client_error'
57
57
  autoload :MethodNotAllowed,
58
- 'lhc/errors/client_error'
58
+ 'lhc/errors/client_error'
59
59
  autoload :NotAcceptable,
60
- 'lhc/errors/client_error'
60
+ 'lhc/errors/client_error'
61
61
  autoload :ProxyAuthenticationRequired,
62
- 'lhc/errors/client_error'
62
+ 'lhc/errors/client_error'
63
63
  autoload :RequestTimeout,
64
- 'lhc/errors/client_error'
64
+ 'lhc/errors/client_error'
65
65
  autoload :Conflict,
66
- 'lhc/errors/client_error'
66
+ 'lhc/errors/client_error'
67
67
  autoload :Gone,
68
- 'lhc/errors/client_error'
68
+ 'lhc/errors/client_error'
69
69
  autoload :LengthRequired,
70
- 'lhc/errors/client_error'
70
+ 'lhc/errors/client_error'
71
71
  autoload :PreconditionFailed,
72
- 'lhc/errors/client_error'
72
+ 'lhc/errors/client_error'
73
73
  autoload :RequestEntityTooLarge,
74
- 'lhc/errors/client_error'
74
+ 'lhc/errors/client_error'
75
75
  autoload :RequestUriToLong,
76
- 'lhc/errors/client_error'
76
+ 'lhc/errors/client_error'
77
77
  autoload :UnsupportedMediaType,
78
- 'lhc/errors/client_error'
78
+ 'lhc/errors/client_error'
79
79
  autoload :RequestedRangeNotSatisfiable,
80
- 'lhc/errors/client_error'
80
+ 'lhc/errors/client_error'
81
81
  autoload :ExpectationFailed,
82
- 'lhc/errors/client_error'
82
+ 'lhc/errors/client_error'
83
83
  autoload :UnprocessableEntity,
84
- 'lhc/errors/client_error'
84
+ 'lhc/errors/client_error'
85
85
  autoload :Locked,
86
- 'lhc/errors/client_error'
86
+ 'lhc/errors/client_error'
87
87
  autoload :FailedDependency,
88
- 'lhc/errors/client_error'
88
+ 'lhc/errors/client_error'
89
89
  autoload :UpgradeRequired,
90
- 'lhc/errors/client_error'
90
+ 'lhc/errors/client_error'
91
91
  autoload :ParserError,
92
- 'lhc/errors/parser_error'
92
+ 'lhc/errors/parser_error'
93
93
  autoload :ServerError,
94
- 'lhc/errors/server_error'
94
+ 'lhc/errors/server_error'
95
95
  autoload :InternalServerError,
96
- 'lhc/errors/server_error'
96
+ 'lhc/errors/server_error'
97
97
  autoload :NotImplemented,
98
- 'lhc/errors/server_error'
98
+ 'lhc/errors/server_error'
99
99
  autoload :BadGateway,
100
- 'lhc/errors/server_error'
100
+ 'lhc/errors/server_error'
101
101
  autoload :ServiceUnavailable,
102
- 'lhc/errors/server_error'
102
+ 'lhc/errors/server_error'
103
103
  autoload :GatewayTimeout,
104
- 'lhc/errors/server_error'
104
+ 'lhc/errors/server_error'
105
105
  autoload :HttpVersionNotSupported,
106
- 'lhc/errors/server_error'
106
+ 'lhc/errors/server_error'
107
107
  autoload :InsufficientStorage,
108
- 'lhc/errors/server_error'
108
+ 'lhc/errors/server_error'
109
109
  autoload :NotExtended,
110
- 'lhc/errors/server_error'
110
+ 'lhc/errors/server_error'
111
111
  autoload :Timeout,
112
- 'lhc/errors/timeout'
112
+ 'lhc/errors/timeout'
113
113
  autoload :UnknownError,
114
- 'lhc/errors/unknown_error'
114
+ 'lhc/errors/unknown_error'
115
115
 
116
116
  autoload :Interceptor,
117
- 'lhc/interceptor'
117
+ 'lhc/interceptor'
118
118
  autoload :Interceptors,
119
- 'lhc/interceptors'
119
+ 'lhc/interceptors'
120
120
  autoload :Formats,
121
- 'lhc/formats'
121
+ 'lhc/formats'
122
122
  autoload :Format,
123
- 'lhc/format'
123
+ 'lhc/format'
124
124
  autoload :Monitoring,
125
- 'lhc/interceptors/monitoring'
125
+ 'lhc/interceptors/monitoring'
126
126
  autoload :Request,
127
- 'lhc/request'
127
+ 'lhc/request'
128
128
  autoload :Response,
129
- 'lhc/response'
129
+ 'lhc/response'
130
130
  autoload :Rollbar,
131
- 'lhc/interceptors/rollbar'
131
+ 'lhc/interceptors/rollbar'
132
132
  autoload :Zipkin,
133
- 'lhc/interceptors/zipkin'
133
+ 'lhc/interceptors/zipkin'
134
134
 
135
135
  require 'lhc/railtie' if defined?(Rails)
136
136
  end
@@ -12,6 +12,7 @@ module LHC
12
12
  # an empty string is returned instead
13
13
  def fix_invalid_encoding(string)
14
14
  return string unless string.is_a?(String)
15
+
15
16
  result = string.dup
16
17
 
17
18
  # we assume it's ISO-8859-1 first
data/lib/lhc/config.rb CHANGED
@@ -13,6 +13,7 @@ class LHC::Config
13
13
  def endpoint(name, url, options = {})
14
14
  name = name.to_sym
15
15
  raise 'Endpoint already exists for that name' if @endpoints[name]
16
+
16
17
  @endpoints[name] = LHC::Endpoint.new(url, options)
17
18
  end
18
19
 
@@ -23,6 +24,7 @@ class LHC::Config
23
24
  def placeholder(name, value)
24
25
  name = name.to_sym
25
26
  raise 'Placeholder already exists for that name' if @placeholders[name]
27
+
26
28
  @placeholders[name] = value
27
29
  end
28
30
 
@@ -36,6 +38,7 @@ class LHC::Config
36
38
 
37
39
  def interceptors=(interceptors)
38
40
  raise 'Default interceptors already set and can only be set once' if @interceptors
41
+
39
42
  @interceptors = interceptors
40
43
  end
41
44
 
data/lib/lhc/endpoint.rb CHANGED
@@ -55,6 +55,7 @@ class LHC::Endpoint
55
55
  # Example: {+datastore}/contracts/{id} == http://local.ch/contracts/1
56
56
  def match?(url)
57
57
  return true if url == uri.pattern
58
+
58
59
  match_data = match_data(url)
59
60
  return false if match_data.nil?
60
61
 
@@ -75,6 +76,7 @@ class LHC::Endpoint
75
76
  def values_as_params(url)
76
77
  match_data = match_data(url)
77
78
  return if match_data.nil?
79
+
78
80
  Hash[match_data.variables.map(&:to_sym).zip(match_data.values)]
79
81
  end
80
82
 
@@ -103,6 +105,7 @@ class LHC::Endpoint
103
105
  # creates params according to template
104
106
  def self.values_as_params(template, url)
105
107
  raise("#{url} does not match the template: #{template}") if !match?(url, template)
108
+
106
109
  new(template).values_as_params(url)
107
110
  end
108
111
 
data/lib/lhc/error.rb CHANGED
@@ -43,6 +43,7 @@ class LHC::Error < StandardError
43
43
 
44
44
  def self.find(response)
45
45
  return LHC::Timeout if response.timeout?
46
+
46
47
  status_code = response.code.to_s[0..2].to_i
47
48
  error = map[status_code]
48
49
  error ||= LHC::UnknownError
@@ -64,8 +65,11 @@ class LHC::Error < StandardError
64
65
  end
65
66
 
66
67
  def to_s
67
- return response if response.is_a?(String)
68
+ return response.to_s unless response.is_a?(LHC::Response)
69
+
68
70
  request = response.request
71
+ return unless request.is_a?(LHC::Request)
72
+
69
73
  debug = []
70
74
  debug << [request.method, request.url].map { |str| self.class.fix_invalid_encoding(str) }.join(' ')
71
75
  debug << "Options: #{request.options}"
@@ -29,4 +29,8 @@ class LHC::Interceptor
29
29
  def self.dup
30
30
  self
31
31
  end
32
+
33
+ def all_interceptor_classes
34
+ @all_interceptors ||= LHC::Interceptors.new(request).all.map(&:class)
35
+ end
32
36
  end
@@ -19,6 +19,7 @@ class LHC::Interceptors
19
19
  result = interceptor.send(name)
20
20
  if result.is_a? LHC::Response
21
21
  raise 'Response already set from another interceptor' if @response
22
+
22
23
  @response = interceptor.request.response = result
23
24
  end
24
25
  end
@@ -16,6 +16,7 @@ class LHC::Auth < LHC::Interceptor
16
16
  def after_response
17
17
  return unless configuration_correct?
18
18
  return unless reauthenticate?
19
+
19
20
  reauthenticate!
20
21
  end
21
22
 
@@ -75,10 +76,6 @@ class LHC::Auth < LHC::Interceptor
75
76
  @refresh_client_token_option ||= auth_options[:refresh_client_token] || refresh_client_token
76
77
  end
77
78
 
78
- def all_interceptor_classes
79
- @all_interceptors ||= LHC::Interceptors.new(request).all.map(&:class)
80
- end
81
-
82
79
  def auth_options
83
80
  request.options[:auth] || {}
84
81
  end
@@ -90,11 +87,13 @@ class LHC::Auth < LHC::Interceptor
90
87
 
91
88
  def refresh_client_token?
92
89
  return true if refresh_client_token_option.is_a?(Proc)
90
+
93
91
  warn("[WARNING] The given refresh_client_token must be a Proc for reauthentication.")
94
92
  end
95
93
 
96
94
  def retry_interceptor?
97
95
  return true if all_interceptor_classes.include?(LHC::Retry) && all_interceptor_classes.index(LHC::Retry) > all_interceptor_classes.index(self.class)
96
+
98
97
  warn("[WARNING] Your interceptors must include LHC::Retry after LHC::Auth.")
99
98
  end
100
99
  end
@@ -41,15 +41,16 @@ class LHC::Caching < LHC::Interceptor
41
41
 
42
42
  def before_request
43
43
  return unless cache?(request)
44
- key = key(request, options[:key])
45
- response_data = multilevel_cache.fetch(key)
46
- return unless response_data
44
+ return if response_data.blank?
45
+
47
46
  from_cache(request, response_data)
48
47
  end
49
48
 
50
49
  def after_response
51
50
  return unless response.success?
52
51
  return unless cache?(request)
52
+ return if response_data.present?
53
+
53
54
  multilevel_cache.write(
54
55
  key(request, options[:key]),
55
56
  to_cache(response),
@@ -59,6 +60,14 @@ class LHC::Caching < LHC::Interceptor
59
60
 
60
61
  private
61
62
 
63
+ # from cache
64
+ def response_data
65
+ # stop calling multi-level cache if it already returned nil for this interceptor instance
66
+ return @response_data if defined? @response_data
67
+
68
+ @response_data ||= multilevel_cache.fetch(key(request, options[:key]))
69
+ end
70
+
62
71
  # performs read/write (fetch/write) on all configured cache levels (e.g. local & central)
63
72
  def multilevel_cache
64
73
  MultilevelCache.new(
@@ -75,6 +84,7 @@ class LHC::Caching < LHC::Interceptor
75
84
 
76
85
  def central_cache
77
86
  return nil if central.blank? || (central[:read].blank? && central[:write].blank?)
87
+
78
88
  {}.tap do |options|
79
89
  options[:read] = ActiveSupport::Cache::RedisCacheStore.new(url: central[:read]) if central[:read].present?
80
90
  options[:write] = ActiveSupport::Cache::RedisCacheStore.new(url: central[:write]) if central[:write].present?
@@ -86,6 +96,7 @@ class LHC::Caching < LHC::Interceptor
86
96
  # return false if this interceptor cannot work
87
97
  def cache?(request)
88
98
  return false unless request.options[:cache]
99
+
89
100
  (local_cache || central_cache) &&
90
101
  cached_method?(request.method, options[:methods])
91
102
  end