gruf 2.9.1 → 2.15.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -15,9 +15,6 @@
15
15
  # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
16
  # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
17
  #
18
- require_relative 'request'
19
- require_relative 'service_binder'
20
-
21
18
  module Gruf
22
19
  module Controllers
23
20
  ##
@@ -26,13 +23,16 @@ module Gruf
26
23
  class Base
27
24
  include Gruf::Errors::Helpers
28
25
 
29
- # @var [Gruf::Controller::Request] request
26
+ # @!attribute [r] request
27
+ # @return [Gruf::Controller::Request] The incoming request
30
28
  attr_reader :request
31
- # @var [Gruf::Error] error
29
+ # @!attribute [r] error
30
+ # @return [Gruf::Error] The current error on the controller
32
31
  attr_reader :error
33
32
 
34
33
  class << self
35
- # @var [GRPC::GenericService] bound_service
34
+ # @!attribute [r] bound_service
35
+ # @return [GRPC::GenericService] bound_service The bound gRPC service class
36
36
  attr_reader :bound_service
37
37
  end
38
38
 
@@ -64,11 +64,12 @@ module Gruf
64
64
  #
65
65
  def self.bind(service)
66
66
  service_class = service.name.constantize
67
- Gruf.services << service_class
67
+ ::Gruf.logger.debug "[gruf] Binding #{service_class} to #{name}"
68
+ ::Gruf.services << service_class
68
69
  # rubocop:disable ThreadSafety/InstanceVariableInClassMethod
69
70
  @bound_service = service_class
70
71
  # rubocop:enable ThreadSafety/InstanceVariableInClassMethod
71
- ServiceBinder.new(service_class).bind!(self)
72
+ ServiceBinder.bind!(service: service_class, controller: self)
72
73
  end
73
74
 
74
75
  ##
@@ -89,6 +90,7 @@ module Gruf
89
90
  # @param [block] &block The passed block for executing the method
90
91
  #
91
92
  def call(method_key, &block)
93
+ ::Gruf.autoloaders.reload if ::Gruf.development?
92
94
  Interceptors::Context.new(@interceptors).intercept! do
93
95
  process_action(method_key, &block)
94
96
  end
@@ -21,15 +21,20 @@ module Gruf
21
21
  # Encapsulates a request for a controller
22
22
  #
23
23
  class Request
24
- # @var [Object] message
24
+ # @!attribute [r] message
25
+ # @return [Object] The protobuf message in the request
25
26
  attr_reader :message
26
- # @var [GRPC::ActiveCall] active_call
27
+ # @!attribute [r] active_call
28
+ # @return [GRPC::ActiveCall] The active call object used for this request
27
29
  attr_reader :active_call
28
- # @var [Symbol] method_key
30
+ # @!attribute [r] method_key
31
+ # @return [Symbol] The method name being requested
29
32
  attr_reader :method_key
30
- # @var [Gruf::Controllers::Request::Type] type
33
+ # @!attribute [r] type
34
+ # @return [Gruf::Controllers::Request::Type] The type of request
31
35
  attr_reader :type
32
- # @var [Class] service
36
+ # @!attribute [r] service
37
+ # @return [Class] The GRPC service class for this request
33
38
  attr_reader :service
34
39
 
35
40
  delegate :metadata, to: :active_call
@@ -76,7 +81,7 @@ module Gruf
76
81
  # @return [String] The mapped service key
77
82
  #
78
83
  def service_key
79
- @service.name.underscore.tr('/', '.').gsub('.service', '')
84
+ @service.name.to_s.underscore.tr('/', '.').gsub('.service', '')
80
85
  end
81
86
 
82
87
  ##
@@ -106,6 +111,7 @@ module Gruf
106
111
  # Return all messages for this request, properly handling different request types
107
112
  #
108
113
  # @return [Enumerable<Object>] All messages for this request
114
+ # @return [Object] If a bidi streamed request, will return the message object
109
115
  #
110
116
  def messages
111
117
  if client_streamer?
@@ -26,92 +26,79 @@ module Gruf
26
26
  #
27
27
  class BoundDesc < SimpleDelegator; end
28
28
 
29
- ##
30
- # Initialize a service binder instance with the given service
31
- #
32
- # @param [GRPC::GenericService] service The gRPC service stub to bind
33
- #
34
- def initialize(service)
35
- @service = service
36
- end
37
-
38
- ##
39
- # Bind all methods on the service to the passed controller
40
- #
41
- # @param [Class<Gruf::Controllers::Base>] controller
42
- #
43
- def bind!(controller)
44
- rpc_methods.each { |name, desc| bind_method(controller, name, desc) }
45
- end
46
-
47
- private
48
-
49
- ##
50
- # Bind the grpc methods to the service, allowing for server interception and execution control
51
- #
52
- # @param [Gruf::Controllers::Base] controller
53
- # @param [Symbol] method_name
54
- # @param [BoundDesc] desc
55
- #
56
- def bind_method(controller, method_name, desc)
57
- method_key = method_name.to_s.underscore.to_sym
58
- service_ref = @service
29
+ class << self
30
+ ##
31
+ # Bind all methods on the service to the passed controller
32
+ #
33
+ # @param [Class<Gruf::Controllers::Base>] controller
34
+ #
35
+ def bind!(service:, controller:)
36
+ rpc_methods = service.rpc_descs.map { |rd| BoundDesc.new(rd) }
37
+ rpc_methods.each { |name, desc| bind_method(service, controller, name, desc) }
38
+ end
59
39
 
60
- @service.class_eval do
61
- if desc.request_response?
62
- define_method(method_key) do |message, active_call|
63
- c = controller.new(
64
- method_key: method_key,
65
- service: service_ref,
66
- message: message,
67
- active_call: active_call,
68
- rpc_desc: desc
69
- )
70
- c.call(method_key)
71
- end
72
- elsif desc.client_streamer?
73
- define_method(method_key) do |active_call|
74
- c = controller.new(
75
- method_key: method_key,
76
- service: service_ref,
77
- message: proc { |&block| active_call.each_remote_read(&block) },
78
- active_call: active_call,
79
- rpc_desc: desc
80
- )
81
- c.call(method_key)
82
- end
83
- elsif desc.server_streamer?
84
- define_method(method_key) do |message, active_call, &block|
85
- c = controller.new(
86
- method_key: method_key,
87
- service: service_ref,
88
- message: message,
89
- active_call: active_call,
90
- rpc_desc: desc
91
- )
92
- c.call(method_key, &block)
93
- end
94
- else # bidi
95
- define_method(method_key) do |messages, active_call, &block|
96
- c = controller.new(
97
- method_key: method_key,
98
- service: service_ref,
99
- message: messages,
100
- active_call: active_call,
101
- rpc_desc: desc
102
- )
103
- c.call(method_key, &block)
40
+ ##
41
+ # Bind the grpc methods to the service, allowing for server interception and execution control
42
+ #
43
+ # @param [Gruf::Controllers::Base] controller
44
+ # @param [Symbol] method_name
45
+ # @param [BoundDesc] desc
46
+ #
47
+ def bind_method(service_ref, controller, method_name, desc)
48
+ method_key = method_name.to_s.underscore.to_sym
49
+ service_ref.class_eval do
50
+ if desc.request_response?
51
+ define_method(method_key) do |message, active_call|
52
+ controller = controller.name.constantize
53
+ c = controller.new(
54
+ method_key: method_key,
55
+ service: service_ref,
56
+ message: message,
57
+ active_call: active_call,
58
+ rpc_desc: desc
59
+ )
60
+ c.call(method_key)
61
+ end
62
+ elsif desc.client_streamer?
63
+ define_method(method_key) do |active_call|
64
+ controller = controller.name.constantize
65
+ c = controller.new(
66
+ method_key: method_key,
67
+ service: service_ref,
68
+ message: proc { |&block| active_call.each_remote_read(&block) },
69
+ active_call: active_call,
70
+ rpc_desc: desc
71
+ )
72
+ c.call(method_key)
73
+ end
74
+ elsif desc.server_streamer?
75
+ define_method(method_key) do |message, active_call, &block|
76
+ controller = controller.name.constantize
77
+ c = controller.new(
78
+ method_key: method_key,
79
+ service: service_ref,
80
+ message: message,
81
+ active_call: active_call,
82
+ rpc_desc: desc
83
+ )
84
+ c.call(method_key, &block)
85
+ end
86
+ else # bidi
87
+ define_method(method_key) do |messages, active_call, &block|
88
+ controller = controller.name.constantize
89
+ c = controller.new(
90
+ method_key: method_key,
91
+ service: service_ref,
92
+ message: messages,
93
+ active_call: active_call,
94
+ rpc_desc: desc
95
+ )
96
+ c.call(method_key, &block)
97
+ end
104
98
  end
105
99
  end
106
100
  end
107
101
  end
108
-
109
- ##
110
- # @return Array<Gruf::Controllers::ServiceBinder::BoundDesc>
111
- #
112
- def rpc_methods
113
- @service.rpc_descs.map { |rd| BoundDesc.new(rd) }
114
- end
115
102
  end
116
103
  end
117
104
  end
data/lib/gruf/error.rb CHANGED
@@ -15,10 +15,6 @@
15
15
  # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
16
  # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
17
  #
18
- require_relative 'errors/field'
19
- require_relative 'errors/debug_info'
20
- require_relative 'serializers/errors/base'
21
- require_relative 'serializers/errors/json'
22
18
 
23
19
  module Gruf
24
20
  ##
@@ -59,20 +55,28 @@ module Gruf
59
55
  METADATA_SIZE_EXCEEDED_CODE = 'metadata_size_exceeded'
60
56
  METADATA_SIZE_EXCEEDED_MSG = 'Metadata too long, risks exceeding http2 trailing metadata limit.'
61
57
 
62
- # @return [Symbol] The given internal gRPC code for the error
58
+ # @!attribute code
59
+ # @return [Symbol] The given internal gRPC code for the error
63
60
  attr_accessor :code
64
- # @return [Symbol] An arbitrary application code that can be used for logical processing of the error by the client
61
+ # @!attribute app_code
62
+ # @return [Symbol] An arbitrary application code that can be used for logical processing of the error
63
+ # by the client
65
64
  attr_accessor :app_code
66
- # @return [String] The error message returned by the server
65
+ # @!attribute message
66
+ # @return [String] The error message returned by the server
67
67
  attr_accessor :message
68
- # @return [Array] An array of field errors that can be returned by the server
68
+ # @!attribute field_errors
69
+ # @return [Array] An array of field errors that can be returned by the server
69
70
  attr_accessor :field_errors
70
- # @return [Errors::DebugInfo] A object containing debugging information, such as a stack trace and exception name,
71
- # that can be used to debug an given error response. This is sent by the server over the trailing metadata.
71
+ # @!attribute debug_info
72
+ # @return [Errors::DebugInfo] A object containing debugging information, such as a stack trace and exception name,
73
+ # that can be used to debug an given error response. This is sent by the server over the trailing metadata.
72
74
  attr_accessor :debug_info
73
- # @return [GRPC::BadStatus] The gRPC BadStatus error object that was generated
75
+ # @!attribute [w] grpc_error
76
+ # @return [GRPC::BadStatus] The gRPC BadStatus error object that was generated
74
77
  attr_writer :grpc_error
75
- # @return [Hash] The trailing metadata that was attached to the error
78
+ # @!attribute [r] metadata
79
+ # @return [Hash] The trailing metadata that was attached to the error
76
80
  attr_reader :metadata
77
81
 
78
82
  ##
@@ -21,9 +21,11 @@ module Gruf
21
21
  # Represents debugging information for an exception that occurred in a gRPC service
22
22
  #
23
23
  class DebugInfo
24
- # @return [String] The detail message of the exception
24
+ # @!attribute [r] detail
25
+ # @return [String] The detail message of the exception
25
26
  attr_reader :detail
26
- # @return [Array<String>] The stack trace generated by the exception as an array of strings
27
+ # @!attribute [r] stack_trace
28
+ # @return [Array<String>] The stack trace generated by the exception as an array of strings
27
29
  attr_reader :stack_trace
28
30
 
29
31
  ##
@@ -21,11 +21,14 @@ module Gruf
21
21
  # Represents a field-specific error
22
22
  #
23
23
  class Field
24
- # @return [Symbol] The name of the field as a Symbol
24
+ # @!attribute [r] field_name
25
+ # @return [Symbol] The name of the field as a Symbol
25
26
  attr_reader :field_name
26
- # @return [Symbol] The application error code for the field, e.g. :job_not_found
27
+ # @!attribute [r] error_code
28
+ # @return [Symbol] The application error code for the field, e.g. :job_not_found
27
29
  attr_reader :error_code
28
- # @return [String] The error message for the field, e.g. "Job with ID 123 not found"
30
+ # @!attribute [r] message
31
+ # @return [String] The error message for the field, e.g. "Job with ID 123 not found"
29
32
  attr_reader :message
30
33
 
31
34
  ##
@@ -16,20 +16,6 @@
16
16
  # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
17
  #
18
18
  module Gruf
19
- ##
20
- # Handles internal gruf logging requests
21
- #
22
- module Logger
23
- ##
24
- # Return the current Gruf logger
25
- #
26
- # @return [Logger]
27
- #
28
- def logger
29
- Gruf.logger
30
- end
31
- end
32
-
33
19
  ##
34
20
  # Handles grpc internal logging requests
35
21
  #
@@ -1,9 +1,39 @@
1
1
  # frozen_string_literal: true
2
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
+ #
3
18
  module Gruf
4
19
  module Integrations
5
20
  module Rails
21
+ ##
22
+ # Rails integration for Gruf, that currently only manages code autoloading in a Rails context
23
+ #
6
24
  class Railtie < ::Rails::Railtie
25
+ initializer 'gruf.initializer' do |app|
26
+ config.before_configuration do
27
+ # Remove autoloading of the controllers path from Rails' zeitwerk, so that we ensure Gruf's zeitwerk
28
+ # properly manages them itself. This allows us to manage code reloading and logging in Gruf specifically
29
+ app.config.eager_load_paths -= [::Gruf.controllers_path] if app.config.respond_to?(:eager_load_paths)
30
+ if ::Rails.respond_to?(:autoloaders) # if we're on a late enough version of rails
31
+ ::Rails.autoloaders.each do |autoloader|
32
+ autoloader.ignore(Gruf.controllers_path)
33
+ end
34
+ end
35
+ end
36
+ end
7
37
  end
8
38
  end
9
39
  end
@@ -21,11 +21,14 @@ module Gruf
21
21
  # Base class for interception requests
22
22
  #
23
23
  class Base
24
- # @var [Gruf::Controllers::Request] request
24
+ # @!attribute [r] request
25
+ # @return [Gruf::Controllers::Request] request
25
26
  attr_reader :request
26
- # @var [Gruf::Error] error
27
+ # @!attribute [r] error
28
+ # @return [Gruf::Error] error
27
29
  attr_reader :error
28
- # @var [Hash] options
30
+ # @!attribute [r] options
31
+ # @return [Hash] options
29
32
  attr_reader :options
30
33
 
31
34
  ##
@@ -41,13 +44,3 @@ module Gruf
41
44
  end
42
45
  end
43
46
  end
44
-
45
- require_relative 'client_interceptor'
46
- require_relative 'server_interceptor'
47
- require_relative 'context'
48
- require_relative 'timer'
49
- require_relative 'active_record/connection_reset'
50
- require_relative 'authentication/basic'
51
- require_relative 'instrumentation/statsd'
52
- require_relative 'instrumentation/output_metadata_timer'
53
- require_relative 'instrumentation/request_logging/interceptor'
@@ -38,7 +38,7 @@ module Gruf
38
38
  def intercept!(&block)
39
39
  return yield if @interceptors.none?
40
40
 
41
- i = @interceptors.pop
41
+ i = @interceptors.shift
42
42
  return yield unless i
43
43
 
44
44
  logger.debug "Intercepting request with interceptor: #{i.class}"
@@ -16,9 +16,6 @@
16
16
  # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
17
  #
18
18
  require 'socket'
19
- require_relative 'formatters/base'
20
- require_relative 'formatters/logstash'
21
- require_relative 'formatters/plain'
22
19
 
23
20
  module Gruf
24
21
  module Interceptors
@@ -28,8 +28,12 @@ module Gruf
28
28
  # @property [Float] elapsed The elapsed time of the request
29
29
  #
30
30
  class Result
31
- attr_reader :message,
32
- :elapsed
31
+ # @!attribute [r] message
32
+ # @return [Object] The returned protobuf message
33
+ attr_reader :message
34
+ # @return [r] elapsed
35
+ # @return [Float] The time elapsed for this interceptor to execute
36
+ attr_reader :elapsed
33
37
 
34
38
  ##
35
39
  # @param [Object] message The protobuf message
@@ -53,7 +57,7 @@ module Gruf
53
57
  # @return [String] The name of the message class
54
58
  #
55
59
  def message_class_name
56
- @message.class.name
60
+ @message.class.name.to_s
57
61
  end
58
62
 
59
63
  ##
@@ -0,0 +1,32 @@
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
+ ##
20
+ # Handles internal gruf logging requests
21
+ #
22
+ module Logger
23
+ ##
24
+ # Return the current Gruf logger
25
+ #
26
+ # @return [Logger]
27
+ #
28
+ def logger
29
+ Gruf.logger
30
+ end
31
+ end
32
+ end
@@ -21,15 +21,20 @@ module Gruf
21
21
  # Encapsulates the context of an outbound client request
22
22
  #
23
23
  class RequestContext
24
- # @var [Symbol]
24
+ # @!attribute [r] type
25
+ # @return [Symbol]
25
26
  attr_reader :type
26
- # @var [Enumerable] requests
27
+ # @!attribute [r] requests
28
+ # @return [Enumerable] requests
27
29
  attr_reader :requests
28
- # @var [GRPC::ActiveCall]
30
+ # @!attribute [r] call
31
+ # @return [GRPC::ActiveCall]
29
32
  attr_reader :call
30
- # @var [Method] method
33
+ # @!attribute [r] method
34
+ # @return [Method] method
31
35
  attr_reader :method
32
- # @var [Hash] metadata
36
+ # @!attribute [r] metadata
37
+ # @return [Hash] metadata
33
38
  attr_reader :metadata
34
39
 
35
40
  ##
@@ -55,7 +60,7 @@ module Gruf
55
60
  # @return [String]
56
61
  #
57
62
  def method_name
58
- @method.to_s.split('/').last
63
+ @method.to_s.split('/').last.to_s
59
64
  end
60
65
 
61
66
  ##
@@ -64,7 +69,7 @@ module Gruf
64
69
  # @return [String]
65
70
  #
66
71
  def route_key
67
- @method[1..-1].underscore.tr('/', '.')
72
+ @method[1..].to_s.underscore.tr('/', '.')
68
73
  end
69
74
  end
70
75
  end
data/lib/gruf/response.rb CHANGED
@@ -20,17 +20,23 @@ module Gruf
20
20
  # Wraps the active call operation to provide metadata and timing around the request
21
21
  #
22
22
  class Response
23
- # @return [GRPC::ActiveCall::Operation] The operation that was executed for the given request
23
+ # @!attribute [r] operation
24
+ # @return [GRPC::ActiveCall::Operation] The operation that was executed for the given request
24
25
  attr_reader :operation
25
- # @return [Hash] The metadata that was attached to the operation
26
+ # @!attribute [r] metadata
27
+ # @return [Hash] The metadata that was attached to the operation
26
28
  attr_reader :metadata
27
- # @return [Hash] The trailing metadata that the service returned
29
+ # @!attribute [r] trailing_metadata
30
+ # @return [Hash] The trailing metadata that the service returned
28
31
  attr_reader :trailing_metadata
29
- # @return [Time] The set deadline on the call
32
+ # @!attribute [r] deadline
33
+ # @return [Time] The set deadline on the call
30
34
  attr_reader :deadline
31
- # @return [Boolean] Whether or not the operation was cancelled
35
+ # @!attribute [r] cancelled
36
+ # @return [Boolean] Whether or not the operation was cancelled
32
37
  attr_reader :cancelled
33
- # @return [Float] The time that the request took to execute
38
+ # @!attribute [r] execution_time
39
+ # @return [Float] The time that the request took to execute
34
40
  attr_reader :execution_time
35
41
 
36
42
  ##
@@ -22,7 +22,8 @@ module Gruf
22
22
  # Base class for serialization of errors for transport across the grpc protocol
23
23
  #
24
24
  class Base
25
- # @return [Gruf::Error|String] The error being serialized
25
+ # @!attribute [r] error
26
+ # @return [Gruf::Error|String] The error being serialized
26
27
  attr_reader :error
27
28
 
28
29
  ##