gruf 2.2.2 → 2.9.1

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.
Files changed (49) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +107 -12
  3. data/CODE_OF_CONDUCT.md +38 -41
  4. data/README.md +8 -360
  5. data/bin/gruf +2 -12
  6. data/gruf.gemspec +31 -7
  7. data/lib/gruf/cli/executor.rb +102 -0
  8. data/lib/gruf/client/error.rb +68 -0
  9. data/lib/gruf/client/error_factory.rb +105 -0
  10. data/lib/gruf/client.rb +52 -38
  11. data/lib/gruf/configuration.rb +25 -5
  12. data/lib/gruf/controllers/base.rb +35 -12
  13. data/lib/gruf/controllers/request.rb +21 -10
  14. data/lib/gruf/controllers/service_binder.rb +38 -6
  15. data/lib/gruf/error.rb +34 -9
  16. data/lib/gruf/errors/debug_info.rb +9 -2
  17. data/lib/gruf/errors/field.rb +2 -0
  18. data/lib/gruf/errors/helpers.rb +5 -0
  19. data/lib/gruf/hooks/base.rb +34 -0
  20. data/lib/gruf/hooks/executor.rb +47 -0
  21. data/lib/gruf/hooks/registry.rb +159 -0
  22. data/lib/gruf/instrumentable_grpc_server.rb +64 -0
  23. data/lib/gruf/integrations/rails/railtie.rb +10 -0
  24. data/lib/gruf/interceptors/active_record/connection_reset.rb +4 -3
  25. data/lib/gruf/interceptors/authentication/basic.rb +10 -2
  26. data/lib/gruf/interceptors/base.rb +3 -0
  27. data/lib/gruf/interceptors/client_interceptor.rb +117 -0
  28. data/lib/gruf/interceptors/context.rb +6 -4
  29. data/lib/gruf/interceptors/instrumentation/output_metadata_timer.rb +5 -4
  30. data/lib/gruf/interceptors/instrumentation/request_logging/formatters/base.rb +5 -1
  31. data/lib/gruf/interceptors/instrumentation/request_logging/formatters/logstash.rb +5 -1
  32. data/lib/gruf/interceptors/instrumentation/request_logging/formatters/plain.rb +5 -1
  33. data/lib/gruf/interceptors/instrumentation/request_logging/interceptor.rb +60 -29
  34. data/lib/gruf/interceptors/instrumentation/statsd.rb +5 -4
  35. data/lib/gruf/interceptors/registry.rb +6 -1
  36. data/lib/gruf/interceptors/server_interceptor.rb +2 -0
  37. data/lib/gruf/interceptors/timer.rb +12 -2
  38. data/lib/gruf/loggable.rb +2 -0
  39. data/lib/gruf/logging.rb +2 -0
  40. data/lib/gruf/outbound/request_context.rb +71 -0
  41. data/lib/gruf/response.rb +5 -2
  42. data/lib/gruf/serializers/errors/base.rb +2 -0
  43. data/lib/gruf/serializers/errors/json.rb +2 -0
  44. data/lib/gruf/server.rb +57 -23
  45. data/lib/gruf/synchronized_client.rb +97 -0
  46. data/lib/gruf/timer.rb +9 -6
  47. data/lib/gruf/version.rb +3 -1
  48. data/lib/gruf.rb +10 -0
  49. metadata +254 -17
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
@@ -22,7 +24,9 @@ module Gruf
22
24
  # Handles basic authentication for gRPC requests
23
25
  #
24
26
  class Basic < Gruf::Interceptors::ServerInterceptor
25
-
27
+ ##
28
+ # Validate authentication
29
+ #
26
30
  def call
27
31
  fail!(:unauthenticated, :unauthenticated) unless bypass? || valid?
28
32
  yield
@@ -63,7 +67,11 @@ module Gruf
63
67
  # @return [String] The decoded request credentials
64
68
  #
65
69
  def request_credentials
66
- credentials = request.active_call.respond_to?(:metadata) ? request.active_call.metadata['authorization'].to_s : ''
70
+ credentials = if request.active_call.respond_to?(:metadata)
71
+ request.active_call.metadata['authorization'].to_s
72
+ else
73
+ ''
74
+ end
67
75
  Base64.decode64(credentials.to_s.gsub('Basic ', '').strip)
68
76
  end
69
77
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
@@ -40,6 +42,7 @@ module Gruf
40
42
  end
41
43
  end
42
44
 
45
+ require_relative 'client_interceptor'
43
46
  require_relative 'server_interceptor'
44
47
  require_relative 'context'
45
48
  require_relative 'timer'
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
6
+ # documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
7
+ # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
8
+ # persons to whom the Software is furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
11
+ # Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
14
+ # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
15
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
+ # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
+ #
18
+ module Gruf
19
+ module Interceptors
20
+ ##
21
+ # Intercepts outbound client requests to provide a unified interface and request context
22
+ #
23
+ class ClientInterceptor < GRPC::ClientInterceptor
24
+ include Gruf::Loggable
25
+
26
+ ##
27
+ # Handles interception of outbound calls. Implement this in your derivative interceptor implementation.
28
+ #
29
+ # @param [Gruf::Outbound::RequestContext] request_context The context of the outbound request
30
+ # @return [Object] This method must return the response from the yielded block
31
+ #
32
+ def call(request_context:)
33
+ logger.debug "Logging client interceptor for request: #{request_context.method}"
34
+ yield
35
+ end
36
+
37
+ ##
38
+ # Call the interceptor from the request_response call
39
+ #
40
+ # @param [Object] request The request being sent
41
+ # @param [GRPC::ActiveCall] call The GRPC ActiveCall object
42
+ # @param [Method] method The method being called
43
+ # @param [Hash] metadata A hash of outgoing metadata
44
+ # @return [Object] The response message
45
+ #
46
+ def request_response(request: nil, call: nil, method: nil, metadata: nil, &block)
47
+ rc = Gruf::Outbound::RequestContext.new(
48
+ type: :request_response,
49
+ requests: [request],
50
+ call: call,
51
+ method: method,
52
+ metadata: metadata
53
+ )
54
+ call(request_context: rc, &block)
55
+ end
56
+
57
+ ##
58
+ # Call the interceptor from the client_streamer call
59
+ #
60
+ # @param [Enumerable] requests An enumerable of requests being sent
61
+ # @param [GRPC::ActiveCall] call The GRPC ActiveCall object
62
+ # @param [Method] method The method being called
63
+ # @param [Hash] metadata A hash of outgoing metadata
64
+ # @return [Object] The response message
65
+ #
66
+ def client_streamer(requests: nil, call: nil, method: nil, metadata: nil, &block)
67
+ rc = Gruf::Outbound::RequestContext.new(
68
+ type: :client_streamer,
69
+ requests: requests,
70
+ call: call,
71
+ method: method,
72
+ metadata: metadata
73
+ )
74
+ call(request_context: rc, &block)
75
+ end
76
+
77
+ ##
78
+ # Call the interceptor from the server_streamer call
79
+ #
80
+ # @param [Object] request The request being sent
81
+ # @param [GRPC::ActiveCall] call The GRPC ActiveCall object
82
+ # @param [Method] method The method being called
83
+ # @param [Hash] metadata A hash of outgoing metadata
84
+ # @return [Object] The response message
85
+ #
86
+ def server_streamer(request: nil, call: nil, method: nil, metadata: nil, &block)
87
+ rc = Gruf::Outbound::RequestContext.new(
88
+ type: :server_streamer,
89
+ requests: [request],
90
+ call: call,
91
+ method: method,
92
+ metadata: metadata
93
+ )
94
+ call(request_context: rc, &block)
95
+ end
96
+
97
+ ##
98
+ # Call the interceptor from the bidi_streamer call
99
+ #
100
+ # @param [Enumerable] requests An enumerable of requests being sent
101
+ # @param [GRPC::ActiveCall] call The GRPC ActiveCall object
102
+ # @param [Method] method The method being called
103
+ # @param [Hash] metadata A hash of outgoing metadata
104
+ #
105
+ def bidi_streamer(requests: nil, call: nil, method: nil, metadata: nil, &block)
106
+ rc = Gruf::Outbound::RequestContext.new(
107
+ type: :bidi_streamer,
108
+ requests: requests,
109
+ call: call,
110
+ method: method,
111
+ metadata: metadata
112
+ )
113
+ call(request_context: rc, &block)
114
+ end
115
+ end
116
+ end
117
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
@@ -26,14 +28,14 @@ module Gruf
26
28
  #
27
29
  # @param [Array<Gruf::Interceptors::ServerInterceptor>] interceptors
28
30
  #
29
- def initialize(interceptors = [])
30
- @interceptors = interceptors
31
+ def initialize(interceptors = nil)
32
+ @interceptors = interceptors || []
31
33
  end
32
34
 
33
35
  ##
34
36
  # Intercept the given request and run interceptors in a FIFO execution order
35
37
  #
36
- def intercept!
38
+ def intercept!(&block)
37
39
  return yield if @interceptors.none?
38
40
 
39
41
  i = @interceptors.pop
@@ -43,7 +45,7 @@ module Gruf
43
45
 
44
46
  i.call do
45
47
  if @interceptors.any?
46
- intercept! { yield }
48
+ intercept!(&block)
47
49
  else
48
50
  yield
49
51
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
@@ -25,15 +27,14 @@ module Gruf
25
27
  ##
26
28
  # Handle the instrumented response. Note: this will only instrument timings of _successful_ responses.
27
29
  #
28
- def call
30
+ def call(&block)
29
31
  return unless active_call.respond_to?(:output_metadata)
30
32
 
31
- result = Gruf::Interceptors::Timer.time do
32
- yield
33
- end
33
+ result = Gruf::Interceptors::Timer.time(&block)
34
34
  output_metadata.update(metadata_key => result.elapsed.to_s)
35
35
 
36
36
  raise result.message unless result.successful?
37
+
37
38
  result.message
38
39
  end
39
40
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
@@ -26,9 +28,11 @@ module Gruf
26
28
  # Format the parameters into a loggable string. Must be implemented in every derivative class
27
29
  #
28
30
  # @param [Hash] _payload The incoming request payload
31
+ # @param [Gruf::Controllers::Request] request The current controller request
32
+ # @param [Gruf::Interceptors::Timer::Result] result The timed result of the response
29
33
  # @return [String] The formatted string
30
34
  #
31
- def format(_payload)
35
+ def format(_payload, request:, result:)
32
36
  raise NotImplementedError
33
37
  end
34
38
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
@@ -28,9 +30,11 @@ module Gruf
28
30
  # Format the request into a JSON-friendly payload
29
31
  #
30
32
  # @param [Hash] payload The incoming request payload
33
+ # @param [Gruf::Controllers::Request] request The current controller request
34
+ # @param [Gruf::Interceptors::Timer::Result] result The timed result of the response
31
35
  # @return [String] The JSON representation of the payload
32
36
  #
33
- def format(payload)
37
+ def format(payload, request:, result:)
34
38
  payload.merge(format: 'json').to_json
35
39
  end
36
40
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
@@ -26,9 +28,11 @@ module Gruf
26
28
  # Format the request by only outputting the message body and params (if set to log params)
27
29
  #
28
30
  # @param [Hash] payload The incoming request payload
31
+ # @param [Gruf::Controllers::Request] request The current controller request
32
+ # @param [Gruf::Interceptors::Timer::Result] result The timed result of the response
29
33
  # @return [String] The formatted string
30
34
  #
31
- def format(payload)
35
+ def format(payload, request:, result:)
32
36
  time = payload.fetch(:duration, 0)
33
37
  grpc_status = payload.fetch(:grpc_status, 'GRPC::Ok')
34
38
  route_key = payload.fetch(:method, 'unknown')
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
@@ -36,29 +38,48 @@ module Gruf
36
38
  # Gruf::Instrumentation::Registry.add(:request_logging, Gruf::Instrumentation::RequestLogging::Hook)
37
39
  #
38
40
  class Interceptor < ::Gruf::Interceptors::ServerInterceptor
41
+ # Default mappings of codes to log levels...
42
+ LOG_LEVEL_MAP = {
43
+ 'GRPC::Ok' => :debug,
44
+ 'GRPC::InvalidArgument' => :debug,
45
+ 'GRPC::NotFound' => :debug,
46
+ 'GRPC::AlreadyExists' => :debug,
47
+ 'GRPC::OutOfRange' => :debug,
48
+ 'GRPC::Unauthenticated' => :warn,
49
+ 'GRPC::PermissionDenied' => :warn,
50
+ 'GRPC::Unknown' => :error,
51
+ 'GRPC::Internal' => :error,
52
+ 'GRPC::DataLoss' => :error,
53
+ 'GRPC::FailedPrecondition' => :error,
54
+ 'GRPC::Unavailable' => :error,
55
+ 'GRPC::DeadlineExceeded' => :error,
56
+ 'GRPC::Cancelled' => :error
57
+ }.freeze
39
58
 
40
59
  ###
41
60
  # Log the request, sending it to the appropriate formatter
42
61
  #
43
62
  # @return [String]
44
63
  #
45
- def call
46
- return yield if options.fetch(:ignore_methods, []).include?(request.method_name)
64
+ def call(&block)
65
+ return yield if options.fetch(:ignore_methods, [])&.include?(request.method_name)
47
66
 
48
- result = Gruf::Interceptors::Timer.time do
49
- yield
50
- end
67
+ result = Gruf::Interceptors::Timer.time(&block)
51
68
 
69
+ # Fetch log level options and merge with default...
70
+ log_level_map = LOG_LEVEL_MAP.merge(options.fetch(:log_levels, {}))
71
+
72
+ # A result is either successful, or, some level of feedback handled in the else block...
52
73
  if result.successful?
53
- type = options.fetch(:success_log_level, :info).to_sym
74
+ type = log_level_map['GRPC::Ok'] || :debug
54
75
  status_name = 'GRPC::Ok'
55
76
  else
56
- type = options.fetch(:failure_log_level, :error).to_sym
77
+ type = log_level_map[result.message_class_name] || :error
57
78
  status_name = result.message_class_name
58
79
  end
59
80
 
60
81
  payload = {}
61
- if !request.client_streamer?
82
+ if !request.client_streamer? && !request.bidi_streamer?
62
83
  payload[:params] = sanitize(request.message.to_h) if options.fetch(:log_parameters, false)
63
84
  payload[:message] = message(request, result)
64
85
  payload[:status] = status(result.message, result.successful?)
@@ -67,6 +88,7 @@ module Gruf
67
88
  payload[:message] = ''
68
89
  payload[:status] = GRPC::Core::StatusCodes::OK
69
90
  end
91
+
70
92
  payload[:service] = request.service_key
71
93
  payload[:method] = request.method_key
72
94
  payload[:action] = request.method_key
@@ -76,14 +98,22 @@ module Gruf
76
98
  payload[:time] = Time.now.to_s
77
99
  payload[:host] = Socket.gethostname
78
100
 
79
- ::Gruf.logger.send(type, formatter.format(payload))
101
+ logger.send(type, formatter.format(payload, request: request, result: result))
80
102
 
81
103
  raise result.message unless result.successful?
104
+
82
105
  result.message
83
106
  end
84
107
 
85
108
  private
86
109
 
110
+ ##
111
+ # @return [::Gruf::Logger]
112
+ #
113
+ def logger
114
+ @logger ||= (options.fetch(:logger, nil) || Gruf.logger)
115
+ end
116
+
87
117
  ##
88
118
  # Return an appropriate log message dependent on the status
89
119
  #
@@ -92,11 +122,9 @@ module Gruf
92
122
  # @return [String] The appropriate message body
93
123
  #
94
124
  def message(request, result)
95
- if result.successful?
96
- "[GRPC::Ok] (#{request.method_name})"
97
- else
98
- "[#{result.message_class_name}] (#{request.method_name}) #{result.message.message}"
99
- end
125
+ return "[GRPC::Ok] (#{request.method_name})" if result.successful?
126
+
127
+ "[#{result.message_class_name}] (#{request.method_name}) #{result.message.message}"
100
128
  end
101
129
 
102
130
  ##
@@ -119,13 +147,14 @@ module Gruf
119
147
  unless @formatter
120
148
  fmt = options.fetch(:formatter, :plain)
121
149
  @formatter = case fmt
122
- when Symbol
123
- klass = "Gruf::Interceptors::Instrumentation::RequestLogging::Formatters::#{fmt.to_s.capitalize}"
124
- fmt = klass.constantize.new
125
- when Class
126
- fmt = fmt.new
127
- else
128
- fmt
150
+ when Symbol
151
+ prefix = 'Gruf::Interceptors::Instrumentation::RequestLogging::Formatters::'
152
+ klass = "#{prefix}#{fmt.to_s.capitalize}"
153
+ fmt = klass.constantize.new
154
+ when Class
155
+ fmt = fmt.new
156
+ else
157
+ fmt
129
158
  end
130
159
  raise InvalidFormatterError unless fmt.is_a?(Formatters::Base)
131
160
  end
@@ -133,32 +162,34 @@ module Gruf
133
162
  end
134
163
 
135
164
  ##
136
- # Redact any blacklisted params and return an updated hash
165
+ # Redact any blocklisted params and return an updated hash
137
166
  #
138
167
  # @param [Hash] params The hash of parameters to sanitize
139
168
  # @return [Hash] The sanitized params in hash form
140
169
  #
141
170
  def sanitize(params = {})
142
- blacklists = options.fetch(:blacklist, []).map(&:to_s)
143
- redacted_string = options.fetch(:redacted_string, 'REDACTED')
144
- blacklists.each do |blacklist|
145
- parts = blacklist.split('.').map(&:to_sym)
171
+ blocklists = (options.fetch(:blocklist, []) || []).map(&:to_s)
172
+ redacted_string = options.fetch(:redacted_string, nil) || 'REDACTED'
173
+ blocklists.each do |blocklist|
174
+ parts = blocklist.split('.').map(&:to_sym)
146
175
  redact!(parts, 0, params, redacted_string)
147
176
  end
148
177
  params
149
178
  end
150
179
 
151
180
  ##
152
- # Helper method to recursively redact based on the black list
181
+ # Helper method to recursively redact based on the blocklist
153
182
  #
154
- # @param [Array] parts The blacklist. ex. 'data.schema' -> [:data, :schema]
155
- # @param [Integer] idx The current index of the blacklist
183
+ # @param [Array] parts The blocklist. ex. 'data.schema' -> [:data, :schema]
184
+ # @param [Integer] idx The current index of the blocklist
156
185
  # @param [Hash] params The hash of parameters to sanitize
157
186
  # @param [String] redacted_string The custom redact string
158
187
  #
159
188
  def redact!(parts = [], idx = 0, params = {}, redacted_string = 'REDACTED')
160
189
  return unless parts.is_a?(Array) && params.is_a?(Hash)
190
+
161
191
  return if idx >= parts.size || !params.key?(parts[idx])
192
+
162
193
  if idx == parts.size - 1
163
194
  if params[parts[idx]].is_a? Hash
164
195
  hash_deep_redact!(params[parts[idx]], redacted_string)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
@@ -23,7 +25,7 @@ module Gruf
23
25
  ##
24
26
  # Push data to StatsD, only doing so if a client is set
25
27
  #
26
- def call
28
+ def call(&block)
27
29
  unless client
28
30
  Gruf.logger.error 'Statsd module loaded, but no client configured!'
29
31
  return yield
@@ -31,14 +33,13 @@ module Gruf
31
33
 
32
34
  client.increment(route_key)
33
35
 
34
- result = Gruf::Interceptors::Timer.time do
35
- yield
36
- end
36
+ result = Gruf::Interceptors::Timer.time(&block)
37
37
 
38
38
  client.increment("#{route_key}.#{postfix(result.successful?)}")
39
39
  client.timing(route_key, result.elapsed)
40
40
 
41
41
  raise result.message unless result.successful?
42
+
42
43
  result.message
43
44
  end
44
45
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
@@ -22,7 +24,7 @@ module Gruf
22
24
  class InterceptorNotFoundError < StandardError; end
23
25
 
24
26
  def initialize
25
- @registry ||= []
27
+ @registry = []
26
28
  end
27
29
 
28
30
  ##
@@ -49,6 +51,7 @@ module Gruf
49
51
  def remove(interceptor_class)
50
52
  pos = @registry.find_index { |opts| opts.fetch(:klass, '') == interceptor_class }
51
53
  raise InterceptorNotFoundError if pos.nil?
54
+
52
55
  @registry.delete_at(pos)
53
56
  end
54
57
 
@@ -64,6 +67,7 @@ module Gruf
64
67
  interceptors_mutex do
65
68
  pos = @registry.find_index { |opts| opts.fetch(:klass, '') == before_class }
66
69
  raise InterceptorNotFoundError if pos.nil?
70
+
67
71
  @registry.insert(
68
72
  pos,
69
73
  klass: interceptor_class,
@@ -84,6 +88,7 @@ module Gruf
84
88
  interceptors_mutex do
85
89
  pos = @registry.find_index { |opts| opts.fetch(:klass, '') == after_class }
86
90
  raise InterceptorNotFoundError if pos.nil?
91
+
87
92
  @registry.insert(
88
93
  (pos + 1),
89
94
  klass: interceptor_class,
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
@@ -22,10 +24,18 @@ module Gruf
22
24
  ##
23
25
  # Represents a timed result for an interceptor
24
26
  #
27
+ # @property [Object] message The protobuf message
28
+ # @property [Float] elapsed The elapsed time of the request
29
+ #
25
30
  class Result
26
- attr_reader :message
27
- attr_reader :elapsed
31
+ attr_reader :message,
32
+ :elapsed
28
33
 
34
+ ##
35
+ # @param [Object] message The protobuf message
36
+ # @param [Float] elapsed The elapsed time of the request
37
+ # @param [Boolean] successful If the request was successful
38
+ #
29
39
  def initialize(message, elapsed, successful)
30
40
  @message = message
31
41
  @elapsed = elapsed.to_f
data/lib/gruf/loggable.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
data/lib/gruf/logging.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
6
+ # documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
7
+ # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
8
+ # persons to whom the Software is furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
11
+ # Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
14
+ # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
15
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
+ # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
+ #
18
+ module Gruf
19
+ module Outbound
20
+ ##
21
+ # Encapsulates the context of an outbound client request
22
+ #
23
+ class RequestContext
24
+ # @var [Symbol]
25
+ attr_reader :type
26
+ # @var [Enumerable] requests
27
+ attr_reader :requests
28
+ # @var [GRPC::ActiveCall]
29
+ attr_reader :call
30
+ # @var [Method] method
31
+ attr_reader :method
32
+ # @var [Hash] metadata
33
+ attr_reader :metadata
34
+
35
+ ##
36
+ # Initialize the new request context
37
+ #
38
+ # @param [Symbol] type The type of request
39
+ # @param [Enumerable] requests An enumerable of requests being sent
40
+ # @param [GRPC::ActiveCall] call The GRPC ActiveCall object
41
+ # @param [Method] method The method being called
42
+ # @param [Hash] metadata A hash of outgoing metadata
43
+ #
44
+ def initialize(type:, requests:, call:, method:, metadata:)
45
+ @type = type
46
+ @requests = requests
47
+ @call = call
48
+ @method = method
49
+ @metadata = metadata
50
+ end
51
+
52
+ ##
53
+ # Return the name of the method being called, e.g. GetThing
54
+ #
55
+ # @return [String]
56
+ #
57
+ def method_name
58
+ @method.to_s.split('/').last
59
+ end
60
+
61
+ ##
62
+ # Return the proper routing key for the request
63
+ #
64
+ # @return [String]
65
+ #
66
+ def route_key
67
+ @method[1..-1].underscore.tr('/', '.')
68
+ end
69
+ end
70
+ end
71
+ end
data/lib/gruf/response.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
@@ -35,11 +37,12 @@ module Gruf
35
37
  # Initialize a response object with the given gRPC operation
36
38
  #
37
39
  # @param [GRPC::ActiveCall::Operation] operation The given operation for the current call
40
+ # @param [StdClass] message
38
41
  # @param [Float] execution_time The amount of time that the response took to occur
39
42
  #
40
- def initialize(operation, execution_time = nil)
43
+ def initialize(operation:, message:, execution_time: nil)
41
44
  @operation = operation
42
- @message = operation.execute
45
+ @message = message
43
46
  @metadata = operation.metadata
44
47
  @trailing_metadata = operation.trailing_metadata
45
48
  @deadline = operation.deadline
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated