gapic-common 0.20.0 → 1.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +77 -0
- data/README.md +5 -5
- data/lib/gapic/call_options/error_codes.rb +2 -60
- data/lib/gapic/call_options/retry_policy.rb +10 -109
- data/lib/gapic/call_options.rb +1 -1
- data/lib/gapic/common/error_codes.rb +74 -0
- data/lib/gapic/common/polling_harness.rb +82 -0
- data/lib/gapic/common/retry_policy.rb +255 -0
- data/lib/gapic/common/version.rb +1 -1
- data/lib/gapic/common.rb +1 -0
- data/lib/gapic/config.rb +2 -2
- data/lib/gapic/grpc/service_stub/channel.rb +4 -4
- data/lib/gapic/grpc/service_stub/channel_pool.rb +9 -2
- data/lib/gapic/grpc/service_stub/rpc_call.rb +120 -12
- data/lib/gapic/grpc/service_stub.rb +39 -12
- data/lib/gapic/headers.rb +5 -1
- data/lib/gapic/logging_concerns.rb +214 -0
- data/lib/gapic/operation/retry_policy.rb +25 -65
- data/lib/gapic/paged_enumerable.rb +1 -1
- data/lib/gapic/rest/client_stub.rb +115 -25
- data/lib/gapic/rest/http_binding_override_configuration.rb +48 -0
- data/lib/gapic/rest.rb +1 -0
- data/lib/gapic/universe_domain_concerns.rb +84 -0
- metadata +35 -171
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# Copyright 2025 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
require "gapic/common/error_codes"
|
|
16
|
+
|
|
17
|
+
module Gapic
|
|
18
|
+
module Common
|
|
19
|
+
##
|
|
20
|
+
# Gapic Common retry policy base class.
|
|
21
|
+
#
|
|
22
|
+
class RetryPolicy
|
|
23
|
+
# @return [Numeric] Default initial delay in seconds.
|
|
24
|
+
DEFAULT_INITIAL_DELAY = 1
|
|
25
|
+
# @return [Numeric] Default maximum delay in seconds.
|
|
26
|
+
DEFAULT_MAX_DELAY = 15
|
|
27
|
+
# @return [Numeric] Default delay scaling factor for subsequent retry attempts.
|
|
28
|
+
DEFAULT_MULTIPLIER = 1.3
|
|
29
|
+
# @return [Array<String|Integer>] Default list of retry codes.
|
|
30
|
+
DEFAULT_RETRY_CODES = [].freeze
|
|
31
|
+
# @return [Numeric] Default timeout threshold value in seconds.
|
|
32
|
+
DEFAULT_TIMEOUT = 3600 # One hour
|
|
33
|
+
|
|
34
|
+
##
|
|
35
|
+
# Create new Gapic::Common::RetryPolicy instance.
|
|
36
|
+
#
|
|
37
|
+
# @param initial_delay [Numeric] Initial delay in seconds.
|
|
38
|
+
# @param max_delay [Numeric] Maximum delay in seconds.
|
|
39
|
+
# @param multiplier [Numeric] The delay scaling factor for each subsequent retry attempt.
|
|
40
|
+
# @param retry_codes [Array<String|Integer>] List of retry codes.
|
|
41
|
+
# @param timeout [Numeric] Timeout threshold value in seconds.
|
|
42
|
+
#
|
|
43
|
+
def initialize initial_delay: nil, max_delay: nil, multiplier: nil, retry_codes: nil, timeout: nil
|
|
44
|
+
# Instance values are set as `nil` to determine whether values are overriden from default.
|
|
45
|
+
@initial_delay = initial_delay
|
|
46
|
+
@max_delay = max_delay
|
|
47
|
+
@multiplier = multiplier
|
|
48
|
+
@retry_codes = convert_codes retry_codes
|
|
49
|
+
@timeout = timeout
|
|
50
|
+
start!
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @return [Numeric] Initial delay in seconds.
|
|
54
|
+
def initial_delay
|
|
55
|
+
@initial_delay || DEFAULT_INITIAL_DELAY
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# @return [Numeric] Maximum delay in seconds.
|
|
59
|
+
def max_delay
|
|
60
|
+
@max_delay || DEFAULT_MAX_DELAY
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# @return [Numeric] The delay scaling factor for each subsequent retry attempt.
|
|
64
|
+
def multiplier
|
|
65
|
+
@multiplier || DEFAULT_MULTIPLIER
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# @return [Array<Integer>] List of retry codes.
|
|
69
|
+
def retry_codes
|
|
70
|
+
@retry_codes || DEFAULT_RETRY_CODES
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# @return [Numeric] Timeout threshold value in seconds.
|
|
74
|
+
def timeout
|
|
75
|
+
@timeout || DEFAULT_TIMEOUT
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
##
|
|
79
|
+
# Returns a duplicate in a non-executing state, i.e. with the deadline
|
|
80
|
+
# and current delay reset.
|
|
81
|
+
#
|
|
82
|
+
# @return [RetryPolicy]
|
|
83
|
+
#
|
|
84
|
+
def dup
|
|
85
|
+
RetryPolicy.new initial_delay: @initial_delay,
|
|
86
|
+
max_delay: @max_delay,
|
|
87
|
+
multiplier: @multiplier,
|
|
88
|
+
retry_codes: @retry_codes,
|
|
89
|
+
timeout: @timeout
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
##
|
|
93
|
+
# Perform delay if and only if retriable.
|
|
94
|
+
#
|
|
95
|
+
# If positional argument `error` is provided, the retriable logic uses
|
|
96
|
+
# `retry_codes`. Otherwise, `timeout` is used.
|
|
97
|
+
#
|
|
98
|
+
# @return [Boolean] Whether the delay was executed.
|
|
99
|
+
#
|
|
100
|
+
def call error = nil
|
|
101
|
+
should_retry = error.nil? ? retry_with_deadline? : retry_error?(error)
|
|
102
|
+
return false unless should_retry
|
|
103
|
+
perform_delay!
|
|
104
|
+
end
|
|
105
|
+
alias perform_delay call
|
|
106
|
+
|
|
107
|
+
##
|
|
108
|
+
# Perform delay.
|
|
109
|
+
#
|
|
110
|
+
# @return [Boolean] Whether the delay was executed.
|
|
111
|
+
#
|
|
112
|
+
def perform_delay!
|
|
113
|
+
delay!
|
|
114
|
+
increment_delay!
|
|
115
|
+
@perform_delay_count += 1
|
|
116
|
+
true
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
##
|
|
120
|
+
# Current delay value in seconds.
|
|
121
|
+
#
|
|
122
|
+
# @return [Numeric] Time delay in seconds.
|
|
123
|
+
#
|
|
124
|
+
def delay
|
|
125
|
+
@delay
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
##
|
|
129
|
+
# Current number of times the delay has been performed
|
|
130
|
+
#
|
|
131
|
+
# @return [Integer]
|
|
132
|
+
#
|
|
133
|
+
def perform_delay_count
|
|
134
|
+
@perform_delay_count
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
##
|
|
138
|
+
# Start tracking the deadline and delay by initializing those values.
|
|
139
|
+
#
|
|
140
|
+
# This is normally done when the object is constructed, but it can be
|
|
141
|
+
# done explicitly in order to reinitialize the state in case this
|
|
142
|
+
# retry policy was created in the past or is being reused.
|
|
143
|
+
#
|
|
144
|
+
# @param mock_delay [boolean,Proc] if truthy, delays are "mocked",
|
|
145
|
+
# meaning they do not actually take time, but are measured as if they
|
|
146
|
+
# did, which is useful for tests. If set to a Proc, it will be called
|
|
147
|
+
# whenever a delay would happen, and passed the delay in seconds,
|
|
148
|
+
# also useful for testing.
|
|
149
|
+
#
|
|
150
|
+
def start! mock_delay: false
|
|
151
|
+
@mock_time = mock_delay ? Process.clock_gettime(Process::CLOCK_MONOTONIC) : nil
|
|
152
|
+
@mock_delay_callback = mock_delay.respond_to?(:call) ? mock_delay : nil
|
|
153
|
+
@deadline = cur_time + timeout
|
|
154
|
+
@delay = initial_delay
|
|
155
|
+
@perform_delay_count = 0
|
|
156
|
+
self
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
##
|
|
160
|
+
# @private
|
|
161
|
+
# @return [Boolean] Whether this error should be retried.
|
|
162
|
+
#
|
|
163
|
+
def retry_error? error
|
|
164
|
+
(defined?(::GRPC) && error.is_a?(::GRPC::BadStatus) && retry_codes.include?(error.code)) ||
|
|
165
|
+
(error.respond_to?(:response_status) &&
|
|
166
|
+
retry_codes.include?(ErrorCodes.grpc_error_for(error.response_status)))
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# @private
|
|
170
|
+
# @return [Boolean] Whether this policy should be retried based on the deadline.
|
|
171
|
+
def retry_with_deadline?
|
|
172
|
+
deadline > cur_time
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
##
|
|
176
|
+
# @private
|
|
177
|
+
# Apply default values to the policy object. This does not replace user-provided values,
|
|
178
|
+
# it only overrides empty values.
|
|
179
|
+
#
|
|
180
|
+
# @param retry_policy [Hash] The policy for error retry. Keys must match the arguments for
|
|
181
|
+
# {Gapic::Common::RetryPolicy.new}.
|
|
182
|
+
#
|
|
183
|
+
def apply_defaults retry_policy
|
|
184
|
+
return unless retry_policy.is_a? Hash
|
|
185
|
+
@retry_codes ||= convert_codes retry_policy[:retry_codes]
|
|
186
|
+
@initial_delay ||= retry_policy[:initial_delay]
|
|
187
|
+
@multiplier ||= retry_policy[:multiplier]
|
|
188
|
+
@max_delay ||= retry_policy[:max_delay]
|
|
189
|
+
|
|
190
|
+
self
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# @private Equality test
|
|
194
|
+
def eql? other
|
|
195
|
+
other.is_a?(RetryPolicy) &&
|
|
196
|
+
other.initial_delay == initial_delay &&
|
|
197
|
+
other.max_delay == max_delay &&
|
|
198
|
+
other.multiplier == multiplier &&
|
|
199
|
+
other.retry_codes == retry_codes &&
|
|
200
|
+
other.timeout == timeout
|
|
201
|
+
end
|
|
202
|
+
alias == eql?
|
|
203
|
+
|
|
204
|
+
# @private Hash code
|
|
205
|
+
def hash
|
|
206
|
+
[initial_delay, max_delay, multiplier, retry_codes, timeout].hash
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
private
|
|
210
|
+
|
|
211
|
+
# @private
|
|
212
|
+
# @return [Numeric] The performed delay.
|
|
213
|
+
def delay!
|
|
214
|
+
if @mock_time
|
|
215
|
+
@mock_time += delay
|
|
216
|
+
@mock_delay_callback&.call delay
|
|
217
|
+
else
|
|
218
|
+
Kernel.sleep delay
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# @private
|
|
223
|
+
# @return [Numeric] The new delay in seconds.
|
|
224
|
+
def increment_delay!
|
|
225
|
+
@delay = [delay * multiplier, max_delay].min
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# @private
|
|
229
|
+
# @return [Numeric] The deadline for timeout-based policies.
|
|
230
|
+
def deadline
|
|
231
|
+
@deadline
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# @private
|
|
235
|
+
# Mockable way to get time.
|
|
236
|
+
def cur_time
|
|
237
|
+
@mock_time || Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# @private
|
|
241
|
+
# @return [Array<Integer> Error codes converted to their respective integer values.
|
|
242
|
+
def convert_codes input_codes
|
|
243
|
+
return nil if input_codes.nil?
|
|
244
|
+
Array(input_codes).map do |obj|
|
|
245
|
+
case obj
|
|
246
|
+
when String
|
|
247
|
+
ErrorCodes::ERROR_STRING_MAPPING[obj]
|
|
248
|
+
when Integer
|
|
249
|
+
obj
|
|
250
|
+
end
|
|
251
|
+
end.compact
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
end
|
data/lib/gapic/common/version.rb
CHANGED
data/lib/gapic/common.rb
CHANGED
data/lib/gapic/config.rb
CHANGED
|
@@ -50,7 +50,7 @@ module Gapic
|
|
|
50
50
|
#
|
|
51
51
|
def config_attr name, default, *valid_values, &validator
|
|
52
52
|
name = String(name).to_sym
|
|
53
|
-
name_setter = "#{name}="
|
|
53
|
+
name_setter = :"#{name}="
|
|
54
54
|
raise NameError, "invalid config name #{name}" if name !~ /^[a-zA-Z]\w*$/ || name == :parent_config
|
|
55
55
|
raise NameError, "method #{name} already exists" if method_defined? name
|
|
56
56
|
raise NameError, "method #{name_setter} already exists" if method_defined? name_setter
|
|
@@ -58,7 +58,7 @@ module Gapic
|
|
|
58
58
|
raise ArgumentError, "validation must be provided" if validator.nil? && valid_values.empty?
|
|
59
59
|
validator ||= ->(value) { valid_values.any? { |v| v === value } }
|
|
60
60
|
|
|
61
|
-
name_ivar = "@#{name}"
|
|
61
|
+
name_ivar = :"@#{name}"
|
|
62
62
|
|
|
63
63
|
create_getter name_ivar, name, default
|
|
64
64
|
create_setter name_ivar, name_setter, default, validator
|
|
@@ -32,12 +32,13 @@ module Gapic
|
|
|
32
32
|
# Creates a new Channel instance
|
|
33
33
|
#
|
|
34
34
|
def initialize grpc_stub_class, endpoint:, credentials:, channel_args: nil, interceptors: nil,
|
|
35
|
-
on_channel_create: nil
|
|
35
|
+
on_channel_create: nil, stub_logger: nil
|
|
36
36
|
@grpc_stub_class = grpc_stub_class
|
|
37
37
|
@endpoint = endpoint
|
|
38
38
|
@credentials = credentials
|
|
39
39
|
@channel_args = Hash channel_args
|
|
40
40
|
@interceptors = Array interceptors
|
|
41
|
+
@stub_logger = stub_logger
|
|
41
42
|
@concurrent_streams = 0
|
|
42
43
|
@mutex = Mutex.new
|
|
43
44
|
setup_grpc_stub
|
|
@@ -88,11 +89,10 @@ module Gapic
|
|
|
88
89
|
def call_rpc method_name, request, options: nil, &block
|
|
89
90
|
@mutex.synchronize { @concurrent_streams += 1 }
|
|
90
91
|
begin
|
|
91
|
-
|
|
92
|
+
meth = @grpc_stub.method method_name
|
|
93
|
+
rpc_call = RpcCall.new meth, stub_logger: @stub_logger, method_name: method_name
|
|
92
94
|
response = rpc_call.call request, options: options, &block
|
|
93
95
|
response
|
|
94
|
-
rescue StandardError => e
|
|
95
|
-
raise e
|
|
96
96
|
ensure
|
|
97
97
|
@mutex.synchronize { @concurrent_streams -= 1 }
|
|
98
98
|
end
|
|
@@ -30,7 +30,12 @@ module Gapic
|
|
|
30
30
|
##
|
|
31
31
|
# Initialize an instance of ServiceStub::ChannelPool
|
|
32
32
|
#
|
|
33
|
-
def initialize grpc_stub_class,
|
|
33
|
+
def initialize grpc_stub_class,
|
|
34
|
+
endpoint:, credentials:,
|
|
35
|
+
channel_args: nil,
|
|
36
|
+
interceptors: nil,
|
|
37
|
+
config: nil,
|
|
38
|
+
stub_logger: nil
|
|
34
39
|
if credentials.is_a? ::GRPC::Core::Channel
|
|
35
40
|
raise ArgumentError, "Can't create a channel pool with GRPC::Core::Channel as credentials"
|
|
36
41
|
end
|
|
@@ -41,6 +46,7 @@ module Gapic
|
|
|
41
46
|
@channel_args = channel_args
|
|
42
47
|
@interceptors = interceptors
|
|
43
48
|
@config = config || Configuration.new
|
|
49
|
+
@stub_logger = stub_logger
|
|
44
50
|
|
|
45
51
|
@channels = (1..@config.channel_count).map { create_channel }
|
|
46
52
|
end
|
|
@@ -49,7 +55,8 @@ module Gapic
|
|
|
49
55
|
# Creates a new channel.
|
|
50
56
|
def create_channel
|
|
51
57
|
Channel.new @grpc_stub_class, endpoint: @endpoint, credentials: @credentials, channel_args: @channel_args,
|
|
52
|
-
interceptors: @interceptors, on_channel_create: @config.on_channel_create
|
|
58
|
+
interceptors: @interceptors, on_channel_create: @config.on_channel_create,
|
|
59
|
+
stub_logger: @stub_logger
|
|
53
60
|
end
|
|
54
61
|
|
|
55
62
|
##
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
require "gapic/call_options"
|
|
16
|
+
require "gapic/logging_concerns"
|
|
16
17
|
require "grpc/errors"
|
|
17
18
|
|
|
18
19
|
module Gapic
|
|
@@ -32,8 +33,11 @@ module Gapic
|
|
|
32
33
|
#
|
|
33
34
|
# @param stub_method [Proc] Used to make a bare rpc call.
|
|
34
35
|
#
|
|
35
|
-
def initialize stub_method
|
|
36
|
+
def initialize stub_method, stub_logger: nil, method_name: nil
|
|
36
37
|
@stub_method = stub_method
|
|
38
|
+
@stub_logger = stub_logger
|
|
39
|
+
@method_name = method_name
|
|
40
|
+
@request_id = LoggingConcerns.random_uuid4
|
|
37
41
|
end
|
|
38
42
|
|
|
39
43
|
##
|
|
@@ -44,7 +48,8 @@ module Gapic
|
|
|
44
48
|
# customize the options object, using keys that match the arguments for {Gapic::CallOptions.new}. This object
|
|
45
49
|
# should only be used once.
|
|
46
50
|
#
|
|
47
|
-
# @yield [response, operation] Access the response along with the RPC operation.
|
|
51
|
+
# @yield [response, operation] Access the response along with the RPC operation. Additionally, throwing
|
|
52
|
+
# `:response, obj` within the block will change the return value to `obj`.
|
|
48
53
|
# @yieldparam response [Object] The response object.
|
|
49
54
|
# @yieldparam operation [::GRPC::ActiveCall::Operation] The RPC operation for the response.
|
|
50
55
|
#
|
|
@@ -91,7 +96,7 @@ module Gapic
|
|
|
91
96
|
# )
|
|
92
97
|
# response = echo_call.call request, options: options
|
|
93
98
|
#
|
|
94
|
-
# @example Accessing the
|
|
99
|
+
# @example Accessing the RPC operation using a block:
|
|
95
100
|
# require "google/showcase/v1beta1/echo_pb"
|
|
96
101
|
# require "google/showcase/v1beta1/echo_services_pb"
|
|
97
102
|
# require "gapic"
|
|
@@ -107,8 +112,8 @@ module Gapic
|
|
|
107
112
|
# echo_call = Gapic::ServiceStub::RpcCall.new echo_stub.method :echo
|
|
108
113
|
#
|
|
109
114
|
# request = Google::Showcase::V1beta1::EchoRequest.new
|
|
110
|
-
# echo_call.call request do |
|
|
111
|
-
# operation.trailing_metadata
|
|
115
|
+
# metadata = echo_call.call request do |_response, operation|
|
|
116
|
+
# throw :response, operation.trailing_metadata
|
|
112
117
|
# end
|
|
113
118
|
#
|
|
114
119
|
def call request, options: nil
|
|
@@ -117,21 +122,27 @@ module Gapic
|
|
|
117
122
|
deadline = calculate_deadline options
|
|
118
123
|
metadata = options.metadata
|
|
119
124
|
|
|
125
|
+
try_number = 1
|
|
120
126
|
retried_exception = nil
|
|
121
127
|
begin
|
|
128
|
+
request = log_request request, metadata, try_number
|
|
122
129
|
operation = stub_method.call request, deadline: deadline, metadata: metadata, return_op: true
|
|
123
130
|
response = operation.execute
|
|
124
|
-
|
|
125
|
-
|
|
131
|
+
catch :response do
|
|
132
|
+
response = log_response response, try_number
|
|
133
|
+
yield response, operation if block_given?
|
|
134
|
+
response
|
|
135
|
+
end
|
|
126
136
|
rescue ::GRPC::DeadlineExceeded => e
|
|
137
|
+
log_response e, try_number
|
|
127
138
|
raise Gapic::GRPC::DeadlineExceededError.new e.message, root_cause: retried_exception
|
|
128
139
|
rescue StandardError => e
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
end
|
|
140
|
+
e = normalize_exception e
|
|
141
|
+
log_response e, try_number
|
|
132
142
|
|
|
133
143
|
if check_retry?(deadline) && options.retry_policy.call(e)
|
|
134
144
|
retried_exception = e
|
|
145
|
+
try_number += 1
|
|
135
146
|
retry
|
|
136
147
|
end
|
|
137
148
|
|
|
@@ -145,13 +156,110 @@ module Gapic
|
|
|
145
156
|
return if options.timeout.nil?
|
|
146
157
|
return if options.timeout.negative?
|
|
147
158
|
|
|
148
|
-
|
|
159
|
+
current_time + options.timeout
|
|
149
160
|
end
|
|
150
161
|
|
|
151
162
|
def check_retry? deadline
|
|
152
163
|
return true if deadline.nil?
|
|
153
164
|
|
|
154
|
-
deadline >
|
|
165
|
+
deadline > current_time
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def current_time
|
|
169
|
+
# An alternative way of saying Time.now without actually calling
|
|
170
|
+
# Time.now. This allows clients that replace Time.now (e.g. via the
|
|
171
|
+
# timecop gem) to do so without interfering with the deadline.
|
|
172
|
+
nanos = Process.clock_gettime Process::CLOCK_REALTIME, :nanosecond
|
|
173
|
+
secs_part = nanos / 1_000_000_000
|
|
174
|
+
nsecs_part = nanos % 1_000_000_000
|
|
175
|
+
Time.at secs_part, nsecs_part, :nanosecond
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def normalize_exception exception
|
|
179
|
+
if exception.is_a?(::GRPC::Unavailable) && /Signet::AuthorizationError/ =~ exception.message
|
|
180
|
+
exception = Gapic::GRPC::AuthorizationError.new exception.message.gsub(%r{^\d+:}, "")
|
|
181
|
+
end
|
|
182
|
+
exception
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def log_request request, metadata, try_number
|
|
186
|
+
return request unless @stub_logger&.enabled?
|
|
187
|
+
@stub_logger.info do |entry|
|
|
188
|
+
entry.set_system_name
|
|
189
|
+
entry.set_service
|
|
190
|
+
entry.set "rpcName", @method_name
|
|
191
|
+
entry.set "retryAttempt", try_number
|
|
192
|
+
entry.set "requestId", @request_id
|
|
193
|
+
entry.message =
|
|
194
|
+
if request.is_a? Enumerable
|
|
195
|
+
"Sending stream to #{entry.service}.#{@method_name} (try #{try_number})"
|
|
196
|
+
else
|
|
197
|
+
"Sending request to #{entry.service}.#{@method_name} (try #{try_number})"
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
loggable_metadata = metadata.to_h rescue {}
|
|
201
|
+
if request.is_a? Enumerable
|
|
202
|
+
request.lazy.map do |req|
|
|
203
|
+
log_single_request req, loggable_metadata
|
|
204
|
+
end
|
|
205
|
+
else
|
|
206
|
+
log_single_request request, loggable_metadata
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def log_single_request request, metadata
|
|
211
|
+
request_content = request.respond_to?(:to_h) ? (request.to_h rescue {}) : request.to_s
|
|
212
|
+
if !request_content.empty? || !metadata.empty?
|
|
213
|
+
@stub_logger.debug do |entry|
|
|
214
|
+
entry.set "requestId", @request_id
|
|
215
|
+
entry.set "request", request_content
|
|
216
|
+
entry.set "headers", metadata
|
|
217
|
+
entry.message = "(request payload as #{request.class})"
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
request
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def log_response response, try_number
|
|
224
|
+
return response unless @stub_logger&.enabled?
|
|
225
|
+
@stub_logger.info do |entry|
|
|
226
|
+
entry.set_system_name
|
|
227
|
+
entry.set_service
|
|
228
|
+
entry.set "rpcName", @method_name
|
|
229
|
+
entry.set "retryAttempt", try_number
|
|
230
|
+
entry.set "requestId", @request_id
|
|
231
|
+
case response
|
|
232
|
+
when StandardError
|
|
233
|
+
entry.set "exception", response.to_s
|
|
234
|
+
entry.message = "Received error for #{entry.service}.#{@method_name} (try #{try_number}): #{response}"
|
|
235
|
+
when Enumerable
|
|
236
|
+
entry.message = "Receiving stream for #{entry.service}.#{@method_name} (try #{try_number})"
|
|
237
|
+
else
|
|
238
|
+
entry.message = "Received response for #{entry.service}.#{@method_name} (try #{try_number})"
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
case response
|
|
242
|
+
when StandardError
|
|
243
|
+
response
|
|
244
|
+
when Enumerable
|
|
245
|
+
response.lazy.map do |resp|
|
|
246
|
+
log_single_response resp
|
|
247
|
+
end
|
|
248
|
+
else
|
|
249
|
+
log_single_response response
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def log_single_response response
|
|
254
|
+
response_content = response.respond_to?(:to_h) ? (response.to_h rescue {}) : response.to_s
|
|
255
|
+
unless response_content.empty?
|
|
256
|
+
@stub_logger.debug do |entry|
|
|
257
|
+
entry.set "requestId", @request_id
|
|
258
|
+
entry.set "response", response_content
|
|
259
|
+
entry.message = "(response payload as #{response.class})"
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
response
|
|
155
263
|
end
|
|
156
264
|
end
|
|
157
265
|
end
|
|
@@ -17,7 +17,8 @@ require "googleauth"
|
|
|
17
17
|
require "gapic/grpc/service_stub/rpc_call"
|
|
18
18
|
require "gapic/grpc/service_stub/channel"
|
|
19
19
|
require "gapic/grpc/service_stub/channel_pool"
|
|
20
|
-
|
|
20
|
+
require "gapic/logging_concerns"
|
|
21
|
+
require "gapic/universe_domain_concerns"
|
|
21
22
|
|
|
22
23
|
module Gapic
|
|
23
24
|
##
|
|
@@ -31,6 +32,9 @@ module Gapic
|
|
|
31
32
|
# @return [Gapic::ServiceStub::ChannelPool] The instance of the ChannelPool class.
|
|
32
33
|
#
|
|
33
34
|
class ServiceStub
|
|
35
|
+
include UniverseDomainConcerns
|
|
36
|
+
include LoggingConcerns
|
|
37
|
+
|
|
34
38
|
attr_reader :grpc_stub
|
|
35
39
|
attr_reader :channel_pool
|
|
36
40
|
|
|
@@ -38,7 +42,12 @@ module Gapic
|
|
|
38
42
|
# Creates a Gapic gRPC stub object.
|
|
39
43
|
#
|
|
40
44
|
# @param grpc_stub_class [Class] gRPC stub class to create a new instance of.
|
|
41
|
-
# @param endpoint [String] The endpoint of the API.
|
|
45
|
+
# @param endpoint [String] The endpoint of the API. Overrides any endpoint_template.
|
|
46
|
+
# @param endpoint_template [String] The endpoint of the API, where the
|
|
47
|
+
# universe domain component of the hostname is marked by the string in
|
|
48
|
+
# the constant {UniverseDomainConcerns::ENDPOINT_SUBSTITUTION}.
|
|
49
|
+
# @param universe_domain [String] The universe domain in which calls should
|
|
50
|
+
# be made. Defaults to `googleapis.com`.
|
|
42
51
|
# @param credentials [Google::Auth::Credentials, Signet::OAuth2::Client, String, Hash, Proc,
|
|
43
52
|
# ::GRPC::Core::Channel, ::GRPC::Core::ChannelCredentials] Provides the means for authenticating requests made by
|
|
44
53
|
# the client. This parameter can be many types:
|
|
@@ -57,25 +66,42 @@ module Gapic
|
|
|
57
66
|
# be used for intercepting calls before they are executed Interceptors are an EXPERIMENTAL API.
|
|
58
67
|
# @param channel_pool_config [::Gapic::ServiceStub:ChannelPool::Configuration] The configuration for channel
|
|
59
68
|
# pool. This argument will raise error when `credentials` is provided as a `::GRPC::Core::Channel`.
|
|
60
|
-
#
|
|
61
|
-
|
|
62
|
-
|
|
69
|
+
# @param logger [Logger,:default,nil] An explicit logger to use, or one
|
|
70
|
+
# of the values `:default` (the default) to construct a default logger,
|
|
71
|
+
# or `nil` to disable logging explicitly.
|
|
72
|
+
#
|
|
73
|
+
def initialize grpc_stub_class,
|
|
74
|
+
credentials:,
|
|
75
|
+
endpoint: nil,
|
|
76
|
+
endpoint_template: nil,
|
|
77
|
+
universe_domain: nil,
|
|
78
|
+
channel_args: nil,
|
|
79
|
+
interceptors: nil,
|
|
80
|
+
channel_pool_config: nil,
|
|
81
|
+
logger: :default
|
|
63
82
|
raise ArgumentError, "grpc_stub_class is required" if grpc_stub_class.nil?
|
|
64
|
-
|
|
65
|
-
|
|
83
|
+
|
|
84
|
+
setup_universe_domain universe_domain: universe_domain,
|
|
85
|
+
endpoint: endpoint,
|
|
86
|
+
endpoint_template: endpoint_template,
|
|
87
|
+
credentials: credentials
|
|
88
|
+
setup_logging logger: logger,
|
|
89
|
+
system_name: grpc_stub_class,
|
|
90
|
+
service: grpc_stub_class,
|
|
91
|
+
endpoint: self.endpoint,
|
|
92
|
+
client_id: object_id
|
|
66
93
|
|
|
67
94
|
@channel_pool = nil
|
|
68
95
|
@grpc_stub = nil
|
|
69
96
|
channel_args = Hash channel_args
|
|
70
97
|
interceptors = Array interceptors
|
|
71
98
|
|
|
72
|
-
|
|
73
99
|
if channel_pool_config && channel_pool_config.channel_count > 1
|
|
74
|
-
create_channel_pool grpc_stub_class, endpoint: endpoint, credentials: credentials,
|
|
100
|
+
create_channel_pool grpc_stub_class, endpoint: self.endpoint, credentials: self.credentials,
|
|
75
101
|
channel_args: channel_args, interceptors: interceptors,
|
|
76
102
|
channel_pool_config: channel_pool_config
|
|
77
103
|
else
|
|
78
|
-
create_grpc_stub grpc_stub_class, endpoint: endpoint, credentials: credentials,
|
|
104
|
+
create_grpc_stub grpc_stub_class, endpoint: self.endpoint, credentials: self.credentials,
|
|
79
105
|
channel_args: channel_args, interceptors: interceptors
|
|
80
106
|
end
|
|
81
107
|
end
|
|
@@ -87,7 +113,7 @@ module Gapic
|
|
|
87
113
|
end
|
|
88
114
|
@channel_pool = ChannelPool.new grpc_stub_class, endpoint: endpoint, credentials: credentials,
|
|
89
115
|
channel_args: channel_args, interceptors: interceptors,
|
|
90
|
-
config: channel_pool_config
|
|
116
|
+
config: channel_pool_config, stub_logger: stub_logger
|
|
91
117
|
end
|
|
92
118
|
|
|
93
119
|
def create_grpc_stub grpc_stub_class, endpoint:, credentials:, channel_args: nil, interceptors: nil
|
|
@@ -186,7 +212,8 @@ module Gapic
|
|
|
186
212
|
#
|
|
187
213
|
def call_rpc method_name, request, options: nil, &block
|
|
188
214
|
if @channel_pool.nil?
|
|
189
|
-
|
|
215
|
+
meth = @grpc_stub.method method_name
|
|
216
|
+
rpc_call = RpcCall.new meth, stub_logger: stub_logger, method_name: method_name
|
|
190
217
|
rpc_call.call request, options: options, &block
|
|
191
218
|
else
|
|
192
219
|
@channel_pool.call_rpc method_name, request, options: options, &block
|
data/lib/gapic/headers.rb
CHANGED
|
@@ -19,6 +19,8 @@ require "gapic/common/version"
|
|
|
19
19
|
module Gapic
|
|
20
20
|
# A collection of common header values.
|
|
21
21
|
module Headers
|
|
22
|
+
# rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
|
23
|
+
|
|
22
24
|
##
|
|
23
25
|
# @param ruby_version [String] The ruby version. Defaults to `RUBY_VERSION`.
|
|
24
26
|
# @param lib_name [String] The client library name.
|
|
@@ -32,10 +34,10 @@ module Gapic
|
|
|
32
34
|
# `:grpc` to send the GRPC library version (if defined)
|
|
33
35
|
# `:rest` to send the REST library version (if defined)
|
|
34
36
|
# Defaults to `[:grpc]`
|
|
37
|
+
#
|
|
35
38
|
def self.x_goog_api_client ruby_version: nil, lib_name: nil, lib_version: nil, gax_version: nil,
|
|
36
39
|
gapic_version: nil, grpc_version: nil, rest_version: nil, protobuf_version: nil,
|
|
37
40
|
transports_version_send: [:grpc]
|
|
38
|
-
|
|
39
41
|
ruby_version ||= ::RUBY_VERSION
|
|
40
42
|
gax_version ||= ::Gapic::Common::VERSION
|
|
41
43
|
grpc_version ||= ::GRPC::VERSION if defined? ::GRPC::VERSION
|
|
@@ -52,4 +54,6 @@ module Gapic
|
|
|
52
54
|
x_goog_api_client_header.join " ".freeze
|
|
53
55
|
end
|
|
54
56
|
end
|
|
57
|
+
|
|
58
|
+
# rubocop:enable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
|
55
59
|
end
|