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,65 +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
- module Authentication
19
- ##
20
- # Base interface for Authentication strategies. All derived strategies must define the `valid?` method.
21
- #
22
- class Base
23
- include Gruf::Loggable
24
-
25
- # @return [String] The credentials sent in the request
26
- attr_reader :credentials
27
- # @return [Hash] A hash of authentication options
28
- attr_reader :options
29
-
30
- ##
31
- # Initialize the authentication middleware
32
- #
33
- # @param [String] credentials The credentials sent in the request
34
- # @param [Hash] options A hash of authentication options
35
- #
36
- def initialize(credentials, options = {})
37
- opts = Gruf.authentication_options || {}
38
- @credentials = credentials
39
- @options = opts.merge(options)
40
- end
41
-
42
- ##
43
- # Verify the credentials. Helper class method.
44
- #
45
- # @param [GRPC::ActiveCall] call The gRPC active call for the given operation
46
- # @param [String] credentials The credentials sent in the request
47
- # @param [Hash] options A hash of authentication options
48
- #
49
- def self.verify(call, credentials = '', options = {})
50
- new(credentials, options).valid?(call)
51
- end
52
-
53
- ##
54
- # Abstract method that is required to be implemented in every derivative class.
55
- # Return true if the call is authenticated, false if a permission denied error is to be sent.
56
- #
57
- # @param [GRPC::ActiveCall] _call The gRPC active call for the given operation
58
- # @return [Boolean] True if the call was authenticated
59
- #
60
- def valid?(_call)
61
- raise NotImplementedError
62
- end
63
- end
64
- end
65
- end
@@ -1,74 +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
- require 'base64'
18
-
19
- module Gruf
20
- module Authentication
21
- ##
22
- # Handles basic authentication for gRPC requests
23
- #
24
- class Basic < Base
25
- ##
26
- # @param [GRPC::ActiveCall] _call The gRPC active call for the given operation
27
- # @return [Boolean] True if the basic authentication was valid
28
- #
29
- def valid?(_call)
30
- server_credentials.any? do |cred|
31
- username = cred.fetch(:username, '').to_s
32
- password = cred.fetch(:password, '').to_s
33
- if username.empty?
34
- request_password == password
35
- else
36
- request_credentials == "#{username}:#{password}"
37
- end
38
- end
39
- end
40
-
41
- private
42
-
43
- ##
44
- # @return [Array<Hash>] An array of valid server credentials for this service
45
- #
46
- def server_credentials
47
- options.fetch(:credentials, [])
48
- end
49
-
50
- ##
51
- # @return [String] The decoded request credentials
52
- #
53
- def request_credentials
54
- Base64.decode64(credentials.to_s.gsub('Basic ', '').strip)
55
- end
56
-
57
- ##
58
- # @return [String] The decoded request username
59
- # @deprecated
60
- # :nocov:
61
- def request_username
62
- @request_username ||= request_credentials.split(':').first
63
- end
64
- # :nocov:
65
-
66
- ##
67
- # @return [String] The decoded request password
68
- #
69
- def request_password
70
- @request_password ||= request_credentials.split(':').last
71
- end
72
- end
73
- end
74
- end
@@ -1,107 +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
- module Authentication
19
- ##
20
- # Provides a modifiable repository of strategies for authentication
21
- #
22
- class Strategies
23
- ##
24
- # Error class that represents when a strategy does not extend the base class
25
- #
26
- class StrategyDescendantError < StandardError; end
27
-
28
- class << self
29
- ##
30
- # Add an authentication strategy, either through a class or a block
31
- #
32
- # @param [String] name The vanity name of the strategy being loaded
33
- # @param [Class|NilClass] strategy (Optional) The class that represents the strategy
34
- # @param [Proc] &block If given, will attempt to build a strategy class from the base class with this block
35
- # @return [Class] The loaded strategy
36
- #
37
- def add(name, strategy = nil, &block)
38
- base = Gruf::Authentication::Base
39
- strategy ||= Class.new(base)
40
- strategy.class_eval(&block) if block_given?
41
-
42
- # all strategies require the valid? method
43
- raise NoMethodError unless strategy.method_defined?(:valid?)
44
-
45
- raise StrategyDescendantError, "Strategies must descend from #{base}" unless strategy.ancestors.include?(base)
46
-
47
- _strategies[name.to_sym] = strategy
48
- end
49
-
50
- ##
51
- # Return a strategy via a hash accessor syntax
52
- #
53
- # @param [Symbol] label The name of the strategy
54
- # @return [Gruf::Authentication::Base] The requested strategy
55
- #
56
- def [](label)
57
- _strategies[label.to_sym]
58
- end
59
-
60
- ##
61
- # Iterate over each strategy and yield it to the caller
62
- #
63
- def each
64
- _strategies.each do |s|
65
- yield s
66
- end
67
- end
68
-
69
- ##
70
- # Return the loaded strategies as a hash
71
- #
72
- # @return [Hash<Class>] A name/strategy pair of loaded strategies
73
- #
74
- def to_h
75
- _strategies
76
- end
77
-
78
- ##
79
- # Return if there are any loaded strategies
80
- #
81
- # @return [Boolean] True if there are loaded strategies
82
- #
83
- def any?
84
- to_h.keys.count > 0
85
- end
86
-
87
- ##
88
- # Clear all given strategies
89
- #
90
- # @return [Hash] The newly empty hash
91
- #
92
- def clear
93
- @strategies = {}
94
- end
95
-
96
- private
97
-
98
- ##
99
- # @return [Hash<Class>]
100
- #
101
- def _strategies
102
- @strategies ||= {}
103
- end
104
- end
105
- end
106
- end
107
- end
@@ -1,66 +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
- module Hooks
19
- ##
20
- # Base class for a hook. Define before, around, outer_around, or after methods to utilize functionality.
21
- #
22
- class Base
23
- include Gruf::Loggable
24
-
25
- # @return [Gruf::Service] service The service to perform the hook against
26
- attr_reader :service
27
- # @return [Hash] options Options to use for the hook
28
- attr_reader :options
29
-
30
- ##
31
- # Initialize the hook and run setup
32
- #
33
- # @param [Gruf::Service] service The gruf service that the hook will perform against
34
- # @param [Hash] options (Optional) A hash of options for this hook
35
- #
36
- def initialize(service, options = {})
37
- @service = service
38
- @options = options
39
- setup
40
- end
41
-
42
- ##
43
- # Method that can be used to setup the hook prior to running it
44
- #
45
- def setup
46
- # noop
47
- end
48
-
49
- ##
50
- # @return [String] Returns the service name as a translated name separated by periods
51
- #
52
- def service_key
53
- service.class.name.underscore.tr('/', '.')
54
- end
55
-
56
- ##
57
- # Parse the method signature into a service.method name format
58
- #
59
- # @return [String] The parsed service method name
60
- #
61
- def method_key(call_signature)
62
- "#{service_key}.#{call_signature.to_s.gsub('_without_intercept', '')}"
63
- end
64
- end
65
- end
66
- end
@@ -1,110 +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
- require_relative 'base'
18
- require_relative 'active_record/connection_reset'
19
-
20
- module Gruf
21
- module Hooks
22
- ##
23
- # Registry of all hooks added
24
- #
25
- class Registry
26
- ##
27
- # Error class that represents when a gruf hook does not extend the base class
28
- #
29
- class HookDescendantError < StandardError; end
30
-
31
- class << self
32
- ##
33
- # Add an authentication strategy, either through a class or a block
34
- #
35
- # @param [String] name The name to represent the hook as
36
- # @param [Gruf::Hooks::Base|NilClass] hook The strategy class to add. If nil, will expect
37
- # a block that can be built as a hook instead
38
- # @return [Class] The hook that was added
39
- #
40
- def add(name, hook = nil, &block)
41
- base = Gruf::Hooks::Base
42
- hook ||= Class.new(base)
43
- hook.class_eval(&block) if block_given?
44
-
45
- # all hooks require either the before, after, or around method
46
- raise NoMethodError unless hook.method_defined?(:before) || hook.method_defined?(:after) || hook.method_defined?(:around) || hook.method_defined?(:outer_around)
47
-
48
- raise HookDescendantError, "Hooks must descend from #{base}" unless hook.ancestors.include?(base)
49
-
50
- _registry[name.to_sym] = hook
51
- end
52
-
53
- ##
54
- # Return a hook via a hash accessor syntax
55
- #
56
- # @return [Gruf::Instrumentation::Base|NilClass] The requested hook, if exists
57
- #
58
- def [](name)
59
- _registry[name.to_sym]
60
- end
61
-
62
- ##
63
- # Iterate over each hook in the registry
64
- #
65
- def each
66
- _registry.each do |name, s|
67
- yield name, s
68
- end
69
- end
70
-
71
- ##
72
- # @return [Hash<Class>] Return the registry represented as a Hash object
73
- #
74
- def to_h
75
- _registry
76
- end
77
-
78
- ##
79
- # @return [Boolean] Return true if there are any registered hooks
80
- #
81
- def any?
82
- count > 0
83
- end
84
-
85
- ##
86
- # @return [Integer] Return the number of registered hooks
87
- #
88
- def count
89
- to_h.keys.count
90
- end
91
-
92
- ##
93
- # @return [Hash] Clear all existing hooks from the registry
94
- #
95
- def clear
96
- @_registry = {}
97
- end
98
-
99
- private
100
-
101
- ##
102
- # @return [Hash<Class>] Return the current registry
103
- #
104
- def _registry
105
- @_registry ||= {}
106
- end
107
- end
108
- end
109
- end
110
- end
@@ -1,114 +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
- require_relative 'request_context'
18
-
19
- module Gruf
20
- module Instrumentation
21
- ##
22
- # Base class for an instrumentation strategy. Define a call method to utilize functionality.
23
- #
24
- class Base
25
- include Gruf::Loggable
26
-
27
- # @return [Gruf::Service] service The service to instrument
28
- attr_reader :service
29
- # @return [Hash] options Options to use when instrumenting the call
30
- attr_reader :options
31
-
32
- ##
33
- # @param [Gruf::Service] service The service to instrument
34
- # @param [Hash] options (Optional) Options to use when instrumenting the call
35
- #
36
- def initialize(service, options = {})
37
- @service = service
38
- @options = options
39
- setup
40
- end
41
-
42
- ##
43
- # Useful for setting up an instrumentation module post instantiation
44
- #
45
- def setup
46
- # noop
47
- end
48
-
49
- ##
50
- # Was this call a success? If a response is a GRPC::BadStatus object, we assume that it was unsuccessful
51
- #
52
- # @param [Object] response The gRPC response object
53
- # @return [Boolean] True if was a successful call
54
- #
55
- def success?(response)
56
- !response.is_a?(GRPC::BadStatus)
57
- end
58
-
59
- ##
60
- # Abstract method that is required for implementing an instrumentation strategy.
61
- #
62
- # @abstract
63
- # @param [Gruf::Instrumentation::RequestContext] _rc The current request context for the call
64
- #
65
- def call(_rc)
66
- raise NotImplementedError
67
- end
68
-
69
- ##
70
- # Hook into the outer_around call to time the request, and pass that to the call method
71
- #
72
- # @param [Symbol] call_signature The method being called
73
- # @param [Object] request The request object
74
- # @param [GRPC::ActiveCall] active_call The gRPC active call object
75
- # @param [Proc] &_block The execution block for the call
76
- # @return [Object] result The result of the block that was called
77
- #
78
- def outer_around(call_signature, request, active_call, &_block)
79
- timed = Timer.time do
80
- yield
81
- end
82
- rc = RequestContext.new(
83
- service: service,
84
- request: request,
85
- response: timed.result,
86
- execution_time: timed.time,
87
- call_signature: call_signature,
88
- active_call: active_call
89
- )
90
- call(rc)
91
- raise rc.response unless rc.success?
92
- rc.response
93
- end
94
-
95
- ##
96
- # @return [String] Returns the service name as a translated name separated by periods
97
- #
98
- def service_key
99
- service.class.name.underscore.tr('/', '.')
100
- end
101
-
102
- ##
103
- # Parse the method signature into a service.method name format
104
- #
105
- # @param [Symbol] call_signature The method call signature
106
- # @param [String] delimiter The delimiter to separate service and method keys
107
- # @return [String] The parsed service method name
108
- #
109
- def method_key(call_signature, delimiter: '.')
110
- "#{service_key}#{delimiter}#{call_signature}"
111
- end
112
- end
113
- end
114
- end