gruf 1.2.7 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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