gruf 1.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.
@@ -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