gruf 1.2.7 → 2.0.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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/README.md +98 -119
  4. data/bin/gruf +9 -3
  5. data/lib/gruf.rb +4 -4
  6. data/lib/gruf/configuration.rb +11 -20
  7. data/lib/gruf/controllers/base.rb +82 -0
  8. data/lib/gruf/controllers/request.rb +96 -0
  9. data/lib/gruf/controllers/service_binder.rb +86 -0
  10. data/lib/gruf/error.rb +9 -0
  11. data/lib/gruf/errors/helpers.rb +40 -0
  12. data/lib/gruf/{hooks → interceptors}/active_record/connection_reset.rb +4 -10
  13. data/lib/gruf/interceptors/authentication/basic.rb +80 -0
  14. data/lib/gruf/interceptors/base.rb +51 -0
  15. data/lib/gruf/{instrumentation/output_metadata_timer.rb → interceptors/context.rb} +25 -15
  16. data/lib/gruf/interceptors/instrumentation/output_metadata_timer.rb +59 -0
  17. data/lib/gruf/{instrumentation → interceptors/instrumentation}/request_logging/formatters/base.rb +15 -13
  18. data/lib/gruf/{instrumentation → interceptors/instrumentation}/request_logging/formatters/logstash.rb +15 -13
  19. data/lib/gruf/{instrumentation → interceptors/instrumentation}/request_logging/formatters/plain.rb +21 -19
  20. data/lib/gruf/interceptors/instrumentation/request_logging/interceptor.rb +191 -0
  21. data/lib/gruf/interceptors/instrumentation/statsd.rb +80 -0
  22. data/lib/gruf/interceptors/registry.rb +131 -0
  23. data/lib/gruf/{authentication/none.rb → interceptors/server_interceptor.rb} +8 -7
  24. data/lib/gruf/interceptors/timer.rb +79 -0
  25. data/lib/gruf/response.rb +1 -2
  26. data/lib/gruf/server.rb +40 -25
  27. data/lib/gruf/version.rb +1 -1
  28. metadata +19 -20
  29. data/lib/gruf/authentication.rb +0 -65
  30. data/lib/gruf/authentication/base.rb +0 -65
  31. data/lib/gruf/authentication/basic.rb +0 -74
  32. data/lib/gruf/authentication/strategies.rb +0 -107
  33. data/lib/gruf/hooks/base.rb +0 -66
  34. data/lib/gruf/hooks/registry.rb +0 -110
  35. data/lib/gruf/instrumentation/base.rb +0 -114
  36. data/lib/gruf/instrumentation/registry.rb +0 -104
  37. data/lib/gruf/instrumentation/request_context.rb +0 -82
  38. data/lib/gruf/instrumentation/request_logging/hook.rb +0 -185
  39. data/lib/gruf/instrumentation/statsd.rb +0 -80
  40. data/lib/gruf/service.rb +0 -333
@@ -1,333 +0,0 @@
1
- # coding: utf-8
2
- # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
3
- #
4
- # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5
- # documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
6
- # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
7
- # persons to whom the Software is furnished to do so, subject to the following conditions:
8
- #
9
- # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
10
- # Software.
11
- #
12
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
13
- # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14
- # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
15
- # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16
- #
17
- module Gruf
18
- ##
19
- # Module for gRPC endpoints. Include this in any gRPC service you wish Gruf to serve. It will automatically mount
20
- # it to the given gruf server and can be run via the command:
21
- #
22
- # bundle exec gruf
23
- #
24
- module Service
25
- extend ActiveSupport::Concern
26
-
27
- included do
28
- include Gruf::Loggable
29
-
30
- ##
31
- # Hook into method_added to add pre/post interceptors for endpoints
32
- #
33
- # @param [String] method_name
34
- #
35
- def self.method_added(method_name)
36
- return if @__last_methods_added && @__last_methods_added.include?(method_name)
37
- return unless rpc_handler_names.include?(method_name)
38
-
39
- rpc_desc = rpc_descs[method_name.to_s.camelcase.to_sym]
40
-
41
- with = :"#{method_name}_with_intercept"
42
- without = :"#{method_name}_without_intercept"
43
- @__last_methods_added = [method_name, with, without]
44
-
45
- if rpc_desc
46
- if rpc_desc.request_response?
47
- define_method(with) do |request, call|
48
- call_chain(method_name, request, call)
49
- end
50
- elsif rpc_desc.client_streamer?
51
- define_method(with) do |call|
52
- call_chain(method_name, nil, call)
53
- end
54
- elsif rpc_desc.server_streamer?
55
- define_method(with) do |request, call, &block|
56
- call_chain(method_name, request, call, &block)
57
- end
58
- else # bidi
59
- define_method(with) do |requests, call, &block|
60
- call_chain(method_name, requests, call, &block)
61
- end
62
- end
63
- else
64
- # fallback to the catch-all
65
- define_method with do |*args, &block|
66
- call_chain(method_name, *args, &block)
67
- end
68
- end
69
-
70
- alias_method without, method_name
71
- alias_method method_name, with
72
- @__last_methods_added = nil
73
- end
74
-
75
- ##
76
- # Properly find all RPC handler methods
77
- #
78
- def self.rpc_handler_names
79
- rpc_descs.keys.map { |n| n.to_s.underscore.to_sym }.uniq
80
- end
81
-
82
- ##
83
- # Mount the service into the server automatically
84
- #
85
- def self.mount
86
- Gruf.services << name.constantize
87
- end
88
-
89
- mount
90
- end
91
-
92
- ##
93
- # Happens before a call.
94
- #
95
- # @param [Symbol] call_signature The method being called
96
- # @param [Object] req The request object
97
- # @param [GRPC::ActiveCall] call The gRPC active call object
98
- #
99
- def before_call(call_signature, req, call)
100
- authenticate(call_signature, req, call)
101
- Gruf::Hooks::Registry.each do |_name, h|
102
- h.new(self, Gruf.hook_options).before(call_signature, req, call) if h.instance_methods.include?(:before)
103
- end
104
- end
105
-
106
- ##
107
- # Happens around a call.
108
- #
109
- # @param [Symbol] call_signature The gRPC method being called
110
- # @param [Object] req The request object
111
- # @param [GRPC::ActiveCall] call The gRPC active call object
112
- #
113
- def around_call(call_signature, req, call, &block)
114
- around_hooks = []
115
- Gruf::Hooks::Registry.each do |_name, h|
116
- around_hooks << h.new(self, Gruf.hook_options) if h.instance_methods.include?(:around)
117
- end
118
- if around_hooks.any?
119
- run_around_hook(around_hooks, call_signature, req, call, &block)
120
- else
121
- yield
122
- end
123
- end
124
-
125
- ##
126
- # Run all around hooks recursively, starting with the last loaded
127
- #
128
- # @param [Array<Gruf::Hooks::Base>] hooks The current stack of hooks
129
- # @param [Symbol] call_signature The gRPC method being called
130
- # @param [Object] req The request object
131
- # @param [GRPC::ActiveCall] call The gRPC active call object
132
- #
133
- def run_around_hook(hooks, call_signature, req, call, &_)
134
- h = hooks.pop
135
- h.around(call_signature, req, call) do
136
- if hooks.any?
137
- run_around_hook(hooks, call_signature, req, call) { yield }
138
- else
139
- yield
140
- end
141
- end
142
- end
143
-
144
- ##
145
- # Happens around the entire call chain - before, around, the call itself, and after hooks.
146
- #
147
- # @param [Symbol] call_signature The gRPC method being called
148
- # @param [Object] req The request object
149
- # @param [GRPC::ActiveCall] call The gRPC active call object
150
- #
151
- def outer_around_call(call_signature, req, call, &block)
152
- outer_around_hooks = []
153
-
154
- # run instrumentation hooks as outer_around calls
155
- Gruf::Instrumentation::Registry.each do |_name, h|
156
- outer_around_hooks << h.new(self, Gruf.instrumentation_options)
157
- end
158
-
159
- Gruf::Hooks::Registry.each do |_name, h|
160
- outer_around_hooks << h.new(self, Gruf.hook_options) if h.instance_methods.include?(:outer_around)
161
- end
162
- if outer_around_hooks.any?
163
- run_outer_around_hook(outer_around_hooks, call_signature, req, call, &block)
164
- else
165
- yield
166
- end
167
- end
168
-
169
- ##
170
- # Run all outer around hooks recursively, starting with the last loaded
171
- #
172
- # @param [Array<Gruf::Hooks::Base>] hooks The current stack of hooks
173
- # @param [Symbol] call_signature The gRPC method being called
174
- # @param [Object] req The request object
175
- # @param [GRPC::ActiveCall] call The gRPC active call object
176
- #
177
- def run_outer_around_hook(hooks, call_signature, req, call, &_)
178
- h = hooks.pop
179
- h.outer_around(call_signature, req, call) do
180
- if hooks.any?
181
- run_outer_around_hook(hooks, call_signature, req, call) { yield }
182
- else
183
- yield
184
- end
185
- end
186
- end
187
-
188
- ##
189
- # Happens after a call
190
- #
191
- # @param [Boolean] success Whether or not the result was successful
192
- # @param [Object] response The response object returned from the gRPC call
193
- # @param [Symbol] call_signature The method being called
194
- # @param [Object] req The request object
195
- # @param [GRPC::ActiveCall] call The gRPC active call object
196
- # @return [Object] If extending this method or using the after_call_hook, you must return the response object
197
- #
198
- def after_call(success, response, call_signature, req, call)
199
- Gruf::Hooks::Registry.each do |_name, h|
200
- h.new(self, Gruf.hook_options).after(success, response, call_signature, req, call) if h.instance_methods.include?(:after)
201
- end
202
- end
203
-
204
- ##
205
- # Authenticate the endpoint caller.
206
- #
207
- # @param [Symbol] _method The method being called
208
- # @param [Object] req The request object
209
- # @param [GRPC::ActiveCall] call The gRPC active call object
210
- #
211
- def authenticate(_method, req, call)
212
- fail!(req, call, :unauthenticated) unless Authentication.verify(call)
213
- end
214
-
215
- ##
216
- # Will issue a GRPC BadStatus exception, with a code based on the code passed.
217
- #
218
- # @param [Object] _req The request object being sent
219
- # @param [GRPC::ActiveCall] call The gRPC active call
220
- # @param [Symbol] error_code The network error code that maps to gRPC status codes
221
- # @param [Symbol] app_code The application-specific code for the error
222
- # @param [String] message (Optional) A detail message about the error
223
- # @param [Hash] metadata (Optional) Any metadata to inject into the trailing metadata for the response
224
- #
225
- def fail!(_req, call, error_code, app_code = nil, message = '', metadata = {})
226
- e = error
227
- e.code = error_code.to_sym
228
- e.app_code = app_code ? app_code.to_sym : error.code
229
- e.message = message.to_s
230
- e.metadata = metadata
231
-
232
- cleanup!
233
- e.fail!(call)
234
- end
235
-
236
- private
237
-
238
- ##
239
- # Encapsulate the call chain to provide before/around/after hooks
240
- #
241
- # @param [String] original_call_sig The original call signature for the service
242
- # @param [Object] req The request object
243
- # @param [GRPC::ActiveCall] call The ActiveCall object being executed
244
- # @return [Object] The response object
245
- #
246
- def call_chain(original_call_sig, req, call, &block)
247
- outer_result = outer_around_call(original_call_sig, req, call) do
248
- begin
249
- before_call(original_call_sig, req, call)
250
-
251
- result = around_call(original_call_sig, req, call) do
252
- # send the actual request to gRPC
253
- if req.nil?
254
- send("#{original_call_sig}_without_intercept", call, &block)
255
- else
256
- send("#{original_call_sig}_without_intercept", req, call, &block)
257
- end
258
- end
259
- rescue GRPC::BadStatus => e
260
- result = e
261
- end
262
- success = !result.is_a?(GRPC::BadStatus)
263
- after_call(success, result, original_call_sig, req, call)
264
-
265
- raise result unless success
266
-
267
- result
268
- end
269
- cleanup!
270
- outer_result
271
- rescue GRPC::BadStatus
272
- cleanup!
273
- raise
274
- rescue => e
275
- set_debug_info(e.message, e.backtrace) if Gruf.backtrace_on_error
276
- error_message = Gruf.use_exception_message ? e.message : Gruf.internal_error_message
277
- fail!(req, call, :internal, :unknown, error_message)
278
- end
279
-
280
- ##
281
- # Return the appropriate RPC descriptor for a given call signature on this service
282
- #
283
- # @return [GRPC::RpcDesc]
284
- #
285
- def rpc_desc(call_signature)
286
- self.class.rpc_descs[call_signature.to_s.camelcase.to_sym]
287
- end
288
-
289
- ##
290
- # Add a field error to this endpoint
291
- #
292
- # @param [Symbol] field_name The name of the field
293
- # @param [Symbol] error_code The application error code for the field
294
- # @param [String] message A given error message for the field
295
- #
296
- def add_field_error(field_name, error_code, message = '')
297
- error.add_field_error(field_name, error_code, message)
298
- end
299
-
300
- ##
301
- # Return true if there are any present field errors
302
- #
303
- # @return [Boolean] True if the service has any field errors
304
- #
305
- def has_field_errors?
306
- error.field_errors.any?
307
- end
308
-
309
- ##
310
- # Set debugging information on the error payload
311
- #
312
- # @param [String] detail A string message that represents debugging information
313
- # @param [Array<String>] stack_trace An array of strings that contain the backtrace
314
- #
315
- def set_debug_info(detail, stack_trace = [])
316
- error.set_debug_info(detail, stack_trace)
317
- end
318
-
319
- ##
320
- # @return [Gruf::Error] The generated gruf error object
321
- #
322
- def error
323
- @error ||= Gruf::Error.new
324
- end
325
-
326
- ##
327
- # Cleanup after a service call
328
- #
329
- def cleanup!
330
- remove_instance_variable(:@error) if instance_variable_defined?(:@error)
331
- end
332
- end
333
- end