gruf 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,124 @@
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
+ # Represents configuration settings for the system
20
+ #
21
+ module Configuration
22
+ VALID_CONFIG_KEYS = {
23
+ root_path: '',
24
+ server_binding_url: '0.0.0.0:9001',
25
+ authentication_options: {},
26
+ instrumentation_options: {},
27
+ hook_options: {},
28
+ default_client_host: '',
29
+ use_ssl: false,
30
+ ssl_crt_file: '',
31
+ ssl_key_file: '',
32
+ servers_path: '',
33
+ services: [],
34
+ logger: nil,
35
+ grpc_logger: nil,
36
+ error_metadata_key: :'error-internal-bin',
37
+ error_serializer: nil,
38
+ authorization_metadata_key: 'authorization',
39
+ append_server_errors_to_trailing_metadata: true,
40
+ use_default_hooks: true,
41
+ backtrace_on_error: false
42
+ }.freeze
43
+
44
+ attr_accessor *VALID_CONFIG_KEYS.keys
45
+
46
+ ##
47
+ # Whenever this is extended into a class, setup the defaults
48
+ #
49
+ def self.extended(base)
50
+ base.reset
51
+ end
52
+
53
+ ##
54
+ # Yield self for ruby-style initialization
55
+ #
56
+ # @yields [Gruf::Configuration]
57
+ # @return [Gruf::Configuration]
58
+ #
59
+ def configure
60
+ yield self
61
+ end
62
+
63
+ ##
64
+ # Return the current configuration options as a Hash
65
+ #
66
+ # @return [Hash]
67
+ #
68
+ def options
69
+ opts = {}
70
+ VALID_CONFIG_KEYS.each do |k, _v|
71
+ opts.merge!(k => send(k))
72
+ end
73
+ opts
74
+ end
75
+
76
+ ##
77
+ # Set the default configuration onto the extended class
78
+ #
79
+ # @return [Hash] options The reset options hash
80
+ #
81
+ def reset
82
+ VALID_CONFIG_KEYS.each do |k, v|
83
+ send((k.to_s + '=').to_sym, v)
84
+ end
85
+ if defined?(Rails) && Rails.logger
86
+ self.root_path = Rails.root
87
+ self.logger = Rails.logger
88
+ else
89
+ require 'logger'
90
+ self.logger = ::Logger.new(STDOUT)
91
+ end
92
+ self.grpc_logger = logger if grpc_logger.nil?
93
+ self.ssl_crt_file = "#{root_path}/config/ssl/#{environment}.crt"
94
+ self.ssl_key_file = "#{root_path}/config/ssl/#{environment}.key"
95
+ self.servers_path = "#{root_path}/app/rpc"
96
+ self.authentication_options = {
97
+ credentials: [{
98
+ username: 'grpc',
99
+ password: 'magic'
100
+ }]
101
+ }
102
+ if use_default_hooks
103
+ Gruf::Hooks::Registry.add(:ar_connection_reset, Gruf::Hooks::ActiveRecord::ConnectionReset)
104
+ Gruf::Instrumentation::Registry.add(:output_metadata_timer, Gruf::Instrumentation::OutputMetadataTimer)
105
+ end
106
+ options
107
+ end
108
+
109
+ private
110
+
111
+ ##
112
+ # Automatically determine environment
113
+ #
114
+ # @return [String]
115
+ #
116
+ def environment
117
+ if defined?(Rails)
118
+ Rails.env.to_s
119
+ else
120
+ (ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development').to_s
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,162 @@
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
+ require_relative 'errors/field'
18
+ require_relative 'errors/debug_info'
19
+ require_relative 'serializers/errors/base'
20
+ require_relative 'serializers/errors/json'
21
+
22
+ module Gruf
23
+ ##
24
+ # Represents a error that can be transformed into a gRPC error and have metadata attached to the trailing headers.
25
+ # This layer acts as an middle layer that can have metadata injection, tracing support, and other functionality
26
+ # not present in the gRPC core.
27
+ #
28
+ class Error
29
+ include Gruf::Loggable
30
+
31
+ TYPES = {
32
+ ok: GRPC::Ok,
33
+ cancelled: GRPC::Cancelled,
34
+ unknown: GRPC::Unknown,
35
+ invalid_argument: GRPC::InvalidArgument,
36
+ bad_request: GRPC::InvalidArgument,
37
+ deadline_exceeded: GRPC::DeadlineExceeded,
38
+ not_found: GRPC::NotFound,
39
+ already_exists: GRPC::AlreadyExists,
40
+ unauthorized: GRPC::PermissionDenied,
41
+ permission_denied: GRPC::PermissionDenied,
42
+ unauthenticated: GRPC::Unauthenticated,
43
+ resource_exhausted: GRPC::ResourceExhausted,
44
+ failed_precondition: GRPC::FailedPrecondition,
45
+ aborted: GRPC::Aborted,
46
+ out_of_range: GRPC::OutOfRange,
47
+ unimplemented: GRPC::Unimplemented,
48
+ internal: GRPC::Internal,
49
+ unavailable: GRPC::Unavailable,
50
+ data_loss: GRPC::DataLoss
51
+ }.freeze
52
+
53
+ attr_accessor :code, :app_code, :message, :field_errors, :debug_info, :grpc_error
54
+ attr_reader :metadata
55
+
56
+ ##
57
+ # Initialize the error, setting default values
58
+ #
59
+ def initialize(args = {})
60
+ args.each do |k, v|
61
+ send("#{k.to_sym}=", v) if respond_to?(k.to_sym)
62
+ end
63
+ @field_errors = []
64
+ end
65
+
66
+ ##
67
+ # Add a field error to this error package
68
+ #
69
+ # @param [Symbol] field_name
70
+ # @param [Symbol] error_code
71
+ # @param [String] message
72
+ #
73
+ def add_field_error(field_name, error_code, message = '')
74
+ @field_errors << Errors::Field.new(field_name, error_code, message)
75
+ end
76
+
77
+ ##
78
+ # @param [String] detail
79
+ # @param [Array<String>] stack_trace
80
+ #
81
+ def set_debug_info(detail, stack_trace = [])
82
+ @debug_info = Errors::DebugInfo.new(detail, stack_trace)
83
+ end
84
+
85
+ ##
86
+ # Ensure all metadata values are strings
87
+ #
88
+ def metadata=(md)
89
+ @metadata = md.map { |k, str| [k, str.to_s] }.to_h
90
+ end
91
+
92
+ ##
93
+ # Serialize the error for transport
94
+ #
95
+ # @return [String]
96
+ #
97
+ def serialize
98
+ serializer = serializer_class.new(self)
99
+ serializer.serialize.to_s
100
+ end
101
+
102
+ ##
103
+ # @param [GRPC::ActiveCall]
104
+ # @return [Error]
105
+ #
106
+ def attach_to_call(active_call)
107
+ metadata[Gruf.error_metadata_key.to_sym] = serialize if Gruf.append_server_errors_to_trailing_metadata
108
+ if !metadata.empty? && active_call && active_call.respond_to?(:output_metadata)
109
+ active_call.output_metadata.update(metadata)
110
+ end
111
+ self
112
+ end
113
+
114
+ ##
115
+ # @param [GRPC::ActiveCall]
116
+ # @return [GRPC::BadStatus]
117
+ #
118
+ def fail!(active_call)
119
+ raise attach_to_call(active_call).grpc_error
120
+ end
121
+
122
+ ##
123
+ # @return [Hash]
124
+ #
125
+ def to_h
126
+ {
127
+ code: code,
128
+ app_code: app_code,
129
+ message: message,
130
+ field_errors: field_errors.map(&:to_h),
131
+ debug_info: debug_info.to_h
132
+ }
133
+ end
134
+
135
+ ##
136
+ # @return [GRPC::BadStatus]
137
+ #
138
+ def grpc_error
139
+ @grpc_error = grpc_class.new(message, **@metadata)
140
+ end
141
+
142
+ private
143
+
144
+ ##
145
+ # @return [Gruf::Serializers::Errors::Base]
146
+ #
147
+ def serializer_class
148
+ if Gruf.error_serializer
149
+ Gruf.error_serializer.is_a?(Class) ? Gruf.error_serializer : Gruf.error_serializer.to_s.constantize
150
+ else
151
+ Gruf::Serializers::Errors::Json
152
+ end
153
+ end
154
+
155
+ ##
156
+ # @return [Class]
157
+ #
158
+ def grpc_class
159
+ TYPES[code]
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,45 @@
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
+ module Errors
19
+ ##
20
+ # Represents debugging information
21
+ #
22
+ class DebugInfo
23
+ attr_reader :detail, :stack_trace
24
+
25
+ ##
26
+ # @param [String] detail
27
+ # @param [Array<String>] stack_trace
28
+ #
29
+ def initialize(detail, stack_trace = [])
30
+ @detail = detail
31
+ @stack_trace = stack_trace.is_a?(String) ? stack_trace.split("\n") : stack_trace
32
+ end
33
+
34
+ ##
35
+ # @return [Hash]
36
+ #
37
+ def to_h
38
+ {
39
+ detail: detail,
40
+ stack_trace: stack_trace
41
+ }
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,48 @@
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
+ module Errors
19
+ ##
20
+ # Represents a field-specific error
21
+ #
22
+ class Field
23
+ attr_reader :field_name, :error_code, :message
24
+
25
+ ##
26
+ # @param [Symbol] field_name
27
+ # @param [Symbol] error_code
28
+ # @param [String] message
29
+ #
30
+ def initialize(field_name, error_code, message = '')
31
+ @field_name = field_name
32
+ @error_code = error_code
33
+ @message = message
34
+ end
35
+
36
+ ##
37
+ # @return [Hash]
38
+ #
39
+ def to_h
40
+ {
41
+ field_name: field_name,
42
+ error_code: error_code,
43
+ message: message
44
+ }
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,36 @@
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
+ module Hooks
19
+ module ActiveRecord
20
+ ##
21
+ # Resets the ActiveRecord connection to maintain accurate connected state in the thread pool
22
+ #
23
+ class ConnectionReset < Gruf::Hooks::Base
24
+ def after(_success, _response, _call_signature, _req, _call)
25
+ ::ActiveRecord::Base.clear_active_connections! if enabled?
26
+ end
27
+
28
+ private
29
+
30
+ def enabled?
31
+ defined?(::ActiveRecord::Base)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,44 @@
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
+ module Hooks
19
+ ##
20
+ # Base class for a hook. Define before, around, or after methods to utilize functionality.
21
+ #
22
+ class Base
23
+ include Gruf::Loggable
24
+
25
+ attr_reader :options, :service
26
+
27
+ ##
28
+ # @param [Gruf::Service] service
29
+ # @param [Hash] options
30
+ #
31
+ def initialize(service, options = {})
32
+ @service = service
33
+ @options = options
34
+ setup
35
+ end
36
+
37
+ ##
38
+ # Method that can be used to setup the hook prior to running it
39
+ def setup
40
+ # noop
41
+ end
42
+ end
43
+ end
44
+ end